clk: socfpga: agilex: add clock driver for eASIC N5X platform

Add support for Intel's eASIC N5X platform. The clock manager driver for
the N5X is very similar to the Agilex platform, we can re-use most of
the Agilex clock driver.

This patch makes the necessary changes for the driver to differentiate
between the Agilex and the N5X platforms.

Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
Link: https://lore.kernel.org/r/20210212143059.478554-2-dinguyen@kernel.org
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
Dinh Nguyen 2021-02-12 08:30:59 -06:00 committed by Stephen Boyd
parent 2bea59d388
commit a0f9819cbe
4 changed files with 238 additions and 3 deletions

View File

@ -196,6 +196,17 @@ static const struct stratix10_pll_clock agilex_pll_clks[] = {
0, 0x9c},
};
static const struct n5x_perip_c_clock n5x_main_perip_c_clks[] = {
{ AGILEX_MAIN_PLL_C0_CLK, "main_pll_c0", "main_pll", NULL, 1, 0, 0x54, 0},
{ AGILEX_MAIN_PLL_C1_CLK, "main_pll_c1", "main_pll", NULL, 1, 0, 0x54, 8},
{ AGILEX_MAIN_PLL_C2_CLK, "main_pll_c2", "main_pll", NULL, 1, 0, 0x54, 16},
{ AGILEX_MAIN_PLL_C3_CLK, "main_pll_c3", "main_pll", NULL, 1, 0, 0x54, 24},
{ AGILEX_PERIPH_PLL_C0_CLK, "peri_pll_c0", "periph_pll", NULL, 1, 0, 0xA8, 0},
{ AGILEX_PERIPH_PLL_C1_CLK, "peri_pll_c1", "periph_pll", NULL, 1, 0, 0xA8, 8},
{ AGILEX_PERIPH_PLL_C2_CLK, "peri_pll_c2", "periph_pll", NULL, 1, 0, 0xA8, 16},
{ AGILEX_PERIPH_PLL_C3_CLK, "peri_pll_c3", "periph_pll", NULL, 1, 0, 0xA8, 24},
};
static const struct stratix10_perip_c_clock agilex_main_perip_c_clks[] = {
{ AGILEX_MAIN_PLL_C0_CLK, "main_pll_c0", "main_pll", NULL, 1, 0, 0x58},
{ AGILEX_MAIN_PLL_C1_CLK, "main_pll_c1", "main_pll", NULL, 1, 0, 0x5C},
@ -289,6 +300,25 @@ static const struct stratix10_gate_clock agilex_gate_clks[] = {
10, 0, 0, 0, 0, 0, 4},
};
static int n5x_clk_register_c_perip(const struct n5x_perip_c_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk *clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
clk = n5x_register_periph(&clks[i], base);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.clks[clks[i].id] = clk;
}
return 0;
}
static int agilex_clk_register_c_perip(const struct stratix10_perip_c_clock *clks,
int nums, struct stratix10_clock_data *data)
{
@ -367,6 +397,26 @@ static int agilex_clk_register_pll(const struct stratix10_pll_clock *clks,
return 0;
}
static int n5x_clk_register_pll(const struct stratix10_pll_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk *clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
clk = n5x_register_pll(&clks[i], base);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.clks[clks[i].id] = clk;
}
return 0;
}
static struct stratix10_clock_data *__socfpga_agilex_clk_init(struct platform_device *pdev,
int nr_clks)
{
@ -401,7 +451,7 @@ static struct stratix10_clock_data *__socfpga_agilex_clk_init(struct platform_de
return clk_data;
}
static int agilex_clkmgr_probe(struct platform_device *pdev)
static int agilex_clkmgr_init(struct platform_device *pdev)
{
struct stratix10_clock_data *clk_data;
@ -423,9 +473,43 @@ static int agilex_clkmgr_probe(struct platform_device *pdev)
return 0;
}
static int n5x_clkmgr_init(struct platform_device *pdev)
{
struct stratix10_clock_data *clk_data;
clk_data = __socfpga_agilex_clk_init(pdev, AGILEX_NUM_CLKS);
if (IS_ERR(clk_data))
return PTR_ERR(clk_data);
n5x_clk_register_pll(agilex_pll_clks, ARRAY_SIZE(agilex_pll_clks), clk_data);
n5x_clk_register_c_perip(n5x_main_perip_c_clks,
ARRAY_SIZE(n5x_main_perip_c_clks), clk_data);
agilex_clk_register_cnt_perip(agilex_main_perip_cnt_clks,
ARRAY_SIZE(agilex_main_perip_cnt_clks),
clk_data);
agilex_clk_register_gate(agilex_gate_clks, ARRAY_SIZE(agilex_gate_clks),
clk_data);
return 0;
}
static int agilex_clkmgr_probe(struct platform_device *pdev)
{
int (*probe_func)(struct platform_device *init_func);
probe_func = of_device_get_match_data(&pdev->dev);
if (!probe_func)
return -ENODEV;
return probe_func(pdev);
}
static const struct of_device_id agilex_clkmgr_match_table[] = {
{ .compatible = "intel,agilex-clkmgr",
.data = agilex_clkmgr_probe },
.data = agilex_clkmgr_init },
{ .compatible = "intel,easic-n5x-clkmgr",
.data = n5x_clkmgr_init },
{ }
};

View File

@ -15,6 +15,21 @@
#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
static unsigned long n5x_clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
unsigned long div;
unsigned long shift = socfpgaclk->shift;
u32 val;
val = readl(socfpgaclk->hw.reg);
val &= (0x1f << shift);
div = (val >> shift) + 1;
return parent_rate / div;
}
static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
@ -63,6 +78,11 @@ static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
return parent;
}
static const struct clk_ops n5x_peri_c_clk_ops = {
.recalc_rate = n5x_clk_peri_c_clk_recalc_rate,
.get_parent = clk_periclk_get_parent,
};
static const struct clk_ops peri_c_clk_ops = {
.recalc_rate = clk_peri_c_clk_recalc_rate,
.get_parent = clk_periclk_get_parent,
@ -107,6 +127,39 @@ struct clk *s10_register_periph(const struct stratix10_perip_c_clock *clks,
return clk;
}
struct clk *n5x_register_periph(const struct n5x_perip_c_clock *clks,
void __iomem *regbase)
{
struct clk *clk;
struct socfpga_periph_clk *periph_clk;
struct clk_init_data init;
const char *name = clks->name;
const char *parent_name = clks->parent_name;
periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
if (WARN_ON(!periph_clk))
return NULL;
periph_clk->hw.reg = regbase + clks->offset;
periph_clk->shift = clks->shift;
init.name = name;
init.ops = &n5x_peri_c_clk_ops;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = parent_name ? &parent_name : NULL;
periph_clk->hw.hw.init = &init;
clk = clk_register(NULL, &periph_clk->hw.hw);
if (WARN_ON(IS_ERR(clk))) {
kfree(periph_clk);
return NULL;
}
return clk;
}
struct clk *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
void __iomem *regbase)
{

View File

@ -27,10 +27,37 @@
#define SWCTRLBTCLKSEL_MASK 0x200
#define SWCTRLBTCLKSEL_SHIFT 9
#define SOCFPGA_N5X_PLLDIV_FDIV_MASK GENMASK(16, 8)
#define SOCFPGA_N5X_PLLDIV_FDIV_SHIFT 8
#define SOCFPGA_N5X_PLLDIV_RDIV_MASK GENMASK(5, 0)
#define SOCFPGA_N5X_PLLDIV_QDIV_MASK GENMASK(26, 24)
#define SOCFPGA_N5X_PLLDIV_QDIV_SHIFT 24
#define SOCFPGA_BOOT_CLK "boot_clk"
#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
static unsigned long n5x_clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
unsigned long fdiv, reg, rdiv, qdiv;
u32 power = 1;
/* read VCO1 reg for numerator and denominator */
reg = readl(socfpgaclk->hw.reg + 0x8);
fdiv = (reg & SOCFPGA_N5X_PLLDIV_FDIV_MASK) >> SOCFPGA_N5X_PLLDIV_FDIV_SHIFT;
rdiv = (reg & SOCFPGA_N5X_PLLDIV_RDIV_MASK);
qdiv = (reg & SOCFPGA_N5X_PLLDIV_QDIV_MASK) >> SOCFPGA_N5X_PLLDIV_QDIV_SHIFT;
while (qdiv) {
power *= 2;
qdiv--;
}
return ((parent_rate * 2 * (fdiv + 1)) / ((rdiv + 1) * power));
}
static unsigned long agilex_clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
@ -123,6 +150,25 @@ static int clk_pll_prepare(struct clk_hw *hwclk)
return 0;
}
static int n5x_clk_pll_prepare(struct clk_hw *hwclk)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 reg;
/* Bring PLL out of reset */
reg = readl(socfpgaclk->hw.reg + 0x4);
reg |= SOCFPGA_PLL_RESET_MASK;
writel(reg, socfpgaclk->hw.reg + 0x4);
return 0;
}
static const struct clk_ops n5x_clk_pll_ops = {
.recalc_rate = n5x_clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
.prepare = n5x_clk_pll_prepare,
};
static const struct clk_ops agilex_clk_pll_ops = {
.recalc_rate = agilex_clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
@ -214,3 +260,40 @@ struct clk *agilex_register_pll(const struct stratix10_pll_clock *clks,
}
return clk;
}
struct clk *n5x_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg)
{
struct clk *clk;
struct socfpga_pll *pll_clk;
struct clk_init_data init;
const char *name = clks->name;
pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
if (WARN_ON(!pll_clk))
return NULL;
pll_clk->hw.reg = reg + clks->offset;
if (streq(name, SOCFPGA_BOOT_CLK))
init.ops = &clk_boot_ops;
else
init.ops = &n5x_clk_pll_ops;
init.name = name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = NULL;
init.parent_data = clks->parent_data;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
clk = clk_register(NULL, &pll_clk->hw.hw);
if (WARN_ON(IS_ERR(clk))) {
kfree(pll_clk);
return NULL;
}
return clk;
}

View File

@ -30,6 +30,17 @@ struct stratix10_perip_c_clock {
unsigned long offset;
};
struct n5x_perip_c_clock {
unsigned int id;
const char *name;
const char *parent_name;
const char *const *parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
unsigned long shift;
};
struct stratix10_perip_cnt_clock {
unsigned int id;
const char *name;
@ -64,8 +75,12 @@ struct clk *s10_register_pll(const struct stratix10_pll_clock *,
void __iomem *);
struct clk *agilex_register_pll(const struct stratix10_pll_clock *,
void __iomem *);
struct clk *n5x_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg);
struct clk *s10_register_periph(const struct stratix10_perip_c_clock *,
void __iomem *);
void __iomem *reg);
struct clk *n5x_register_periph(const struct n5x_perip_c_clock *clks,
void __iomem *reg);
struct clk *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *,
void __iomem *);
struct clk *s10_register_gate(const struct stratix10_gate_clock *,