diff options
author | Masahiro Yamada <yamada.masahiro@socionext.com> | 2016-02-02 21:11:32 +0900 |
---|---|---|
committer | Masahiro Yamada <yamada.masahiro@socionext.com> | 2016-02-14 16:36:13 +0900 |
commit | 48264d9beba2ccc18b9497944048eea135883529 (patch) | |
tree | 165956e2eae989ea4ca52144aad1aabf9a7bdb8d /drivers/clk | |
parent | fec4816387a978e651da74c83bcdd5019a6d024c (diff) | |
download | u-boot-imx-48264d9beba2ccc18b9497944048eea135883529.zip u-boot-imx-48264d9beba2ccc18b9497944048eea135883529.tar.gz u-boot-imx-48264d9beba2ccc18b9497944048eea135883529.tar.bz2 |
clk: uniphier: add Media I/O clock driver for UniPhier SoCs
This is the initial commit for the UniPhier clock drivers.
Currently, only the Media I/O clock is supported.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Kconfig | 2 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/uniphier/Kconfig | 13 | ||||
-rw-r--r-- | drivers/clk/uniphier/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-core.c | 159 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-mio.c | 178 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier.h | 57 |
7 files changed, 413 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9fcde39..a98b74b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -20,4 +20,6 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. +source "drivers/clk/uniphier/Kconfig" + endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c9144e3..c51db15 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o +obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ diff --git a/drivers/clk/uniphier/Kconfig b/drivers/clk/uniphier/Kconfig new file mode 100644 index 0000000..0e90c01 --- /dev/null +++ b/drivers/clk/uniphier/Kconfig @@ -0,0 +1,13 @@ +config CLK_UNIPHIER + bool + select CLK + select SPL_CLK + +menu "Clock drivers for UniPhier SoCs" + depends on CLK_UNIPHIER + +config CLK_UNIPHIER_MIO + bool "Clock driver for UniPhier Media I/O block" + default y + +endmenu diff --git a/drivers/clk/uniphier/Makefile b/drivers/clk/uniphier/Makefile new file mode 100644 index 0000000..a3168f9 --- /dev/null +++ b/drivers/clk/uniphier/Makefile @@ -0,0 +1,3 @@ +obj-y += clk-uniphier-core.o + +obj-$(CONFIG_CLK_UNIPHIER_MIO) += clk-uniphier-mio.o diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c new file mode 100644 index 0000000..e79e0ff --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mapmem.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <clk.h> +#include <dm/device.h> + +#include "clk-uniphier.h" + +DECLARE_GLOBAL_DATA_PTR; + +static int uniphier_clk_enable(struct udevice *dev, int index) +{ + 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; + int i; + + for (i = 0; i < nr_gate; i++) { + if (gate[i].index != index) + 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); + } + + return 0; +} + +static ulong uniphier_clk_get_rate(struct udevice *dev, int index) +{ + 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; + int i; + + for (i = 0; i < nr_rdata; i++) { + if (rdata[i].index != index) + continue; + + 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; + } + } + + debug("%s: rate = %lu\n", __func__, matched_rate); + + return matched_rate; +} + +static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate) +{ + 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; + int i; + + /* first, decide the best match rate */ + for (i = 0; i < nr_rdata; i++) { + if (rdata[i].index != index) + continue; + + if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) + return 0; + + if (rdata[i].rate > best_rate && rdata[i].rate <= rate) + best_rate = rdata[i].rate; + } + + if (!best_rate) + return -ENODEV; + + debug("%s: requested rate = %lu, set rate = %lu\n", __func__, + rate, best_rate); + + /* second, really set registers */ + for (i = 0; i < nr_rdata; i++) { + if (rdata[i].index != index || rdata[i].rate != best_rate) + continue; + + reg = priv->base + rdata[i].reg; + mask = rdata[i].mask; + data = rdata[i].data & mask; + + tmp = readl(reg); + tmp &= ~mask; + tmp |= data; + debug("%s: %p: %08x\n", __func__, reg, tmp); + writel(tmp, reg); + } + + return best_rate; +} + +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, +}; + +int uniphier_clk_probe(struct udevice *dev) +{ + struct uniphier_clk_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", + &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = map_sysmem(addr, size); + if (!priv->base) + return -ENOMEM; + + priv->socdata = (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); + + return 0; +} diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c new file mode 100644 index 0000000..d91ae34 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-mio.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <clk.h> +#include <dm/device.h> + +#include "clk-uniphier.h" + +#define UNIPHIER_MIO_CLK_GATE_SD(ch, idx) \ + { \ + .index = (idx), \ + .reg = 0x20 + 0x200 * (ch), \ + .mask = 0x00000100, \ + .data = 0x00000100, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x110 + 0x200 * (ch), \ + .mask = 0x00000001, \ + .data = 0x00000001, \ + } + +#define UNIPHIER_MIO_CLK_RATE_SD(ch, idx) \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00000000, \ + .rate = 44444444, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00010000, \ + .rate = 33333333, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00020000, \ + .rate = 50000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00020000, \ + .rate = 66666666, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00001000, \ + .rate = 100000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00001100, \ + .rate = 40000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00001200, \ + .rate = 25000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x30 + 0x200 * (ch), \ + .mask = 0x00031300, \ + .data = 0x00001300, \ + .rate = 22222222, \ + } + +#define UNIPHIER_MIO_CLK_GATE_USB(ch, idx) \ + { \ + .index = (idx), \ + .reg = 0x20 + 0x200 * (ch), \ + .mask = 0x30000000, \ + .data = 0x30000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x110 + 0x200 * (ch), \ + .mask = 0x01000000, \ + .data = 0x01000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x114 + 0x200 * (ch), \ + .mask = 0x00000001, \ + .data = 0x00000001, \ + } + +#define UNIPHIER_MIO_CLK_GATE_DMAC(idx) \ + { \ + .index = (idx), \ + .reg = 0x20, \ + .mask = 0x02000000, \ + .data = 0x02000000, \ + }, \ + { \ + .index = (idx), \ + .reg = 0x110, \ + .mask = 0x00020000, \ + .data = 0x00020000, \ + } + +static struct uniphier_clk_gate_data uniphier_mio_clk_gate[] = { + UNIPHIER_MIO_CLK_GATE_SD(0, 0), + UNIPHIER_MIO_CLK_GATE_SD(1, 1), + UNIPHIER_MIO_CLK_GATE_SD(2, 2), /* for PH1-Pro4 only */ + UNIPHIER_MIO_CLK_GATE_USB(0, 3), + UNIPHIER_MIO_CLK_GATE_USB(1, 4), + UNIPHIER_MIO_CLK_GATE_USB(2, 5), + UNIPHIER_MIO_CLK_GATE_DMAC(6), + UNIPHIER_MIO_CLK_GATE_USB(3, 7), /* for PH1-sLD3 only */ +}; + +static struct uniphier_clk_rate_data uniphier_mio_clk_rate[] = { + UNIPHIER_MIO_CLK_RATE_SD(0, 0), + UNIPHIER_MIO_CLK_RATE_SD(1, 1), + UNIPHIER_MIO_CLK_RATE_SD(2, 2), /* for PH1-Pro4 only */ +}; + +static struct uniphier_clk_soc_data uniphier_mio_clk_data = { + .gate = uniphier_mio_clk_gate, + .nr_gate = ARRAY_SIZE(uniphier_mio_clk_gate), + .rate = uniphier_mio_clk_rate, + .nr_rate = ARRAY_SIZE(uniphier_mio_clk_rate), +}; + +static const struct udevice_id uniphier_mio_clk_match[] = { + { + .compatible = "socionext,ph1-sld3-mioctrl", + .data = (ulong)&uniphier_mio_clk_data, + }, + { + .compatible = "socionext,ph1-ld4-mioctrl", + .data = (ulong)&uniphier_mio_clk_data, + }, + { + .compatible = "socionext,ph1-pro4-mioctrl", + .data = (ulong)&uniphier_mio_clk_data, + }, + { + .compatible = "socionext,ph1-sld8-mioctrl", + .data = (ulong)&uniphier_mio_clk_data, + }, + { + .compatible = "socionext,ph1-pro5-mioctrl", + .data = (ulong)&uniphier_mio_clk_data, + }, + { + .compatible = "socionext,proxstream2-mioctrl", + .data = (ulong)&uniphier_mio_clk_data, + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(uniphier_mio_clk) = { + .name = "uniphier-mio-clk", + .id = UCLASS_CLK, + .of_match = uniphier_mio_clk_match, + .probe = uniphier_clk_probe, + .remove = uniphier_clk_remove, + .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv), + .ops = &uniphier_clk_ops, +}; diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h new file mode 100644 index 0000000..560b3f8 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CLK_UNIPHIER_H__ +#define __CLK_UNIPHIER_H__ + +#include <linux/kernel.h> + +struct uniphier_clk_gate_data { + int index; + unsigned int reg; + u32 mask; + u32 data; +}; + +struct uniphier_clk_rate_data { + int index; + unsigned int reg; +#define UNIPHIER_CLK_RATE_IS_FIXED UINT_MAX + u32 mask; + u32 data; + unsigned long rate; +}; + +struct uniphier_clk_soc_data { + struct uniphier_clk_gate_data *gate; + unsigned int nr_gate; + struct uniphier_clk_rate_data *rate; + unsigned int nr_rate; +}; + +#define UNIPHIER_CLK_FIXED_RATE(i, f) \ + { \ + .index = i, \ + .reg = UNIPHIER_CLK_RATE_IS_FIXED, \ + .rate = f, \ + } + +/** + * struct uniphier_clk_priv - private data for UniPhier clock driver + * + * @base: base address of the clock provider + * @socdata: SoC specific data + */ +struct uniphier_clk_priv { + void __iomem *base; + struct uniphier_clk_soc_data *socdata; +}; + +extern const struct clk_ops uniphier_clk_ops; +int uniphier_clk_probe(struct udevice *dev); +int uniphier_clk_remove(struct udevice *dev); + +#endif /* __CLK_UNIPHIER_H__ */ |