clk: eswin: Add eic7700 clock driver

Add clock drivers for the EIC7700 SoC. The clock controller on the ESWIN
EIC7700 provides various clocks to different IP blocks within the SoC.

Signed-off-by: Yifeng Huang <huangyifeng@eswincomputing.com>
Tested-by: Marcel Ziswiler <marcel@ziswiler.com> # ebc77
Reviewed-by: Brian Masney <bmasney@redhat.com>
Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
Tested-by: Bo Gan <ganboing@gmail.com> # hfp550
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
Xuyang Dong 2026-03-03 16:07:12 +08:00 committed by Stephen Boyd
parent 8add6d87dc
commit cd44f127c1
No known key found for this signature in database
GPG Key ID: AD028897C6E49525
7 changed files with 2327 additions and 0 deletions

View File

@ -504,6 +504,7 @@ source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/aspeed/Kconfig"
source "drivers/clk/baikal-t1/Kconfig"
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/eswin/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/imgtec/Kconfig"
source "drivers/clk/imx/Kconfig"

View File

@ -120,6 +120,7 @@ obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-y += imgtec/
obj-y += imx/

15
drivers/clk/eswin/Kconfig Normal file
View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
config COMMON_CLK_ESWIN
bool
config COMMON_CLK_EIC7700
tristate "EIC7700 Clock Driver"
depends on ARCH_ESWIN || COMPILE_TEST
select COMMON_CLK_ESWIN
default ARCH_ESWIN
help
This driver provides support for clock controller on ESWIN EIC7700
SoC. The clock controller generates and supplies clocks to various
peripherals within the SoC.
Say yes here to support the clock controller on the EIC7700 SoC.

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
#
# Eswin Clock specific Makefile
#
obj-$(CONFIG_COMMON_CLK_ESWIN) += clk.o
obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o

File diff suppressed because it is too large Load Diff

586
drivers/clk/eswin/clk.c Normal file
View File

@ -0,0 +1,586 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
* All rights reserved.
*
* Authors:
* Yifeng Huang <huangyifeng@eswincomputing.com>
* Xuyang Dong <dongxuyang@eswincomputing.com>
*/
#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include <linux/math.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "common.h"
#define PLL_EN_MASK GENMASK(1, 0)
#define PLL_REFDIV_MASK GENMASK(17, 12)
#define PLL_FBDIV_MASK GENMASK(31, 20)
#define PLL_FRAC_MASK GENMASK(27, 4)
#define PLL_POSTDIV1_MASK GENMASK(10, 8)
#define PLL_POSTDIV2_MASK GENMASK(18, 16)
struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev,
size_t nr_clks)
{
struct eswin_clock_data *eclk_data;
eclk_data = devm_kzalloc(&pdev->dev,
struct_size(eclk_data, clk_data.hws, nr_clks),
GFP_KERNEL);
if (!eclk_data)
return ERR_PTR(-ENOMEM);
eclk_data->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(eclk_data->base))
return ERR_PTR(-EINVAL);
eclk_data->clk_data.num = nr_clks;
spin_lock_init(&eclk_data->lock);
return eclk_data;
}
EXPORT_SYMBOL_GPL(eswin_clk_init);
/**
* eswin_calc_pll - calculate PLL values
* @frac_val: fractional divider
* @fbdiv_val: feedback divider
* @rate: reference rate
* @parent_rate: parent rate
*
* Calculate PLL values for frac and fbdiv:
* fbdiv = rate * 4 / parent_rate
* frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate
*/
static void eswin_calc_pll(u32 *frac_val, u32 *fbdiv_val, unsigned long rate,
unsigned long parent_rate)
{
u32 rem;
u64 tmp;
/* step 1: rate * 4 */
tmp = rate * 4;
/* step 2: use do_div() to get the quotient(tmp) and remainder(rem) */
rem = do_div(tmp, (u32)parent_rate);
/* fbdiv = rate * 4 / parent_rate */
*fbdiv_val = (u32)tmp;
/*
* step 3: rem << 24
* 24: 24-bit fractional accuracy
*/
tmp = (u64)rem << 24;
/* step 4: use do_div() to get the quotient(tmp) */
do_div(tmp, (u32)parent_rate);
/* frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate */
*frac_val = (u32)tmp;
}
static inline struct eswin_clk_pll *to_pll_clk(struct clk_hw *hw)
{
return container_of(hw, struct eswin_clk_pll, hw);
}
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct eswin_clk_pll *clk = to_pll_clk(hw);
u32 frac_val, fbdiv_val, val, mask;
int ret;
eswin_calc_pll(&frac_val, &fbdiv_val, rate, parent_rate);
/* First, disable pll */
val = readl_relaxed(clk->ctrl_reg0);
val &= ~PLL_EN_MASK;
val |= FIELD_PREP(PLL_EN_MASK, 0);
writel_relaxed(val, clk->ctrl_reg0);
val = readl_relaxed(clk->ctrl_reg0);
val &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK);
val |= FIELD_PREP(PLL_FBDIV_MASK, fbdiv_val);
val |= FIELD_PREP(PLL_REFDIV_MASK, 1);
writel_relaxed(val, clk->ctrl_reg0);
val = readl_relaxed(clk->ctrl_reg1);
val &= ~PLL_FRAC_MASK;
val |= FIELD_PREP(PLL_FRAC_MASK, frac_val);
writel_relaxed(val, clk->ctrl_reg1);
val = readl_relaxed(clk->ctrl_reg2);
val &= ~(PLL_POSTDIV1_MASK | PLL_POSTDIV2_MASK);
val |= FIELD_PREP(PLL_POSTDIV1_MASK, 1);
val |= FIELD_PREP(PLL_POSTDIV2_MASK, 1);
writel_relaxed(val, clk->ctrl_reg2);
/* Last, enable pll */
val = readl_relaxed(clk->ctrl_reg0);
val &= ~PLL_EN_MASK;
val |= FIELD_PREP(PLL_EN_MASK, 1);
writel_relaxed(val, clk->ctrl_reg0);
/* Usually the pll will lock in 50us */
mask = GENMASK(clk->lock_shift + clk->lock_width - 1, clk->lock_shift);
ret = readl_poll_timeout(clk->status_reg, val, val & mask, 1, 50 * 2);
if (ret)
pr_err("failed to lock the pll!\n");
return ret;
}
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct eswin_clk_pll *clk = to_pll_clk(hw);
u64 fbdiv_val, frac_val, tmp;
u32 rem, val;
val = readl_relaxed(clk->ctrl_reg0);
val &= PLL_FBDIV_MASK;
fbdiv_val = (val >> clk->fbdiv_shift);
val = readl_relaxed(clk->ctrl_reg1);
val &= PLL_FRAC_MASK;
frac_val = (val >> clk->frac_shift);
/* rate = 24000000 * (fbdiv + frac / (2 ^ 24)) / 4 */
tmp = parent_rate * frac_val;
rem = do_div(tmp, BIT(24));
if (rem)
tmp = parent_rate * fbdiv_val + tmp + 1;
else
tmp = parent_rate * fbdiv_val + tmp;
do_div(tmp, 4);
return tmp;
}
static int clk_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct eswin_clk_pll *clk = to_pll_clk(hw);
req->rate = clamp(req->rate, clk->min_rate, clk->max_rate);
req->min_rate = clk->min_rate;
req->max_rate = clk->max_rate;
return 0;
}
int eswin_clk_register_fixed_rate(struct device *dev,
struct eswin_fixed_rate_clock *clks,
int nums, struct eswin_clock_data *data)
{
struct clk_hw *clk_hw;
int i;
for (i = 0; i < nums; i++) {
clk_hw = devm_clk_hw_register_fixed_rate(dev, clks[i].name,
NULL, clks[i].flags,
clks[i].rate);
if (IS_ERR(clk_hw))
return PTR_ERR(clk_hw);
clks[i].hw = *clk_hw;
data->clk_data.hws[clks[i].id] = clk_hw;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_rate);
static const struct clk_ops eswin_clk_pll_ops = {
.set_rate = clk_pll_set_rate,
.recalc_rate = clk_pll_recalc_rate,
.determine_rate = clk_pll_determine_rate,
};
int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks,
int nums, struct eswin_clock_data *data)
{
struct eswin_clk_pll *p_clk = NULL;
struct clk_init_data init;
struct clk_hw *clk_hw;
int i, ret;
p_clk = devm_kzalloc(dev, sizeof(*p_clk) * nums, GFP_KERNEL);
if (!p_clk)
return -ENOMEM;
for (i = 0; i < nums; i++) {
p_clk->id = clks[i].id;
p_clk->ctrl_reg0 = data->base + clks[i].ctrl_reg0;
p_clk->fbdiv_shift = clks[i].fbdiv_shift;
p_clk->ctrl_reg1 = data->base + clks[i].ctrl_reg1;
p_clk->frac_shift = clks[i].frac_shift;
p_clk->ctrl_reg2 = data->base + clks[i].ctrl_reg2;
p_clk->status_reg = data->base + clks[i].status_reg;
p_clk->lock_shift = clks[i].lock_shift;
p_clk->lock_width = clks[i].lock_width;
p_clk->max_rate = clks[i].max_rate;
p_clk->min_rate = clks[i].min_rate;
init.name = clks[i].name;
init.flags = 0;
init.parent_data = clks[i].parent_data;
init.num_parents = 1;
init.ops = &eswin_clk_pll_ops;
p_clk->hw.init = &init;
clk_hw = &p_clk->hw;
ret = devm_clk_hw_register(dev, clk_hw);
if (ret)
return ret;
clks[i].hw = *clk_hw;
data->clk_data.hws[clks[i].id] = clk_hw;
p_clk++;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_pll);
int eswin_clk_register_fixed_factor(struct device *dev,
struct eswin_fixed_factor_clock *clks,
int nums, struct eswin_clock_data *data)
{
struct clk_hw *clk_hw;
int i;
for (i = 0; i < nums; i++) {
clk_hw = devm_clk_hw_register_fixed_factor_index(dev, clks[i].name,
clks[i].parent_data->index,
clks[i].flags, clks[i].mult,
clks[i].div);
if (IS_ERR(clk_hw))
return PTR_ERR(clk_hw);
clks[i].hw = *clk_hw;
data->clk_data.hws[clks[i].id] = clk_hw;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_factor);
int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks,
int nums, struct eswin_clock_data *data)
{
struct clk_hw *clk_hw;
int i;
for (i = 0; i < nums; i++) {
clk_hw = devm_clk_hw_register_mux_parent_data_table(dev, clks[i].name,
clks[i].parent_data,
clks[i].num_parents,
clks[i].flags,
data->base + clks[i].reg,
clks[i].shift, clks[i].width,
clks[i].mux_flags,
clks[i].table, &data->lock);
if (IS_ERR(clk_hw))
return PTR_ERR(clk_hw);
clks[i].hw = *clk_hw;
data->clk_data.hws[clks[i].id] = clk_hw;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_mux);
static unsigned int _eswin_get_val(unsigned int div, unsigned long flags,
u8 width)
{
unsigned int maxdiv;
maxdiv = clk_div_mask(width);
div = div > maxdiv ? maxdiv : div;
if (flags & ESWIN_PRIV_DIV_MIN_2)
return (div < 2) ? 2 : div;
return div;
}
static unsigned int eswin_div_get_val(unsigned long rate,
unsigned long parent_rate, u8 width,
unsigned long flags)
{
unsigned int div;
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
return _eswin_get_val(div, flags, width);
}
static inline struct eswin_divider_clock *to_div_clk(struct clk_hw *hw)
{
return container_of(hw, struct eswin_divider_clock, hw);
}
static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct eswin_divider_clock *dclk = to_div_clk(hw);
unsigned long flags;
unsigned int value;
u32 val;
value = eswin_div_get_val(rate, parent_rate, dclk->width,
dclk->priv_flag);
spin_lock_irqsave(dclk->lock, flags);
val = readl_relaxed(dclk->ctrl_reg);
val &= ~(clk_div_mask(dclk->width) << dclk->shift);
val |= (u32)value << dclk->shift;
writel_relaxed(val, dclk->ctrl_reg);
spin_unlock_irqrestore(dclk->lock, flags);
return 0;
}
static unsigned long clk_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct eswin_divider_clock *dclk = to_div_clk(hw);
unsigned int div, val;
val = readl_relaxed(dclk->ctrl_reg) >> dclk->shift;
val &= clk_div_mask(dclk->width);
div = _eswin_get_val(val, dclk->priv_flag, dclk->width);
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
}
static int eswin_clk_bestdiv(unsigned long rate,
unsigned long best_parent_rate, u8 width,
unsigned long flags)
{
unsigned long bestdiv, up_rate, down_rate;
int up, down;
if (!rate)
rate = 1;
/* closest round */
up = DIV_ROUND_UP_ULL((u64)best_parent_rate, rate);
down = best_parent_rate / rate;
up_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, up);
down_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, down);
bestdiv = (rate - up_rate) <= (down_rate - rate) ? up : down;
return bestdiv;
}
static int clk_div_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct eswin_divider_clock *dclk = to_div_clk(hw);
int div;
div = eswin_clk_bestdiv(req->rate, req->best_parent_rate, dclk->width,
dclk->priv_flag);
div = _eswin_get_val(div, dclk->priv_flag, dclk->width);
req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div);
return 0;
}
static const struct clk_ops eswin_clk_div_ops = {
.set_rate = clk_div_set_rate,
.recalc_rate = clk_div_recalc_rate,
.determine_rate = clk_div_determine_rate,
};
struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id,
const char *name,
const struct clk_hw *parent_hw,
unsigned long flags, void __iomem *reg,
u8 shift, u8 width,
unsigned long clk_divider_flags,
unsigned long priv_flag, spinlock_t *lock)
{
struct eswin_divider_clock *dclk;
struct clk_init_data init;
struct clk_hw *clk_hw;
int ret;
dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
if (!dclk)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &eswin_clk_div_ops;
init.flags = flags;
init.parent_hws = &parent_hw;
init.num_parents = 1;
/* struct clk_divider assignments */
dclk->id = id;
dclk->ctrl_reg = reg;
dclk->shift = shift;
dclk->width = width;
dclk->div_flags = clk_divider_flags;
dclk->priv_flag = priv_flag;
dclk->lock = lock;
dclk->hw.init = &init;
/* register the clock */
clk_hw = &dclk->hw;
ret = devm_clk_hw_register(dev, clk_hw);
if (ret) {
dev_err(dev, "failed to register divider clock!\n");
return ERR_PTR(ret);
}
return clk_hw;
}
EXPORT_SYMBOL_GPL(eswin_register_clkdiv);
int eswin_clk_register_divider(struct device *dev,
struct eswin_divider_clock *clks,
int nums, struct eswin_clock_data *data)
{
struct clk_hw *clk_hw;
int i;
for (i = 0; i < nums; i++) {
clk_hw = devm_clk_hw_register_divider_parent_data(dev, clks[i].name,
clks[i].parent_data,
clks[i].flags,
data->base + clks[i].reg,
clks[i].shift, clks[i].width,
clks[i].div_flags, &data->lock);
if (IS_ERR(clk_hw))
return PTR_ERR(clk_hw);
clks[i].hw = *clk_hw;
data->clk_data.hws[clks[i].id] = clk_hw;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_divider);
int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks,
int nums, struct eswin_clock_data *data)
{
struct clk_hw *clk_hw;
int i;
for (i = 0; i < nums; i++) {
clk_hw = devm_clk_hw_register_gate_parent_data(dev, clks[i].name,
clks[i].parent_data,
clks[i].flags,
data->base + clks[i].reg,
clks[i].bit_idx, clks[i].gate_flags,
&data->lock);
if (IS_ERR(clk_hw))
return PTR_ERR(clk_hw);
clks[i].hw = *clk_hw;
data->clk_data.hws[clks[i].id] = clk_hw;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_gate);
int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks,
int nums, struct eswin_clock_data *data)
{
struct eswin_clk_info *info;
const struct clk_hw *phw = NULL;
struct clk_hw *hw;
int i;
for (i = 0; i < nums; i++) {
info = &clks[i];
switch (info->type) {
case CLK_FIXED_FACTOR: {
const struct eswin_fixed_factor_clock *factor;
factor = &info->data.factor;
phw = data->clk_data.hws[info->pid];
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, factor->name, phw,
factor->flags,
factor->mult,
factor->div);
break;
}
case CLK_MUX: {
const struct eswin_mux_clock *mux = &info->data.mux;
hw = devm_clk_hw_register_mux_parent_data_table(dev, mux->name,
mux->parent_data,
mux->num_parents,
mux->flags,
data->base + mux->reg,
mux->shift, mux->width,
mux->mux_flags,
mux->table, &data->lock);
break;
}
case CLK_DIVIDER: {
const struct eswin_divider_clock *div = &info->data.div;
phw = data->clk_data.hws[info->pid];
if (div->priv_flag)
hw = eswin_register_clkdiv(dev, div->id, div->name, phw,
div->flags, data->base + div->reg,
div->shift, div->width, div->div_flags,
div->priv_flag, &data->lock);
else
hw = devm_clk_hw_register_divider_parent_hw(dev, div->name, phw,
div->flags,
data->base + div->reg,
div->shift, div->width,
div->div_flags,
&data->lock);
break;
}
case CLK_GATE: {
const struct eswin_gate_clock *gate = &info->data.gate;
phw = data->clk_data.hws[info->pid];
hw = devm_clk_hw_register_gate_parent_hw(dev, gate->name, phw,
gate->flags,
data->base + gate->reg,
gate->bit_idx, gate->gate_flags,
&data->lock);
break;
}
default:
dev_err(dev, "Unidentifiable clock type!\n");
return -EINVAL;
}
if (IS_ERR(hw))
return PTR_ERR(hw);
info->hw = *hw;
data->clk_data.hws[info->id] = hw;
}
return 0;
}
EXPORT_SYMBOL_GPL(eswin_clk_register_clks);

340
drivers/clk/eswin/common.h Normal file
View File

@ -0,0 +1,340 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
* All rights reserved.
*
* Authors:
* Yifeng Huang <huangyifeng@eswincomputing.com>
* Xuyang Dong <dongxuyang@eswincomputing.com>
*/
#ifndef __ESWIN_COMMON_H__
#define __ESWIN_COMMON_H__
#define APLL_HIGH_FREQ 983040000
#define APLL_LOW_FREQ 225792000
#define PLL_HIGH_FREQ 1800000000
#define PLL_LOW_FREQ 24000000
/*
* ESWIN_PRIV_DIV_MIN_2: If ESWIN_PRIV_DIV_MIN_2 is set, the minimum value of
* the register is 2, i.e. the minimum division ratio is 2.
*/
#define ESWIN_PRIV_DIV_MIN_2 BIT(0)
enum eswin_clk_type {
CLK_FIXED_FACTOR,
CLK_MUX,
CLK_DIVIDER,
CLK_GATE,
};
struct eswin_clock_data {
void __iomem *base;
struct clk_hw *original_clk;
struct notifier_block pll_nb;
spinlock_t lock; /* protect register read-modify-write cycle */
struct clk_hw_onecell_data clk_data;
};
struct eswin_divider_clock {
struct clk_hw hw;
unsigned int id;
const char *name;
const struct clk_parent_data *parent_data;
void __iomem *ctrl_reg; /* register address of the divider clock */
unsigned long flags;
unsigned long reg; /* register offset */
u8 shift;
u8 width;
unsigned long div_flags;
unsigned long priv_flag;
spinlock_t *lock; /* protect register read-modify-write cycle */
};
struct eswin_fixed_rate_clock {
struct clk_hw hw;
unsigned int id;
const char *name;
unsigned long flags;
unsigned long rate;
};
struct eswin_fixed_factor_clock {
struct clk_hw hw;
unsigned int id;
const char *name;
const struct clk_parent_data *parent_data;
unsigned long mult;
unsigned long div;
unsigned long flags;
};
struct eswin_gate_clock {
struct clk_hw hw;
unsigned int id;
const char *name;
const struct clk_parent_data *parent_data;
unsigned long flags;
unsigned long reg;
u8 bit_idx;
u8 gate_flags;
};
struct eswin_mux_clock {
struct clk_hw hw;
unsigned int id;
const char *name;
const struct clk_parent_data *parent_data;
u8 num_parents;
unsigned long flags;
unsigned long reg;
u8 shift;
u8 width;
u8 mux_flags;
u32 *table;
};
struct eswin_pll_clock {
struct clk_hw hw;
u32 id;
const char *name;
const struct clk_parent_data *parent_data;
const u32 ctrl_reg0;
const u8 fbdiv_shift;
const u32 ctrl_reg1;
const u8 frac_shift;
const u32 ctrl_reg2;
const u32 status_reg;
const u8 lock_shift;
const u8 lock_width;
const u64 max_rate;
const u64 min_rate;
};
struct eswin_clk_pll {
struct clk_hw hw;
u32 id;
void __iomem *ctrl_reg0;
u8 fbdiv_shift;
void __iomem *ctrl_reg1;
u8 frac_shift;
void __iomem *ctrl_reg2;
void __iomem *status_reg;
u8 lock_shift;
u8 lock_width;
u64 max_rate;
u64 min_rate;
};
struct eswin_clk_info {
unsigned int type;
unsigned int pid;
unsigned int id;
struct clk_hw hw;
union {
struct eswin_divider_clock div;
struct eswin_fixed_factor_clock factor;
struct eswin_gate_clock gate;
struct eswin_mux_clock mux;
} data;
};
struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev,
size_t nr_clks);
int eswin_clk_register_fixed_rate(struct device *dev,
struct eswin_fixed_rate_clock *clks,
int nums, struct eswin_clock_data *data);
int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks,
int nums, struct eswin_clock_data *data);
int eswin_clk_register_fixed_factor(struct device *dev,
struct eswin_fixed_factor_clock *clks,
int nums, struct eswin_clock_data *data);
int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks,
int nums, struct eswin_clock_data *data);
int eswin_clk_register_divider(struct device *dev,
struct eswin_divider_clock *clks,
int nums, struct eswin_clock_data *data);
int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks,
int nums, struct eswin_clock_data *data);
int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks,
int nums, struct eswin_clock_data *data);
struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id,
const char *name,
const struct clk_hw *parent_hw,
unsigned long flags, void __iomem *reg,
u8 shift, u8 width,
unsigned long clk_divider_flags,
unsigned long priv_flag, spinlock_t *lock);
#define ESWIN_DIV(_id, _name, _pdata, _flags, _reg, _shift, _width, \
_dflags, _pflag) \
{ \
.id = _id, \
.name = _name, \
.parent_data = _pdata, \
.flags = _flags, \
.reg = _reg, \
.shift = _shift, \
.width = _width, \
.div_flags = _dflags, \
.priv_flag = _pflag, \
}
#define ESWIN_DIV_TYPE(_id, _name, _pid, _flags, _reg, _shift, _width, \
_dflags, _pflag) \
{ \
.type = CLK_DIVIDER, \
.pid = _pid, \
.id = _id, \
.data = { \
.div = { \
.name = _name, \
.flags = _flags, \
.reg = _reg, \
.shift = _shift, \
.width = _width, \
.div_flags = _dflags, \
.priv_flag = _pflag, \
}, \
}, \
}
#define ESWIN_FACTOR(_id, _name, _pdata, _mult, _div, _flags) \
{ \
.id = _id, \
.name = _name, \
.parent_data = _pdata, \
.mult = _mult, \
.div = _div, \
.flags = _flags, \
}
#define ESWIN_FACTOR_TYPE(_id, _name, _pid, _mult, _div, _flags) \
{ \
.type = CLK_FIXED_FACTOR, \
.pid = _pid, \
.id = _id, \
.data = { \
.factor = { \
.name = _name, \
.mult = _mult, \
.div = _div, \
.flags = _flags, \
}, \
}, \
}
#define ESWIN_FIXED(_id, _name, _flags, _rate) \
{ \
.id = _id, \
.name = _name, \
.flags = _flags, \
.rate = _rate, \
}
#define ESWIN_GATE(_id, _name, _pdata, _flags, _reg, _idx, _gflags) \
{ \
.id = _id, \
.name = _name, \
.parent_data = _pdata, \
.flags = _flags, \
.reg = _reg, \
.bit_idx = _idx, \
.gate_flags = _gflags, \
}
#define ESWIN_GATE_TYPE(_id, _name, _pid, _flags, _reg, _idx, _gflags) \
{ \
.type = CLK_GATE, \
.pid = _pid, \
.id = _id, \
.data = { \
.gate = { \
.name = _name, \
.flags = _flags, \
.reg = _reg, \
.bit_idx = _idx, \
.gate_flags = _gflags, \
}, \
}, \
}
#define ESWIN_MUX(_id, _name, _pdata, _num_parents, _flags, _reg, \
_shift, _width, _mflags) \
{ \
.id = _id, \
.name = _name, \
.parent_data = _pdata, \
.num_parents = _num_parents, \
.flags = _flags, \
.reg = _reg, \
.shift = _shift, \
.width = _width, \
.mux_flags = _mflags, \
.table = NULL, \
}
#define ESWIN_MUX_TBL(_id, _name, _pdata, _num_parents, _flags, _reg, \
_shift, _width, _mflags, _table) \
{ \
.id = _id, \
.name = _name, \
.parent_data = _pdata, \
.num_parents = _num_parents, \
.flags = _flags, \
.reg = _reg, \
.shift = _shift, \
.width = _width, \
.mux_flags = _mflags, \
.table = _table, \
}
#define ESWIN_MUX_TYPE(_id, _name, _pdata, _num_parents, _flags, _reg, \
_shift, _width, _mflags, _table) \
{ \
.type = CLK_MUX, \
.id = _id, \
.data = { \
.mux = { \
.name = _name, \
.parent_data = _pdata, \
.num_parents = _num_parents, \
.flags = _flags, \
.reg = _reg, \
.shift = _shift, \
.width = _width, \
.mux_flags = _mflags, \
.table = _table, \
}, \
}, \
}
#define ESWIN_PLL(_id, _name, _pdata, _reg0, _fb_shift, _reg1, \
_frac_shift, _reg2, _reg, _lock_shift, _lock_width, \
_max_rate, _min_rate) \
{ \
.id = _id, \
.name = _name, \
.parent_data = _pdata, \
.ctrl_reg0 = _reg0, \
.fbdiv_shift = _fb_shift, \
.ctrl_reg1 = _reg1, \
.frac_shift = _frac_shift, \
.ctrl_reg2 = _reg2, \
.status_reg = _reg, \
.lock_shift = _lock_shift, \
.lock_width = _lock_width, \
.max_rate = _max_rate, \
.min_rate = _min_rate, \
}
#endif /* __ESWIN_COMMON_H__ */