X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fclk%2Funiphier%2Fclk-uniphier-core.c;h=1da63819e7db62f86b95d8ce3d4561dac8dad7bb;hb=355be915ed0874d31fd34cd2eee78588cd168553;hp=25c163b395dd2058c3e43e89423c560cd182ab3c;hpb=814013253fd4cf932d0fb32f7043f09a2a748d9a;p=oweals%2Fu-boot.git diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 25c163b395..1da63819e7 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -1,156 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2016 Masahiro Yamada - * - * SPDX-License-Identifier: GPL-2.0+ + * Copyright (C) 2016-2017 Socionext Inc. + * Author: Masahiro Yamada */ #include -#include +#include +#include #include #include #include -#include -#include #include "clk-uniphier.h" -static int uniphier_clk_enable(struct udevice *dev, int index) +/** + * struct uniphier_clk_priv - private data for UniPhier clock driver + * + * @base: base address of the clock provider + * @data: SoC specific data + */ +struct uniphier_clk_priv { + struct udevice *dev; + void __iomem *base; + const struct uniphier_clk_data *data; +}; + +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(dev); - struct uniphier_clk_gate_data *gate = priv->socdata->gate; - unsigned int nr_gate = priv->socdata->nr_gate; - void __iomem *reg; - u32 mask, data, tmp; + u32 val; + + val = readl(priv->base + gate->reg); + val |= BIT(gate->bit); + writel(val, priv->base + gate->reg); +} + +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; - for (i = 0; i < nr_gate; i++) { - if (gate[i].index != index) + for (i = 0; i < mux->num_parents; i++) { + if (mux->parent_ids[i] != id) continue; - reg = priv->base + gate[i].reg; - mask = gate[i].mask; - data = gate[i].data & mask; - - tmp = readl(reg); - tmp &= ~mask; - tmp |= data & mask; - debug("%s: %p: %08x\n", __func__, reg, tmp); - writel(tmp, reg); + val = readl(priv->base + mux->reg); + val &= ~mux->masks[i]; + val |= mux->vals[i]; + writel(val, priv->base + mux->reg); + return; } - return 0; + WARN_ON(1); } -static ulong uniphier_clk_get_rate(struct udevice *dev, int index) +static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv, + const struct uniphier_clk_mux_data *mux) { - struct uniphier_clk_priv *priv = dev_get_priv(dev); - struct uniphier_clk_rate_data *rdata = priv->socdata->rate; - unsigned int nr_rdata = priv->socdata->nr_rate; - void __iomem *reg; - u32 mask, data; - ulong matched_rate = 0; + u32 val; int i; - for (i = 0; i < nr_rdata; i++) { - if (rdata[i].index != index) - continue; + val = readl(priv->base + mux->reg); - if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) - return rdata[i].rate; - - reg = priv->base + rdata[i].reg; - mask = rdata[i].mask; - data = rdata[i].data & mask; - if ((readl(reg) & mask) == data) { - if (matched_rate && rdata[i].rate != matched_rate) { - printf("failed to get clk rate for insane register values\n"); - return -EINVAL; - } - matched_rate = rdata[i].rate; - } + 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 const struct uniphier_clk_data *uniphier_clk_get_parent_data( + struct uniphier_clk_priv *priv, + const struct uniphier_clk_data *data) +{ + 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; } - debug("%s: rate = %lu\n", __func__, matched_rate); + if (parent_id == UNIPHIER_CLK_ID_INVALID) + return NULL; + + parent_data = uniphier_clk_get_data(priv, parent_id); + + WARN_ON(!parent_data); - return matched_rate; + return parent_data; } -static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate) +static void __uniphier_clk_enable(struct uniphier_clk_priv *priv, + const struct uniphier_clk_data *data) { - struct uniphier_clk_priv *priv = dev_get_priv(dev); - struct uniphier_clk_rate_data *rdata = priv->socdata->rate; - unsigned int nr_rdata = priv->socdata->nr_rate; - void __iomem *reg; - u32 mask, data, tmp; - ulong best_rate = 0; + const struct uniphier_clk_data *parent_data; + + 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 int uniphier_clk_enable(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; + + __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; + + 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; + + return __uniphier_clk_get_rate(priv, parent_data); +} + +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; - /* first, decide the best match rate */ - for (i = 0; i < nr_rdata; i++) { - if (rdata[i].index != index) - continue; + if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) + return data->data.rate.fixed_rate; - if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) + if (data->type == UNIPHIER_CLK_TYPE_GATE) { + parent_data = uniphier_clk_get_parent_data(priv, data); + if (!parent_data) return 0; - if (rdata[i].rate > best_rate && rdata[i].rate <= rate) - best_rate = rdata[i].rate; + return __uniphier_clk_set_rate(priv, parent_data, rate, set); } - if (!best_rate) - return -ENODEV; - - debug("%s: requested rate = %lu, set rate = %lu\n", __func__, - rate, best_rate); + if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX)) + return -EINVAL; - /* second, really set registers */ - for (i = 0; i < nr_rdata; i++) { - if (rdata[i].index != index || rdata[i].rate != best_rate) - continue; + 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; - reg = priv->base + rdata[i].reg; - mask = rdata[i].mask; - data = rdata[i].data & mask; + parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate, + false); - tmp = readl(reg); - tmp &= ~mask; - tmp |= data; - debug("%s: %p: %08x\n", __func__, reg, tmp); - writel(tmp, reg); + if (parent_rate <= rate && best_rate < parent_rate) { + best_rate = parent_rate; + best_parent_data = parent_data; + } } - return best_rate; + dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate); + + if (!best_parent_data) + return -EINVAL; + + 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); } -const struct clk_ops uniphier_clk_ops = { +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; + + data = uniphier_clk_get_data(priv, clk->id); + if (!data) + return -ENODEV; + + return __uniphier_clk_set_rate(priv, data, rate, true); +} + +static const struct clk_ops uniphier_clk_ops = { .enable = uniphier_clk_enable, - .get_periph_rate = uniphier_clk_get_rate, - .set_periph_rate = uniphier_clk_set_rate, + .get_rate = uniphier_clk_get_rate, + .set_rate = uniphier_clk_set_rate, }; -int uniphier_clk_probe(struct udevice *dev) +static int uniphier_clk_probe(struct udevice *dev) { struct uniphier_clk_priv *priv = dev_get_priv(dev); fdt_addr_t addr; - addr = dev_get_addr(dev); + addr = devfdt_get_addr(dev->parent); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - priv->base = map_sysmem(addr, SZ_4K); + priv->base = devm_ioremap(dev, addr, SZ_4K); if (!priv->base) return -ENOMEM; - priv->socdata = (void *)dev_get_driver_data(dev); + priv->dev = dev; + priv->data = (void *)dev_get_driver_data(dev); return 0; } -int uniphier_clk_remove(struct udevice *dev) -{ - struct uniphier_clk_priv *priv = dev_get_priv(dev); - - unmap_sysmem(priv->base); +static const struct udevice_id uniphier_clk_match[] = { + /* System clock */ + { + .compatible = "socionext,uniphier-ld4-clock", + .data = (ulong)uniphier_pxs2_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pro4-clock", + .data = (ulong)uniphier_pxs2_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-sld8-clock", + .data = (ulong)uniphier_pxs2_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pro5-clock", + .data = (ulong)uniphier_pxs2_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs2-clock", + .data = (ulong)uniphier_pxs2_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-ld11-clock", + .data = (ulong)uniphier_ld20_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-ld20-clock", + .data = (ulong)uniphier_ld20_sys_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs3-clock", + .data = (ulong)uniphier_pxs3_sys_clk_data, + }, + /* Media I/O clock */ + { + .compatible = "socionext,uniphier-ld4-mio-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pro4-mio-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-sld8-mio-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pro5-sd-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs2-sd-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-ld11-mio-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-ld20-sd-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { + .compatible = "socionext,uniphier-pxs3-sd-clock", + .data = (ulong)uniphier_mio_clk_data, + }, + { /* sentinel */ } +}; - return 0; -} +U_BOOT_DRIVER(uniphier_clk) = { + .name = "uniphier-clk", + .id = UCLASS_CLK, + .of_match = uniphier_clk_match, + .probe = uniphier_clk_probe, + .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv), + .ops = &uniphier_clk_ops, +};