diff options
Diffstat (limited to 'drivers/clk/uniphier/clk-uniphier-core.c')
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-core.c | 267 |
1 files changed, 194 insertions, 73 deletions
diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 722cd6b060..3d1d411e79 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Socionext Inc. + * Copyright (C) 2016-2017 Socionext Inc. * Author: Masahiro Yamada <yamada.masahiro@socionext.com> * * SPDX-License-Identifier: GPL-2.0+ @@ -21,104 +21,224 @@ * @data: SoC specific data */ struct uniphier_clk_priv { + struct udevice *dev; void __iomem *base; const struct uniphier_clk_data *data; }; -static int uniphier_clk_enable(struct clk *clk) +static void uniphier_clk_gate_enable(struct uniphier_clk_priv *priv, + const struct uniphier_clk_gate_data *gate) { - struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); - unsigned long id = clk->id; - const struct uniphier_clk_gate_data *p; + u32 val; - for (p = priv->data->gate; p->id != UNIPHIER_CLK_ID_END; p++) { - u32 val; + val = readl(priv->base + gate->reg); + val |= BIT(gate->bit); + writel(val, priv->base + gate->reg); +} - if (p->id != id) - continue; +static void uniphier_clk_mux_set_parent(struct uniphier_clk_priv *priv, + const struct uniphier_clk_mux_data *mux, + u8 id) +{ + u32 val; + int i; - val = readl(priv->base + p->reg); - val |= BIT(p->bit); - writel(val, priv->base + p->reg); + for (i = 0; i < mux->num_parents; i++) { + if (mux->parent_ids[i] != id) + continue; - return 0; + val = readl(priv->base + mux->reg); + val &= ~mux->masks[i]; + val |= mux->vals[i]; + writel(val, priv->base + mux->reg); + return; } - dev_err(priv->dev, "clk_id=%lu was not handled\n", id); - return -EINVAL; + WARN_ON(1); } -static const struct uniphier_clk_mux_data * -uniphier_clk_get_mux_data(struct uniphier_clk_priv *priv, unsigned long id) +static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv, + const struct uniphier_clk_mux_data *mux) { - const struct uniphier_clk_mux_data *p; + u32 val; + int i; - for (p = priv->data->mux; p->id != UNIPHIER_CLK_ID_END; p++) { - if (p->id == id) - return p; - } + val = readl(priv->base + mux->reg); + + for (i = 0; i < mux->num_parents; i++) + if ((mux->masks[i] & val) == mux->vals[i]) + return mux->parent_ids[i]; + + dev_err(priv->dev, "invalid mux setting\n"); + + return UNIPHIER_CLK_ID_INVALID; +} + +static const struct uniphier_clk_data *uniphier_clk_get_data( + struct uniphier_clk_priv *priv, u8 id) +{ + const struct uniphier_clk_data *data; + + for (data = priv->data; data->type != UNIPHIER_CLK_TYPE_END; data++) + if (data->id == id) + return data; + + dev_err(priv->dev, "id=%u not found\n", id); return NULL; } -static ulong uniphier_clk_get_rate(struct clk *clk) +static const struct uniphier_clk_data *uniphier_clk_get_parent_data( + struct uniphier_clk_priv *priv, + const struct uniphier_clk_data *data) { - struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); - const struct uniphier_clk_mux_data *mux; - u32 val; - int i; + const struct uniphier_clk_data *parent_data; + u8 parent_id = UNIPHIER_CLK_ID_INVALID; + + switch (data->type) { + case UNIPHIER_CLK_TYPE_GATE: + parent_id = data->data.gate.parent_id; + break; + case UNIPHIER_CLK_TYPE_MUX: + parent_id = uniphier_clk_mux_get_parent(priv, &data->data.mux); + break; + default: + break; + } - mux = uniphier_clk_get_mux_data(priv, clk->id); - if (!mux) - return 0; + if (parent_id == UNIPHIER_CLK_ID_INVALID) + return NULL; - if (!mux->nr_muxs) /* fixed-rate */ - return mux->rates[0]; + parent_data = uniphier_clk_get_data(priv, parent_id); - val = readl(priv->base + mux->reg); + WARN_ON(!parent_data); - for (i = 0; i < mux->nr_muxs; i++) - if ((mux->masks[i] & val) == mux->vals[i]) - return mux->rates[i]; + return parent_data; +} + +static void __uniphier_clk_enable(struct uniphier_clk_priv *priv, + const struct uniphier_clk_data *data) +{ + const struct uniphier_clk_data *parent_data; - return -EINVAL; + if (data->type == UNIPHIER_CLK_TYPE_GATE) + uniphier_clk_gate_enable(priv, &data->data.gate); + + parent_data = uniphier_clk_get_parent_data(priv, data); + if (!parent_data) + return; + + return __uniphier_clk_enable(priv, parent_data); } -static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate) +static int uniphier_clk_enable(struct clk *clk) { struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); - const struct uniphier_clk_mux_data *mux; - u32 val; - int i, best_rate_id = -1; - ulong best_rate = 0; + const struct uniphier_clk_data *data; + + data = uniphier_clk_get_data(priv, clk->id); + if (!data) + return -ENODEV; + + __uniphier_clk_enable(priv, data); + + return 0; +} + +static unsigned long __uniphier_clk_get_rate( + struct uniphier_clk_priv *priv, + const struct uniphier_clk_data *data) +{ + const struct uniphier_clk_data *parent_data; - mux = uniphier_clk_get_mux_data(priv, clk->id); - if (!mux) + if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) + return data->data.rate.fixed_rate; + + parent_data = uniphier_clk_get_parent_data(priv, data); + if (!parent_data) return 0; - if (!mux->nr_muxs) /* fixed-rate */ - return mux->rates[0]; + return __uniphier_clk_get_rate(priv, parent_data); +} - /* first, decide the best match rate */ - for (i = 0; i < mux->nr_muxs; i++) { - if (mux->rates[i] > best_rate && mux->rates[i] <= rate) { - best_rate = mux->rates[i]; - best_rate_id = i; +static unsigned long uniphier_clk_get_rate(struct clk *clk) +{ + struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); + const struct uniphier_clk_data *data; + + data = uniphier_clk_get_data(priv, clk->id); + if (!data) + return -ENODEV; + + return __uniphier_clk_get_rate(priv, data); +} + +static unsigned long __uniphier_clk_set_rate( + struct uniphier_clk_priv *priv, + const struct uniphier_clk_data *data, + unsigned long rate, bool set) +{ + const struct uniphier_clk_data *best_parent_data = NULL; + const struct uniphier_clk_data *parent_data; + unsigned long best_rate = 0; + unsigned long parent_rate; + u8 parent_id; + int i; + + if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) + return data->data.rate.fixed_rate; + + if (data->type == UNIPHIER_CLK_TYPE_GATE) { + parent_data = uniphier_clk_get_parent_data(priv, data); + if (!parent_data) + return 0; + + return __uniphier_clk_set_rate(priv, parent_data, rate, set); + } + + if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX)) + return -EINVAL; + + for (i = 0; i < data->data.mux.num_parents; i++) { + parent_id = data->data.mux.parent_ids[i]; + parent_data = uniphier_clk_get_data(priv, parent_id); + if (WARN_ON(!parent_data)) + return -EINVAL; + + parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate, + false); + + if (parent_rate <= rate && best_rate < parent_rate) { + best_rate = parent_rate; + best_parent_data = parent_data; } } - if (best_rate_id < 0) + dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate); + + if (!best_parent_data) return -EINVAL; - val = readl(priv->base + mux->reg); - val &= ~mux->masks[best_rate_id]; - val |= mux->vals[best_rate_id]; - writel(val, priv->base + mux->reg); + if (!set) + return best_rate; + + uniphier_clk_mux_set_parent(priv, &data->data.mux, + best_parent_data->id); + + return best_rate = __uniphier_clk_set_rate(priv, best_parent_data, + rate, true); +} + +static unsigned long uniphier_clk_set_rate(struct clk *clk, ulong rate) +{ + struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); + const struct uniphier_clk_data *data; - debug("%s: requested rate = %lu, set rate = %lu\n", __func__, - rate, best_rate); + data = uniphier_clk_get_data(priv, clk->id); + if (!data) + return -ENODEV; - return best_rate; + return __uniphier_clk_set_rate(priv, data, rate, true); } static const struct clk_ops uniphier_clk_ops = { @@ -140,6 +260,7 @@ static int uniphier_clk_probe(struct udevice *dev) if (!priv->base) return -ENOMEM; + priv->dev = dev; priv->data = (void *)dev_get_driver_data(dev); return 0; @@ -149,60 +270,60 @@ static const struct udevice_id uniphier_clk_match[] = { /* System clock */ { .compatible = "socionext,uniphier-ld4-clock", - .data = (ulong)&uniphier_pxs2_sys_clk_data, + .data = (ulong)uniphier_pxs2_sys_clk_data, }, { .compatible = "socionext,uniphier-pro4-clock", - .data = (ulong)&uniphier_pxs2_sys_clk_data, + .data = (ulong)uniphier_pxs2_sys_clk_data, }, { .compatible = "socionext,uniphier-sld8-clock", - .data = (ulong)&uniphier_pxs2_sys_clk_data, + .data = (ulong)uniphier_pxs2_sys_clk_data, }, { .compatible = "socionext,uniphier-pro5-clock", - .data = (ulong)&uniphier_pxs2_sys_clk_data, + .data = (ulong)uniphier_pxs2_sys_clk_data, }, { .compatible = "socionext,uniphier-pxs2-clock", - .data = (ulong)&uniphier_pxs2_sys_clk_data, + .data = (ulong)uniphier_pxs2_sys_clk_data, }, { .compatible = "socionext,uniphier-ld11-clock", - .data = (ulong)&uniphier_ld20_sys_clk_data, + .data = (ulong)uniphier_ld20_sys_clk_data, }, { .compatible = "socionext,uniphier-ld20-clock", - .data = (ulong)&uniphier_ld20_sys_clk_data, + .data = (ulong)uniphier_ld20_sys_clk_data, }, /* Media I/O clock */ { .compatible = "socionext,uniphier-ld4-mio-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { .compatible = "socionext,uniphier-pro4-mio-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { .compatible = "socionext,uniphier-sld8-mio-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { .compatible = "socionext,uniphier-pro5-sd-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { .compatible = "socionext,uniphier-pxs2-sd-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { .compatible = "socionext,uniphier-ld11-mio-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { .compatible = "socionext,uniphier-ld20-sd-clock", - .data = (ulong)&uniphier_mio_clk_data, + .data = (ulong)uniphier_mio_clk_data, }, { /* sentinel */ } }; |