mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-17 22:49:02 +00:00
clk: exynos: Add Samsung clock framework
Heavily based on Linux kernel Samsung clock framework, with some changes to accommodate the differences in U-Boot CCF implementation. It's also quite minimal as compared to the Linux version. Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> Reviewed-by: Chanho Park <chanho61.park@samsung.com> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
This commit is contained in:
parent
0caae9fdc2
commit
ff3e8b8c6c
5 changed files with 546 additions and 2 deletions
|
@ -1,6 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2016 Samsung Electronics
|
||||
# Thomas Abraham <thomas.ab@samsung.com>
|
||||
# Copyright (C) 2023 Linaro Ltd.
|
||||
#
|
||||
# Authors:
|
||||
# Thomas Abraham <thomas.ab@samsung.com>
|
||||
# Sam Protsenko <semen.protsenko@linaro.org>
|
||||
|
||||
obj-$(CONFIG_CLK_EXYNOS7420) += clk-exynos7420.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-pll.o
|
||||
obj-$(CONFIG_CLK_EXYNOS7420) += clk-exynos7420.o
|
||||
|
|
167
drivers/clk/exynos/clk-pll.c
Normal file
167
drivers/clk/exynos/clk-pll.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Samsung Electronics
|
||||
* Copyright (C) 2023 Linaro Ltd.
|
||||
*
|
||||
* Authors:
|
||||
* Thomas Abraham <thomas.ab@samsung.com>
|
||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*
|
||||
* This file contains the utility functions to register the pll clocks.
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <div64.h>
|
||||
#include <malloc.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
#include <clk.h>
|
||||
#include "clk.h"
|
||||
|
||||
#define UBOOT_DM_CLK_SAMSUNG_PLL0822X "samsung_clk_pll0822x"
|
||||
#define UBOOT_DM_CLK_SAMSUNG_PLL0831X "samsung_clk_pll0831x"
|
||||
|
||||
struct samsung_clk_pll {
|
||||
struct clk clk;
|
||||
void __iomem *con_reg;
|
||||
enum samsung_pll_type type;
|
||||
};
|
||||
|
||||
#define to_clk_pll(_clk) container_of(_clk, struct samsung_clk_pll, clk)
|
||||
|
||||
/*
|
||||
* PLL0822x Clock Type
|
||||
*/
|
||||
|
||||
#define PLL0822X_MDIV_MASK 0x3ff
|
||||
#define PLL0822X_PDIV_MASK 0x3f
|
||||
#define PLL0822X_SDIV_MASK 0x7
|
||||
#define PLL0822X_MDIV_SHIFT 16
|
||||
#define PLL0822X_PDIV_SHIFT 8
|
||||
#define PLL0822X_SDIV_SHIFT 0
|
||||
|
||||
static unsigned long samsung_pll0822x_recalc_rate(struct clk *clk)
|
||||
{
|
||||
struct samsung_clk_pll *pll = to_clk_pll(clk);
|
||||
u32 mdiv, pdiv, sdiv, pll_con3;
|
||||
u64 fvco = clk_get_parent_rate(clk);
|
||||
|
||||
pll_con3 = readl_relaxed(pll->con_reg);
|
||||
mdiv = (pll_con3 >> PLL0822X_MDIV_SHIFT) & PLL0822X_MDIV_MASK;
|
||||
pdiv = (pll_con3 >> PLL0822X_PDIV_SHIFT) & PLL0822X_PDIV_MASK;
|
||||
sdiv = (pll_con3 >> PLL0822X_SDIV_SHIFT) & PLL0822X_SDIV_MASK;
|
||||
|
||||
fvco *= mdiv;
|
||||
do_div(fvco, (pdiv << sdiv));
|
||||
return (unsigned long)fvco;
|
||||
}
|
||||
|
||||
static const struct clk_ops samsung_pll0822x_clk_min_ops = {
|
||||
.get_rate = samsung_pll0822x_recalc_rate,
|
||||
};
|
||||
|
||||
/*
|
||||
* PLL0831x Clock Type
|
||||
*/
|
||||
|
||||
#define PLL0831X_KDIV_MASK 0xffff
|
||||
#define PLL0831X_MDIV_MASK 0x1ff
|
||||
#define PLL0831X_PDIV_MASK 0x3f
|
||||
#define PLL0831X_SDIV_MASK 0x7
|
||||
#define PLL0831X_MDIV_SHIFT 16
|
||||
#define PLL0831X_PDIV_SHIFT 8
|
||||
#define PLL0831X_SDIV_SHIFT 0
|
||||
#define PLL0831X_KDIV_SHIFT 0
|
||||
|
||||
static unsigned long samsung_pll0831x_recalc_rate(struct clk *clk)
|
||||
{
|
||||
struct samsung_clk_pll *pll = to_clk_pll(clk);
|
||||
u32 mdiv, pdiv, sdiv, pll_con3, pll_con5;
|
||||
s16 kdiv;
|
||||
u64 fvco = clk_get_parent_rate(clk);
|
||||
|
||||
pll_con3 = readl_relaxed(pll->con_reg);
|
||||
pll_con5 = readl_relaxed(pll->con_reg + 8);
|
||||
mdiv = (pll_con3 >> PLL0831X_MDIV_SHIFT) & PLL0831X_MDIV_MASK;
|
||||
pdiv = (pll_con3 >> PLL0831X_PDIV_SHIFT) & PLL0831X_PDIV_MASK;
|
||||
sdiv = (pll_con3 >> PLL0831X_SDIV_SHIFT) & PLL0831X_SDIV_MASK;
|
||||
kdiv = (s16)((pll_con5 >> PLL0831X_KDIV_SHIFT) & PLL0831X_KDIV_MASK);
|
||||
|
||||
fvco *= (mdiv << 16) + kdiv;
|
||||
do_div(fvco, (pdiv << sdiv));
|
||||
fvco >>= 16;
|
||||
|
||||
return (unsigned long)fvco;
|
||||
}
|
||||
|
||||
static const struct clk_ops samsung_pll0831x_clk_min_ops = {
|
||||
.get_rate = samsung_pll0831x_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk *_samsung_clk_register_pll(void __iomem *base,
|
||||
const struct samsung_pll_clock *pll_clk)
|
||||
{
|
||||
struct samsung_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
const char *drv_name;
|
||||
int ret;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pll->con_reg = base + pll_clk->con_offset;
|
||||
pll->type = pll_clk->type;
|
||||
clk = &pll->clk;
|
||||
clk->flags = pll_clk->flags;
|
||||
|
||||
switch (pll_clk->type) {
|
||||
case pll_0822x:
|
||||
drv_name = UBOOT_DM_CLK_SAMSUNG_PLL0822X;
|
||||
break;
|
||||
case pll_0831x:
|
||||
drv_name = UBOOT_DM_CLK_SAMSUNG_PLL0831X;
|
||||
break;
|
||||
default:
|
||||
kfree(pll);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
ret = clk_register(clk, drv_name, pll_clk->name, pll_clk->parent_name);
|
||||
if (ret) {
|
||||
kfree(pll);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
void samsung_clk_register_pll(void __iomem *base,
|
||||
const struct samsung_pll_clock *clk_list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
unsigned int cnt;
|
||||
|
||||
for (cnt = 0; cnt < nr_clk; cnt++) {
|
||||
struct clk *clk;
|
||||
const struct samsung_pll_clock *pll_clk;
|
||||
|
||||
pll_clk = &clk_list[cnt];
|
||||
clk = _samsung_clk_register_pll(base, pll_clk);
|
||||
clk_dm(pll_clk->id, clk);
|
||||
}
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(samsung_pll0822x_clk) = {
|
||||
.name = UBOOT_DM_CLK_SAMSUNG_PLL0822X,
|
||||
.id = UCLASS_CLK,
|
||||
.ops = &samsung_pll0822x_clk_min_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(samsung_pll0831x_clk) = {
|
||||
.name = UBOOT_DM_CLK_SAMSUNG_PLL0831X,
|
||||
.id = UCLASS_CLK,
|
||||
.ops = &samsung_pll0831x_clk_min_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
23
drivers/clk/exynos/clk-pll.h
Normal file
23
drivers/clk/exynos/clk-pll.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2016 Samsung Electronics
|
||||
* Copyright (C) 2023 Linaro Ltd.
|
||||
*
|
||||
* Authors:
|
||||
* Thomas Abraham <thomas.ab@samsung.com>
|
||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*
|
||||
* Common Clock Framework support for all PLL's in Samsung platforms.
|
||||
*/
|
||||
|
||||
#ifndef __EXYNOS_CLK_PLL_H
|
||||
#define __EXYNOS_CLK_PLL_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
enum samsung_pll_type {
|
||||
pll_0822x,
|
||||
pll_0831x,
|
||||
};
|
||||
|
||||
#endif /* __EXYNOS_CLK_PLL_H */
|
121
drivers/clk/exynos/clk.c
Normal file
121
drivers/clk/exynos/clk.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2023 Linaro Ltd.
|
||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*
|
||||
* This file includes utility functions to register clocks to common
|
||||
* clock framework for Samsung platforms.
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include "clk.h"
|
||||
|
||||
void samsung_clk_register_mux(void __iomem *base,
|
||||
const struct samsung_mux_clock *clk_list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
unsigned int cnt;
|
||||
|
||||
for (cnt = 0; cnt < nr_clk; cnt++) {
|
||||
struct clk *clk;
|
||||
const struct samsung_mux_clock *m;
|
||||
|
||||
m = &clk_list[cnt];
|
||||
clk = clk_register_mux(NULL, m->name, m->parent_names,
|
||||
m->num_parents, m->flags, base + m->offset, m->shift,
|
||||
m->width, m->mux_flags);
|
||||
clk_dm(m->id, clk);
|
||||
}
|
||||
}
|
||||
|
||||
void samsung_clk_register_div(void __iomem *base,
|
||||
const struct samsung_div_clock *clk_list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
unsigned int cnt;
|
||||
|
||||
for (cnt = 0; cnt < nr_clk; cnt++) {
|
||||
struct clk *clk;
|
||||
const struct samsung_div_clock *d;
|
||||
|
||||
d = &clk_list[cnt];
|
||||
clk = clk_register_divider(NULL, d->name, d->parent_name,
|
||||
d->flags, base + d->offset, d->shift,
|
||||
d->width, d->div_flags);
|
||||
clk_dm(d->id, clk);
|
||||
}
|
||||
}
|
||||
|
||||
void samsung_clk_register_gate(void __iomem *base,
|
||||
const struct samsung_gate_clock *clk_list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
unsigned int cnt;
|
||||
|
||||
for (cnt = 0; cnt < nr_clk; cnt++) {
|
||||
struct clk *clk;
|
||||
const struct samsung_gate_clock *g;
|
||||
|
||||
g = &clk_list[cnt];
|
||||
clk = clk_register_gate(NULL, g->name, g->parent_name,
|
||||
g->flags, base + g->offset, g->bit_idx,
|
||||
g->gate_flags, NULL);
|
||||
clk_dm(g->id, clk);
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*samsung_clk_register_fn)(void __iomem *base,
|
||||
const void *clk_list,
|
||||
unsigned int nr_clk);
|
||||
|
||||
static const samsung_clk_register_fn samsung_clk_register_fns[] = {
|
||||
[S_CLK_MUX] = (samsung_clk_register_fn)samsung_clk_register_mux,
|
||||
[S_CLK_DIV] = (samsung_clk_register_fn)samsung_clk_register_div,
|
||||
[S_CLK_GATE] = (samsung_clk_register_fn)samsung_clk_register_gate,
|
||||
[S_CLK_PLL] = (samsung_clk_register_fn)samsung_clk_register_pll,
|
||||
};
|
||||
|
||||
/**
|
||||
* samsung_cmu_register_clocks() - Register provided clock groups
|
||||
* @base: Base address of CMU registers
|
||||
* @clk_groups: list of clock groups
|
||||
* @nr_groups: count of clock groups in @clk_groups
|
||||
*
|
||||
* Having the array of clock groups @clk_groups makes it possible to keep a
|
||||
* correct clocks registration order.
|
||||
*/
|
||||
void samsung_cmu_register_clocks(void __iomem *base,
|
||||
const struct samsung_clk_group *clk_groups,
|
||||
unsigned int nr_groups)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < nr_groups; i++) {
|
||||
const struct samsung_clk_group *g = &clk_groups[i];
|
||||
|
||||
samsung_clk_register_fns[g->type](base, g->clk_list, g->nr_clk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* samsung_cmu_register_one - Register all CMU clocks
|
||||
* @dev: CMU device
|
||||
* @clk_groups: list of CMU clock groups
|
||||
* @nr_groups: count of CMU clock groups in @clk_groups
|
||||
*
|
||||
* Return: 0 on success or negative value on error.
|
||||
*/
|
||||
int samsung_cmu_register_one(struct udevice *dev,
|
||||
const struct samsung_clk_group *clk_groups,
|
||||
unsigned int nr_groups)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
||||
base = dev_read_addr_ptr(dev);
|
||||
if (!base)
|
||||
return -EINVAL;
|
||||
|
||||
samsung_cmu_register_clocks(base, clk_groups, nr_groups);
|
||||
|
||||
return 0;
|
||||
}
|
228
drivers/clk/exynos/clk.h
Normal file
228
drivers/clk/exynos/clk.h
Normal file
|
@ -0,0 +1,228 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Linaro Ltd.
|
||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*
|
||||
* Common Clock Framework support for all Samsung platforms.
|
||||
*/
|
||||
|
||||
#ifndef __EXYNOS_CLK_H
|
||||
#define __EXYNOS_CLK_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include "clk-pll.h"
|
||||
|
||||
/**
|
||||
* struct samsung_mux_clock - information about mux clock
|
||||
* @id: platform specific id of the clock
|
||||
* @name: name of this mux clock
|
||||
* @parent_names: array of pointer to parent clock names
|
||||
* @num_parents: number of parents listed in @parent_names
|
||||
* @flags: optional flags for basic clock
|
||||
* @offset: offset of the register for configuring the mux
|
||||
* @shift: starting bit location of the mux control bit-field in @reg
|
||||
* @width: width of the mux control bit-field in @reg
|
||||
* @mux_flags: flags for mux-type clock
|
||||
*/
|
||||
struct samsung_mux_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char * const *parent_names;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 mux_flags;
|
||||
};
|
||||
|
||||
#define PNAME(x) static const char * const x[]
|
||||
|
||||
#define __MUX(_id, cname, pnames, o, s, w, f, mf) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = cname, \
|
||||
.parent_names = pnames, \
|
||||
.num_parents = ARRAY_SIZE(pnames), \
|
||||
.flags = (f) | CLK_SET_RATE_NO_REPARENT, \
|
||||
.offset = o, \
|
||||
.shift = s, \
|
||||
.width = w, \
|
||||
.mux_flags = mf, \
|
||||
}
|
||||
|
||||
#define MUX(_id, cname, pnames, o, s, w) \
|
||||
__MUX(_id, cname, pnames, o, s, w, 0, 0)
|
||||
|
||||
#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \
|
||||
__MUX(_id, cname, pnames, o, s, w, f, mf)
|
||||
|
||||
/**
|
||||
* struct samsung_div_clock - information about div clock
|
||||
* @id: platform specific id of the clock
|
||||
* @name: name of this div clock
|
||||
* @parent_name: name of the parent clock
|
||||
* @flags: optional flags for basic clock
|
||||
* @offset: offset of the register for configuring the div
|
||||
* @shift: starting bit location of the div control bit-field in @reg
|
||||
* @width: width of the bitfield
|
||||
* @div_flags: flags for div-type clock
|
||||
*/
|
||||
struct samsung_div_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 div_flags;
|
||||
};
|
||||
|
||||
#define __DIV(_id, cname, pname, o, s, w, f, df) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = cname, \
|
||||
.parent_name = pname, \
|
||||
.flags = f, \
|
||||
.offset = o, \
|
||||
.shift = s, \
|
||||
.width = w, \
|
||||
.div_flags = df, \
|
||||
}
|
||||
|
||||
#define DIV(_id, cname, pname, o, s, w) \
|
||||
__DIV(_id, cname, pname, o, s, w, 0, 0)
|
||||
|
||||
#define DIV_F(_id, cname, pname, o, s, w, f, df) \
|
||||
__DIV(_id, cname, pname, o, s, w, f, df)
|
||||
|
||||
/**
|
||||
* struct samsung_gate_clock - information about gate clock
|
||||
* @id: platform specific id of the clock
|
||||
* @name: name of this gate clock
|
||||
* @parent_name: name of the parent clock
|
||||
* @flags: optional flags for basic clock
|
||||
* @offset: offset of the register for configuring the gate
|
||||
* @bit_idx: bit index of the gate control bit-field in @reg
|
||||
* @gate_flags: flags for gate-type clock
|
||||
*/
|
||||
struct samsung_gate_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 bit_idx;
|
||||
u8 gate_flags;
|
||||
};
|
||||
|
||||
#define __GATE(_id, cname, pname, o, b, f, gf) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = cname, \
|
||||
.parent_name = pname, \
|
||||
.flags = f, \
|
||||
.offset = o, \
|
||||
.bit_idx = b, \
|
||||
.gate_flags = gf, \
|
||||
}
|
||||
|
||||
#define GATE(_id, cname, pname, o, b, f, gf) \
|
||||
__GATE(_id, cname, pname, o, b, f, gf)
|
||||
|
||||
/**
|
||||
* struct samsung_pll_clock - information about pll clock
|
||||
* @id: platform specific id of the clock
|
||||
* @name: name of this pll clock
|
||||
* @parent_name: name of the parent clock
|
||||
* @flags: optional flags for basic clock
|
||||
* @con_offset: offset of the register for configuring the PLL
|
||||
* @type: type of PLL to be registered
|
||||
*/
|
||||
struct samsung_pll_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
int con_offset;
|
||||
enum samsung_pll_type type;
|
||||
};
|
||||
|
||||
#define PLL(_typ, _id, _name, _pname, _con) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.flags = CLK_GET_RATE_NOCACHE, \
|
||||
.con_offset = _con, \
|
||||
.type = _typ, \
|
||||
}
|
||||
|
||||
enum samsung_clock_type {
|
||||
S_CLK_MUX,
|
||||
S_CLK_DIV,
|
||||
S_CLK_GATE,
|
||||
S_CLK_PLL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct samsung_clock_group - contains a list of clocks of one type
|
||||
* @type: type of clocks this structure contains
|
||||
* @clk_list: list of clocks
|
||||
* @nr_clk: count of clocks in @clk_list
|
||||
*/
|
||||
struct samsung_clk_group {
|
||||
enum samsung_clock_type type;
|
||||
const void *clk_list;
|
||||
unsigned int nr_clk;
|
||||
};
|
||||
|
||||
void samsung_clk_register_mux(void __iomem *base,
|
||||
const struct samsung_mux_clock *clk_list,
|
||||
unsigned int nr_clk);
|
||||
void samsung_clk_register_div(void __iomem *base,
|
||||
const struct samsung_div_clock *clk_list,
|
||||
unsigned int nr_clk);
|
||||
void samsung_clk_register_gate(void __iomem *base,
|
||||
const struct samsung_gate_clock *clk_list,
|
||||
unsigned int nr_clk);
|
||||
void samsung_clk_register_pll(void __iomem *base,
|
||||
const struct samsung_pll_clock *clk_list,
|
||||
unsigned int nr_clk);
|
||||
|
||||
void samsung_cmu_register_clocks(void __iomem *base,
|
||||
const struct samsung_clk_group *clk_groups,
|
||||
unsigned int nr_groups);
|
||||
int samsung_cmu_register_one(struct udevice *dev,
|
||||
const struct samsung_clk_group *clk_groups,
|
||||
unsigned int nr_groups);
|
||||
|
||||
/**
|
||||
* samsung_register_cmu - Register CMU clocks ensuring parent CMU is present
|
||||
* @dev: CMU device
|
||||
* @clk_groups: list of CMU clock groups
|
||||
* @parent_drv: name of parent CMU driver
|
||||
*
|
||||
* Register provided CMU clocks, but make sure CMU_TOP driver is instantiated
|
||||
* first.
|
||||
*
|
||||
* Return: 0 on success or negative value on error.
|
||||
*/
|
||||
#define samsung_register_cmu(dev, clk_groups, parent_drv) \
|
||||
({ \
|
||||
struct udevice *__parent; \
|
||||
int __ret; \
|
||||
\
|
||||
__ret = uclass_get_device_by_driver(UCLASS_CLK, \
|
||||
DM_DRIVER_GET(parent_drv), &__parent); \
|
||||
if (__ret || !__parent) \
|
||||
__ret = -ENOENT; \
|
||||
else \
|
||||
__ret = samsung_cmu_register_one(dev, clk_groups, \
|
||||
ARRAY_SIZE(clk_groups)); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#endif /* __EXYNOS_CLK_H */
|
Loading…
Add table
Reference in a new issue