diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c index e4e690a12a23..7b8de08388d3 100644 --- a/drivers/clk/clk-eyeq.c +++ b/drivers/clk/clk-eyeq.c @@ -71,6 +71,13 @@ #define FRACG_PCSR1_DOWN_SPREAD BIT(11) #define FRACG_PCSR1_FRAC_IN GENMASK(31, 12) +/* + * Special index values to lookup a parent clock by its name + * from the device tree or by its globally unique name. + */ +#define PARENT_BY_FWNAME (-1) +#define PARENT_BY_NAME (-2) + struct eqc_pll { unsigned int index; const char *name; @@ -98,6 +105,33 @@ struct eqc_fixed_factor { unsigned int parent; }; +struct eqc_clock { + int index; + int parent_idx; + const char *name; + const char *parent_name; + int (*probe)(struct device *dev, struct device_node *np, + const struct eqc_clock *clk, void __iomem *base, + struct clk_hw_onecell_data *cells); + void (*unregister)(struct clk_hw *hw); + union { + struct { + unsigned int reg; + u8 shift; + u8 width; + unsigned long flags; + const struct clk_div_table *table; + } div; + struct { + unsigned int mult; + unsigned int div; + } ff; + struct { + unsigned int reg; + } pll; + }; +}; + struct eqc_match_data { unsigned int pll_count; const struct eqc_pll *plls; @@ -108,6 +142,9 @@ struct eqc_match_data { unsigned int fixed_factor_count; const struct eqc_fixed_factor *fixed_factors; + unsigned int clk_count; + const struct eqc_clock *clks; + const char *reset_auxdev_name; const char *pinctrl_auxdev_name; const char *eth_phy_auxdev_name; @@ -122,6 +159,9 @@ struct eqc_early_match_data { unsigned int early_fixed_factor_count; const struct eqc_fixed_factor *early_fixed_factors; + unsigned int early_clk_count; + const struct eqc_clock *early_clks; + /* * We want our of_xlate callback to EPROBE_DEFER instead of dev_err() * and EINVAL. For that, we must know the total clock count. @@ -336,6 +376,102 @@ static void eqc_auxdev_create_optional(struct device *dev, void __iomem *base, } } +static int eqc_fill_parent_data(const struct eqc_clock *clk, + struct clk_hw_onecell_data *cells, + struct clk_parent_data *parent_data) +{ + int pidx = clk->parent_idx; + + memset(parent_data, 0, sizeof(struct clk_parent_data)); + + if (pidx == PARENT_BY_FWNAME) { + /* lookup the parent clock by its fw_name */ + parent_data->index = -1; + parent_data->fw_name = clk->parent_name; + } else if (pidx == PARENT_BY_NAME) { + /* lookup the parent clock by its global name */ + parent_data->index = -1; + parent_data->name = clk->parent_name; + } else if (pidx >= 0 && pidx < cells->num && !IS_ERR(cells->hws[pidx])) { + /* get the parent hw directly */ + parent_data->hw = cells->hws[pidx]; + } else { + /* no parent lookup by index: explicitly fail */ + return -EINVAL; + } + + return 0; +} + +static int eqc_probe_divider(struct device *dev, struct device_node *np, + const struct eqc_clock *clk, void __iomem *base, + struct clk_hw_onecell_data *cells) +{ + struct clk_parent_data parent_data; + struct clk_hw *hw; + int ret; + + ret = eqc_fill_parent_data(clk, cells, &parent_data); + if (ret) + return ret; + + hw = clk_hw_register_divider_table_parent_data(dev, clk->name, + &parent_data, 0, base + clk->div.reg, clk->div.shift, + clk->div.width, clk->div.flags, clk->div.table, NULL); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + cells->hws[clk->index] = hw; + return 0; +} + +static int eqc_probe_fixed_factor(struct device *dev, struct device_node *np, + const struct eqc_clock *clk, void __iomem *base, + struct clk_hw_onecell_data *cells) +{ + struct clk_parent_data parent_data; + struct clk_hw *hw; + int ret; + + ret = eqc_fill_parent_data(clk, cells, &parent_data); + if (ret) + return ret; + + hw = clk_hw_register_fixed_factor_pdata(dev, np, clk->name, &parent_data, 0, + clk->ff.mult, clk->ff.div, 0, 0); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + cells->hws[clk->index] = hw; + return 0; +} + +static int eqc_probe_pll_fracg(struct device *dev, struct device_node *np, + const struct eqc_clock *clk, void __iomem *base, + struct clk_hw_onecell_data *cells) +{ + struct clk_parent_data parent_data; + unsigned long mult, div, acc; + struct clk_hw *hw; + int ret; + + ret = eqc_pll_parse_fracg(base + clk->pll.reg, &mult, &div, &acc); + if (ret) + return ret; + + ret = eqc_fill_parent_data(clk, cells, &parent_data); + if (ret) + return ret; + + hw = clk_hw_register_fixed_factor_pdata(dev, np, clk->name, &parent_data, 0, mult, + div, acc, CLK_FIXED_FACTOR_FIXED_ACCURACY); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + cells->hws[clk->index] = hw; + return 0; +} + static int eqc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -345,6 +481,7 @@ static int eqc_probe(struct platform_device *pdev) unsigned int i, clk_count; struct resource *res; void __iomem *base; + int ret; data = device_get_match_data(dev); if (!data) @@ -363,11 +500,12 @@ static int eqc_probe(struct platform_device *pdev) eqc_auxdev_create_optional(dev, base, data->pinctrl_auxdev_name); eqc_auxdev_create_optional(dev, base, data->eth_phy_auxdev_name); - if (data->pll_count + data->div_count + data->fixed_factor_count == 0) + if (data->pll_count + data->div_count + data->fixed_factor_count + data->clk_count == 0) return 0; /* Zero clocks, we are done. */ clk_count = data->pll_count + data->div_count + - data->fixed_factor_count + data->early_clk_count; + data->fixed_factor_count + data->clk_count + + data->early_clk_count; cells = kzalloc_flex(*cells, hws, clk_count); if (!cells) return -ENOMEM; @@ -384,9 +522,59 @@ static int eqc_probe(struct platform_device *pdev) eqc_probe_init_fixed_factors(dev, data, cells); + for (i = 0; i < data->clk_count; i++) { + const struct eqc_clock *clk = &data->clks[i]; + + if (clk->probe) + ret = clk->probe(dev, NULL, clk, base, cells); + else + ret = -EINVAL; + if (ret) + dev_warn(dev, "failed probing clock %s: %d\n", clk->name, ret); + } + return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells); } +#define DIV(_index, _parent_idx, _name, _parent_name, \ + _reg, _shift, _width) \ + { \ + .index = _index, \ + .parent_idx = _parent_idx, \ + .name = _name, \ + .parent_name = _parent_name, \ + .probe = eqc_probe_divider, \ + .unregister = clk_hw_unregister_divider, \ + .div.reg = _reg, \ + .div.shift = _shift, \ + .div.width = _width, \ + .div.flags = CLK_DIVIDER_EVEN_INTEGERS, \ + .div.table = NULL, \ + } + +#define FF(_index, _parent_idx, _name, _parent_name, _mult, _div) \ + { \ + .index = _index, \ + .parent_idx = _parent_idx, \ + .name = _name, \ + .parent_name = _parent_name, \ + .probe = eqc_probe_fixed_factor, \ + .unregister = clk_hw_unregister_fixed_factor, \ + .ff.mult = _mult, \ + .ff.div = _div, \ + } + +#define PLL_FRACG(_index, _parent_idx, _name, _parent_name, _reg) \ + { \ + .index = _index, \ + .parent_idx = _parent_idx, \ + .name = _name, \ + .parent_name = _parent_name, \ + .probe = eqc_probe_pll_fracg, \ + .unregister = clk_hw_unregister_fixed_factor, \ + .pll.reg = _reg, \ + } + /* Required early for GIC timer (pll-cpu) and UARTs (pll-per). */ static const struct eqc_pll eqc_eyeq5_early_plls[] = { { .index = EQ5C_PLL_CPU, .name = "pll-cpu", .reg64 = 0x02C }, @@ -769,7 +957,7 @@ static void __init eqc_early_init(struct device_node *np, int ret; clk_count = early_data->early_pll_count + early_data->early_fixed_factor_count + - early_data->late_clk_count; + early_data->early_clk_count + early_data->late_clk_count; cells = kzalloc_flex(*cells, hws, clk_count); if (!cells) { ret = -ENOMEM; @@ -831,6 +1019,19 @@ static void __init eqc_early_init(struct device_node *np, } } + for (i = 0; i < early_data->early_clk_count; i++) { + const struct eqc_clock *clk = &early_data->early_clks[i]; + + if (clk->probe) + ret = clk->probe(NULL, np, clk, base, cells); + else + ret = -EINVAL; + if (ret) { + pr_err("failed registering %s\n", clk->name); + goto err; + } + } + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells); if (ret) { pr_err("failed registering clk provider: %d\n", ret); @@ -860,6 +1061,14 @@ static void __init eqc_early_init(struct device_node *np, clk_hw_unregister_fixed_factor(hw); } + for (i = 0; i < early_data->early_clk_count; i++) { + const struct eqc_clock *clk = &early_data->early_clks[i]; + struct clk_hw *hw = cells->hws[clk->index]; + + if (!IS_ERR_OR_NULL(hw) && clk->unregister) + clk->unregister(hw); + } + kfree(cells); } }