mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-06 05:06:29 +00:00
clk: clk-loongson2: Refactor driver for adding new platforms
The driver only supported loongson-2K1000 at first, but the clock structure of loongson-2K0500 and loongson-2K2000 are actually similar, and I tried to refactor the whole driver to adjust to the addition of the new platform. Briefly, I have divided all clocks into three categories according to their properties and their parent clocks: Independent PLLs, clocks based on frequency scales, and clock dividers. Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn> Link: https://lore.kernel.org/r/fb020d1ca19e6f4cdcc95c87b2748869ca76b8ec.1712731524.git.zhoubinbin@loongson.cn Acked-by: Huacai Chen <chenhuacai@loongson.cn> [sboyd@kernel.org: Drop clk.h include] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
parent
0b1bfd15f3
commit
9796ec0bd0
@ -13,317 +13,254 @@
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <dt-bindings/clock/loongson,ls2k-clk.h>
|
||||
|
||||
#define LOONGSON2_PLL_MULT_SHIFT 32
|
||||
#define LOONGSON2_PLL_MULT_WIDTH 10
|
||||
#define LOONGSON2_PLL_DIV_SHIFT 26
|
||||
#define LOONGSON2_PLL_DIV_WIDTH 6
|
||||
#define LOONGSON2_APB_FREQSCALE_SHIFT 20
|
||||
#define LOONGSON2_APB_FREQSCALE_WIDTH 3
|
||||
#define LOONGSON2_USB_FREQSCALE_SHIFT 16
|
||||
#define LOONGSON2_USB_FREQSCALE_WIDTH 3
|
||||
#define LOONGSON2_SATA_FREQSCALE_SHIFT 12
|
||||
#define LOONGSON2_SATA_FREQSCALE_WIDTH 3
|
||||
#define LOONGSON2_BOOT_FREQSCALE_SHIFT 8
|
||||
#define LOONGSON2_BOOT_FREQSCALE_WIDTH 3
|
||||
|
||||
static void __iomem *loongson2_pll_base;
|
||||
|
||||
static const struct clk_parent_data pdata[] = {
|
||||
{ .fw_name = "ref_100m",},
|
||||
{ .fw_name = "ref_100m", },
|
||||
};
|
||||
|
||||
static struct clk_hw *loongson2_clk_register(struct device *dev,
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
const struct clk_ops *ops,
|
||||
unsigned long flags)
|
||||
{
|
||||
int ret;
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init = { };
|
||||
|
||||
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
|
||||
if (!hw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = ops;
|
||||
init.flags = flags;
|
||||
init.num_parents = 1;
|
||||
|
||||
if (!parent_name)
|
||||
init.parent_data = pdata;
|
||||
else
|
||||
init.parent_names = &parent_name;
|
||||
|
||||
hw->init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(dev, hw);
|
||||
if (ret)
|
||||
hw = ERR_PTR(ret);
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
static unsigned long loongson2_calc_pll_rate(int offset, unsigned long rate)
|
||||
{
|
||||
u64 val;
|
||||
u32 mult, div;
|
||||
|
||||
val = readq(loongson2_pll_base + offset);
|
||||
|
||||
mult = (val >> LOONGSON2_PLL_MULT_SHIFT) &
|
||||
clk_div_mask(LOONGSON2_PLL_MULT_WIDTH);
|
||||
div = (val >> LOONGSON2_PLL_DIV_SHIFT) &
|
||||
clk_div_mask(LOONGSON2_PLL_DIV_WIDTH);
|
||||
|
||||
return div_u64((u64)rate * mult, div);
|
||||
}
|
||||
|
||||
static unsigned long loongson2_node_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x0, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_node_clk_ops = {
|
||||
.recalc_rate = loongson2_node_recalc_rate,
|
||||
enum loongson2_clk_type {
|
||||
CLK_TYPE_PLL,
|
||||
CLK_TYPE_SCALE,
|
||||
CLK_TYPE_DIVIDER,
|
||||
CLK_TYPE_NONE,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_ddr_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x10, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_ddr_clk_ops = {
|
||||
.recalc_rate = loongson2_ddr_recalc_rate,
|
||||
struct loongson2_clk_provider {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk_hw_onecell_data clk_data;
|
||||
spinlock_t clk_lock; /* protect access to DIV registers */
|
||||
};
|
||||
|
||||
static unsigned long loongson2_dc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x20, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_dc_clk_ops = {
|
||||
.recalc_rate = loongson2_dc_recalc_rate,
|
||||
struct loongson2_clk_data {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
u8 div_shift;
|
||||
u8 div_width;
|
||||
u8 mult_shift;
|
||||
u8 mult_width;
|
||||
};
|
||||
|
||||
static unsigned long loongson2_pix0_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x30, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_pix0_clk_ops = {
|
||||
.recalc_rate = loongson2_pix0_recalc_rate,
|
||||
struct loongson2_clk_board_info {
|
||||
u8 id;
|
||||
enum loongson2_clk_type type;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
u8 reg_offset;
|
||||
u8 div_shift;
|
||||
u8 div_width;
|
||||
u8 mult_shift;
|
||||
u8 mult_width;
|
||||
};
|
||||
|
||||
static unsigned long loongson2_pix1_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x40, parent_rate);
|
||||
}
|
||||
#define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_DIVIDER, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.reg_offset = _offset, \
|
||||
.div_shift = _dshift, \
|
||||
.div_width = _dwidth, \
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_pix1_clk_ops = {
|
||||
.recalc_rate = loongson2_pix1_recalc_rate,
|
||||
};
|
||||
#define CLK_PLL(_id, _name, _offset, _mshift, _mwidth, \
|
||||
_dshift, _dwidth) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_PLL, \
|
||||
.name = _name, \
|
||||
.parent_name = NULL, \
|
||||
.reg_offset = _offset, \
|
||||
.mult_shift = _mshift, \
|
||||
.mult_width = _mwidth, \
|
||||
.div_shift = _dshift, \
|
||||
.div_width = _dwidth, \
|
||||
}
|
||||
|
||||
static unsigned long loongson2_calc_rate(unsigned long rate,
|
||||
int shift, int width)
|
||||
{
|
||||
u64 val;
|
||||
u32 mult;
|
||||
|
||||
val = readq(loongson2_pll_base + 0x50);
|
||||
|
||||
mult = (val >> shift) & clk_div_mask(width);
|
||||
|
||||
return div_u64((u64)rate * (mult + 1), 8);
|
||||
}
|
||||
|
||||
static unsigned long loongson2_boot_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_BOOT_FREQSCALE_SHIFT,
|
||||
LOONGSON2_BOOT_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_boot_clk_ops = {
|
||||
.recalc_rate = loongson2_boot_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_APB_FREQSCALE_SHIFT,
|
||||
LOONGSON2_APB_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_apb_clk_ops = {
|
||||
.recalc_rate = loongson2_apb_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_usb_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_USB_FREQSCALE_SHIFT,
|
||||
LOONGSON2_USB_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_usb_clk_ops = {
|
||||
.recalc_rate = loongson2_usb_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_sata_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_SATA_FREQSCALE_SHIFT,
|
||||
LOONGSON2_SATA_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_sata_clk_ops = {
|
||||
.recalc_rate = loongson2_sata_recalc_rate,
|
||||
};
|
||||
|
||||
static inline int loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (IS_ERR(clks[i])) {
|
||||
pr_err("Loongson2 clk %u: register failed with %ld\n",
|
||||
i, PTR_ERR(clks[i]));
|
||||
return PTR_ERR(clks[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loongson2_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct clk_hw **hws;
|
||||
struct clk_hw_onecell_data *clk_hw_data;
|
||||
spinlock_t loongson2_clk_lock;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(loongson2_pll_base))
|
||||
return PTR_ERR(loongson2_pll_base);
|
||||
|
||||
clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
|
||||
GFP_KERNEL);
|
||||
if (WARN_ON(!clk_hw_data))
|
||||
return -ENOMEM;
|
||||
|
||||
clk_hw_data->num = LOONGSON2_CLK_END;
|
||||
hws = clk_hw_data->hws;
|
||||
|
||||
hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
|
||||
NULL,
|
||||
&loongson2_node_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
|
||||
NULL,
|
||||
&loongson2_ddr_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
|
||||
NULL,
|
||||
&loongson2_dc_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
|
||||
NULL,
|
||||
&loongson2_pix0_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
|
||||
NULL,
|
||||
&loongson2_pix1_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
|
||||
NULL,
|
||||
&loongson2_boot_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_NODE_CLK] = devm_clk_hw_register_divider(dev, "node",
|
||||
"node_pll", 0,
|
||||
loongson2_pll_base + 0x8, 0,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
#define CLK_SCALE(_id, _name, _pname, _offset, \
|
||||
_dshift, _dwidth) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_SCALE, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.reg_offset = _offset, \
|
||||
.div_shift = _dshift, \
|
||||
.div_width = _dwidth, \
|
||||
}
|
||||
|
||||
static const struct loongson2_clk_board_info ls2k1000_clks[] = {
|
||||
CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x10, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_DC_PLL, "pll_dc", 0x20, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 32, 10, 26, 6),
|
||||
CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0x8, 0, 6),
|
||||
CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x18, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_ddr", 0x18, 22, 6),
|
||||
/*
|
||||
* The hda clk divisor in the upper 32bits and the clk-prodiver
|
||||
* layer code doesn't support 64bit io operation thus a conversion
|
||||
* is required that subtract shift by 32 and add 4byte to the hda
|
||||
* address
|
||||
*/
|
||||
hws[LOONGSON2_HDA_CLK] = devm_clk_hw_register_divider(dev, "hda",
|
||||
"ddr_pll", 0,
|
||||
loongson2_pll_base + 0x22, 12,
|
||||
7, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0x22, 12, 7),
|
||||
CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "pll_dc", 0x28, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_dc", 0x28, 22, 6),
|
||||
CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x38, 0, 6),
|
||||
CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x38, 0, 6),
|
||||
CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 8, 3),
|
||||
CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
|
||||
CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_gmac", 0x50, 16, 3),
|
||||
CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_gmac", 0x50, 20, 3),
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
hws[LOONGSON2_GPU_CLK] = devm_clk_hw_register_divider(dev, "gpu",
|
||||
"ddr_pll", 0,
|
||||
loongson2_pll_base + 0x18, 22,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct loongson2_clk_data, hw);
|
||||
}
|
||||
|
||||
hws[LOONGSON2_DDR_CLK] = devm_clk_hw_register_divider(dev, "ddr",
|
||||
"ddr_pll", 0,
|
||||
loongson2_pll_base + 0x18, 0,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
|
||||
{
|
||||
return (val & GENMASK(shift + width - 1, shift)) >> shift;
|
||||
}
|
||||
|
||||
hws[LOONGSON2_GMAC_CLK] = devm_clk_hw_register_divider(dev, "gmac",
|
||||
"dc_pll", 0,
|
||||
loongson2_pll_base + 0x28, 22,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u64 val, mult, div;
|
||||
struct loongson2_clk_data *clk = to_loongson2_clk(hw);
|
||||
|
||||
hws[LOONGSON2_DC_CLK] = devm_clk_hw_register_divider(dev, "dc",
|
||||
"dc_pll", 0,
|
||||
loongson2_pll_base + 0x28, 0,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
val = readq(clk->reg);
|
||||
mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
|
||||
div = loongson2_rate_part(val, clk->div_shift, clk->div_width);
|
||||
|
||||
hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
|
||||
"gmac",
|
||||
&loongson2_apb_clk_ops, 0);
|
||||
return div_u64((u64)parent_rate * mult, div);
|
||||
}
|
||||
|
||||
hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
|
||||
"gmac",
|
||||
&loongson2_usb_clk_ops, 0);
|
||||
static const struct clk_ops loongson2_pll_recalc_ops = {
|
||||
.recalc_rate = loongson2_pll_recalc_rate,
|
||||
};
|
||||
|
||||
hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
|
||||
"gmac",
|
||||
&loongson2_sata_clk_ops, 0);
|
||||
static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u64 val, mult;
|
||||
struct loongson2_clk_data *clk = to_loongson2_clk(hw);
|
||||
|
||||
hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
|
||||
"pix0_pll", 0,
|
||||
loongson2_pll_base + 0x38, 0, 6,
|
||||
CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
val = readq(clk->reg);
|
||||
mult = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
|
||||
|
||||
hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
|
||||
"pix1_pll", 0,
|
||||
loongson2_pll_base + 0x48, 0, 6,
|
||||
CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
return div_u64((u64)parent_rate * mult, 8);
|
||||
}
|
||||
|
||||
ret = loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
|
||||
static const struct clk_ops loongson2_freqscale_recalc_ops = {
|
||||
.recalc_rate = loongson2_freqscale_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk_hw *loongson2_clk_register(struct loongson2_clk_provider *clp,
|
||||
const struct loongson2_clk_board_info *cld,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
int ret;
|
||||
struct clk_hw *hw;
|
||||
struct loongson2_clk_data *clk;
|
||||
struct clk_init_data init = { };
|
||||
|
||||
clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
|
||||
if (!clk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = cld->name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
init.num_parents = 1;
|
||||
|
||||
if (!cld->parent_name)
|
||||
init.parent_data = pdata;
|
||||
else
|
||||
init.parent_names = &cld->parent_name;
|
||||
|
||||
clk->reg = clp->base + cld->reg_offset;
|
||||
clk->div_shift = cld->div_shift;
|
||||
clk->div_width = cld->div_width;
|
||||
clk->mult_shift = cld->mult_shift;
|
||||
clk->mult_width = cld->mult_width;
|
||||
clk->hw.init = &init;
|
||||
|
||||
hw = &clk->hw;
|
||||
ret = devm_clk_hw_register(clp->dev, hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
clk = ERR_PTR(ret);
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
|
||||
return hw;
|
||||
}
|
||||
|
||||
static int loongson2_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i, clks_num = 0;
|
||||
struct clk_hw *hw;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct loongson2_clk_provider *clp;
|
||||
const struct loongson2_clk_board_info *p, *data;
|
||||
|
||||
data = device_get_match_data(dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
for (p = data; p->name; p++)
|
||||
clks_num++;
|
||||
|
||||
clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
|
||||
GFP_KERNEL);
|
||||
if (!clp)
|
||||
return -ENOMEM;
|
||||
|
||||
clp->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(clp->base))
|
||||
return PTR_ERR(clp->base);
|
||||
|
||||
spin_lock_init(&clp->clk_lock);
|
||||
clp->clk_data.num = clks_num + 1;
|
||||
clp->dev = dev;
|
||||
|
||||
for (i = 0; i < clks_num; i++) {
|
||||
p = &data[i];
|
||||
switch (p->type) {
|
||||
case CLK_TYPE_PLL:
|
||||
hw = loongson2_clk_register(clp, p,
|
||||
&loongson2_pll_recalc_ops);
|
||||
break;
|
||||
case CLK_TYPE_SCALE:
|
||||
hw = loongson2_clk_register(clp, p,
|
||||
&loongson2_freqscale_recalc_ops);
|
||||
break;
|
||||
case CLK_TYPE_DIVIDER:
|
||||
hw = devm_clk_hw_register_divider(dev, p->name,
|
||||
p->parent_name, 0,
|
||||
clp->base + p->reg_offset,
|
||||
p->div_shift, p->div_width,
|
||||
CLK_DIVIDER_ONE_BASED,
|
||||
&clp->clk_lock);
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
|
||||
}
|
||||
|
||||
if (IS_ERR(hw))
|
||||
return dev_err_probe(dev, PTR_ERR(hw),
|
||||
"Register clk: %s, type: %u failed!\n",
|
||||
p->name, p->type);
|
||||
|
||||
clp->clk_data.hws[p->id] = hw;
|
||||
}
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id loongson2_clk_match_table[] = {
|
||||
{ .compatible = "loongson,ls2k-clk" },
|
||||
{ .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
|
||||
@ -338,4 +275,5 @@ static struct platform_driver loongson2_clk_driver = {
|
||||
module_platform_driver(loongson2_clk_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Loongson2 clock driver");
|
||||
MODULE_AUTHOR("Loongson Technology Corporation Limited");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
Loading…
Reference in New Issue
Block a user