diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2017-01-11 19:05:11 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2017-01-12 07:13:47 +0100 |
commit | dcedcec3751d3307e42b2a6583186b2a39f2076d (patch) | |
tree | 3defe034003c9025c1caf6ce71ac5568e2abf832 | |
parent | 08c62ae2fa9378732588ef7b9a9120b377d26d1e (diff) | |
parent | 1b2cc689fc2db0db56c617e789906a684b22097a (diff) | |
download | barebox-dcedcec3751d3307e42b2a6583186b2a39f2076d.tar.gz barebox-dcedcec3751d3307e42b2a6583186b2a39f2076d.tar.xz |
Merge branch 'for-next/vybrid'
-rw-r--r-- | arch/arm/dts/vf610-twr.dts | 4 | ||||
-rw-r--r-- | arch/arm/mach-imx/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/Makefile | 23 | ||||
-rw-r--r-- | arch/arm/mach-imx/ocotp.c | 51 | ||||
-rw-r--r-- | drivers/clk/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/clk-conf.c | 148 | ||||
-rw-r--r-- | drivers/clk/clk.c | 2 | ||||
-rw-r--r-- | drivers/clk/imx/Makefile | 22 | ||||
-rw-r--r-- | drivers/clk/imx/clk-gate-exclusive.c (renamed from arch/arm/mach-imx/clk-gate-exclusive.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-gate2.c (renamed from arch/arm/mach-imx/clk-gate2.c) | 12 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx1.c (renamed from arch/arm/mach-imx/clk-imx1.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx21.c (renamed from arch/arm/mach-imx/clk-imx21.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx25.c (renamed from arch/arm/mach-imx/clk-imx25.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx27.c (renamed from arch/arm/mach-imx/clk-imx27.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx31.c (renamed from arch/arm/mach-imx/clk-imx31.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx35.c (renamed from arch/arm/mach-imx/clk-imx35.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx5.c (renamed from arch/arm/mach-imx/clk-imx5.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx6.c (renamed from arch/arm/mach-imx/clk-imx6.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx6sx.c (renamed from arch/arm/mach-imx/clk-imx6sx.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx6ul.c (renamed from arch/arm/mach-imx/clk-imx6ul.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pfd.c (renamed from arch/arm/mach-imx/clk-pfd.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pllv1.c (renamed from arch/arm/mach-imx/clk-pllv1.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pllv2.c (renamed from arch/arm/mach-imx/clk-pllv2.c) | 0 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pllv3.c (renamed from arch/arm/mach-imx/clk-pllv3.c) | 9 | ||||
-rw-r--r-- | drivers/clk/imx/clk-vf610.c | 446 | ||||
-rw-r--r-- | drivers/clk/imx/clk.c | 21 | ||||
-rw-r--r-- | drivers/clk/imx/clk.h (renamed from arch/arm/mach-imx/clk.h) | 23 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 3 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-vf610.c | 166 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 222 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc.c | 129 | ||||
-rw-r--r-- | drivers/net/fec_imx.c | 74 | ||||
-rw-r--r-- | drivers/net/fec_imx.h | 10 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 5 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_lpuart.c | 217 | ||||
-rw-r--r-- | include/linux/clk/clk-conf.h | 17 |
38 files changed, 1485 insertions, 126 deletions
diff --git a/arch/arm/dts/vf610-twr.dts b/arch/arm/dts/vf610-twr.dts index 54b4435a54..5947fdbdaa 100644 --- a/arch/arm/dts/vf610-twr.dts +++ b/arch/arm/dts/vf610-twr.dts @@ -12,3 +12,7 @@ &usbdev0 { status = "disabled"; }; + +&enet_ext { + clock-output-names = "enet_ext"; +}; diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index ed5f9d9655..9dbe31c4b6 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -668,7 +668,7 @@ config IMX_IIM_FUSE_BLOW config IMX_OCOTP tristate "i.MX6 On Chip OTP controller" - depends on ARCH_IMX6 + depends on ARCH_IMX6 || ARCH_VF610 depends on OFDEVICE help This adds support for the i.MX6 On-Chip OTP controller. Currently the diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index ee839f7443..d0fe7abc00 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -1,26 +1,23 @@ obj-y += clocksource.o -obj-$(CONFIG_ARCH_IMX1) += imx1.o clk-imx1.o -obj-$(CONFIG_ARCH_IMX25) += imx25.o clk-imx25.o -obj-$(CONFIG_ARCH_IMX21) += imx21.o clk-imx21.o -obj-$(CONFIG_ARCH_IMX27) += imx27.o clk-imx27.o -obj-$(CONFIG_ARCH_IMX31) += imx31.o clk-imx31.o -obj-$(CONFIG_ARCH_IMX35) += imx35.o clk-imx35.o -obj-$(CONFIG_ARCH_IMX50) += imx50.o imx5.o clk-imx5.o +obj-$(CONFIG_ARCH_IMX1) += imx1.o +obj-$(CONFIG_ARCH_IMX25) += imx25.o +obj-$(CONFIG_ARCH_IMX21) += imx21.o +obj-$(CONFIG_ARCH_IMX27) += imx27.o +obj-$(CONFIG_ARCH_IMX31) += imx31.o +obj-$(CONFIG_ARCH_IMX35) += imx35.o +obj-$(CONFIG_ARCH_IMX50) += imx50.o imx5.o pbl-$(CONFIG_ARCH_IMX50) += imx50.o imx5.o -obj-$(CONFIG_ARCH_IMX51) += imx51.o imx5.o clk-imx5.o +obj-$(CONFIG_ARCH_IMX51) += imx51.o imx5.o pbl-$(CONFIG_ARCH_IMX51) += imx51.o imx5.o -obj-$(CONFIG_ARCH_IMX53) += imx53.o imx5.o clk-imx5.o esdctl-v4.o +obj-$(CONFIG_ARCH_IMX53) += imx53.o imx5.o esdctl-v4.o pbl-$(CONFIG_ARCH_IMX53) += imx53.o imx5.o esdctl-v4.o -obj-$(CONFIG_ARCH_IMX6) += imx6.o usb-imx6.o clk-imx6.o +obj-$(CONFIG_ARCH_IMX6) += imx6.o usb-imx6.o lwl-$(CONFIG_ARCH_IMX6) += imx6-mmdc.o -obj-$(CONFIG_ARCH_IMX6SX) += clk-imx6sx.o -obj-$(CONFIG_ARCH_IMX6UL) += clk-imx6ul.o obj-$(CONFIG_ARCH_IMX_XLOAD) += xload.o obj-$(CONFIG_IMX_IIM) += iim.o obj-$(CONFIG_IMX_OCOTP) += ocotp.o obj-$(CONFIG_NAND_IMX) += nand.o lwl-$(CONFIG_ARCH_IMX_EXTERNAL_BOOT_NAND) += external-nand-boot.o -obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-pfd.o clk-gate2.o clk-gate-exclusive.o obj-y += devices.o imx.o obj-pbl-y += esdctl.o boot.o obj-$(CONFIG_BAREBOX_UPDATE) += imx-bbu-internal.o diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c index e1d0c256a5..68ff0ce28b 100644 --- a/arch/arm/mach-imx/ocotp.c +++ b/arch/arm/mach-imx/ocotp.c @@ -67,7 +67,6 @@ #define BF(value, field) (((value) << field) & field##_MASK) /* Other definitions */ -#define FUSE_REGS_COUNT (16 * 8) #define IMX6_OTP_DATA_ERROR_VAL 0xBADABADA #define DEF_RELAX 20 #define MAC_OFFSET (0x22 * 4) @@ -75,6 +74,7 @@ struct imx_ocotp_data { int num_regs; + u32 (*addr_to_offset)(u32 addr); }; struct ocotp_priv { @@ -86,6 +86,7 @@ struct ocotp_priv { int sense_enable; char ethaddr[6]; struct regmap_config map_config; + const struct imx_ocotp_data *data; }; static struct ocotp_priv *imx_ocotp; @@ -196,7 +197,8 @@ static int imx_ocotp_reg_read(void *ctx, unsigned int reg, unsigned int *val) if (ret) return ret; } else { - *(u32 *)val = readl(priv->base + 0x400 + index * 0x10); + *(u32 *)val = readl(priv->base + + priv->data->addr_to_offset(index)); } return 0; @@ -276,7 +278,8 @@ static int imx_ocotp_reg_write(void *ctx, unsigned int reg, unsigned int val) if (ret < 0) return ret; } else { - writel(val, priv->base + 0x400 + index * 0x10); + writel(val, priv->base + + priv->data->addr_to_offset(index)); } if (priv->permanent_write_enable) @@ -438,7 +441,7 @@ static int imx_ocotp_probe(struct device_d *dev) void __iomem *base; struct ocotp_priv *priv; int ret = 0; - struct imx_ocotp_data *data; + const struct imx_ocotp_data *data; ret = dev_get_drvdata(dev, (const void **)&data); if (ret) @@ -453,6 +456,7 @@ static int imx_ocotp_probe(struct device_d *dev) priv = xzalloc(sizeof(*priv)); + priv->data = data; priv->base = base; priv->clk = clk_get(dev, NULL); if (IS_ERR(priv->clk)) @@ -491,12 +495,48 @@ static int imx_ocotp_probe(struct device_d *dev) return 0; } +static u32 imx6sl_addr_to_offset(u32 addr) +{ + return 0x400 + addr * 0x10; +} + +static u32 imx6q_addr_to_offset(u32 addr) +{ + u32 addendum = 0; + + if (addr > 0x2F) { + /* + * If we are reading past Bank 5, take into account a + * 0x100 bytes wide gap between Bank 5 and Bank 6 + */ + addendum += 0x100; + } + + + return imx6sl_addr_to_offset(addr) + addendum; +} + +static u32 vf610_addr_to_offset(u32 addr) +{ + if (addr == 0x04) + return 0x450; + else + return imx6q_addr_to_offset(addr); +} + static struct imx_ocotp_data imx6q_ocotp_data = { .num_regs = 512, + .addr_to_offset = imx6q_addr_to_offset, }; static struct imx_ocotp_data imx6sl_ocotp_data = { .num_regs = 256, + .addr_to_offset = imx6sl_addr_to_offset, +}; + +static struct imx_ocotp_data vf610_ocotp_data = { + .num_regs = 512, + .addr_to_offset = vf610_addr_to_offset, }; static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = { @@ -513,6 +553,9 @@ static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = { .compatible = "fsl,imx6ul-ocotp", .data = &imx6q_ocotp_data, }, { + .compatible = "fsl,vf610-ocotp", + .data = &vf610_ocotp_data, + }, { /* sentinel */ } }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 47ed7b1b6e..a4e4ed0241 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \ clk-mux.o clk-gate.o clk-composite.o \ - clk-fractional-divider.o + clk-fractional-divider.o clk-conf.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_ARCH_MVEBU) += mvebu/ @@ -9,3 +9,4 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_CLK_SOCFPGA) += socfpga.o obj-$(CONFIG_MACH_MIPS_ATH79) += clk-ar933x.o +obj-$(CONFIG_ARCH_IMX) += imx/ diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c new file mode 100644 index 0000000000..93271b4f99 --- /dev/null +++ b/drivers/clk/clk-conf.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <io.h> +#include <malloc.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/clk/clk-conf.h> + +#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER) + +static int __set_clk_parents(struct device_node *node, bool clk_supplier) +{ + struct of_phandle_args clkspec; + int index, rc, num_parents; + struct clk *clk, *pclk; + + num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells"); + if (num_parents == -EINVAL) + pr_err("clk: invalid value of clock-parents property at %s\n", + node->full_name); + + for (index = 0; index < num_parents; index++) { + rc = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return rc; + } + if (clkspec.np == node && !clk_supplier) + return 0; + pclk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(pclk)) { + pr_warn("clk: couldn't get parent clock %d for %s\n", + index, node->full_name); + return PTR_ERR(pclk); + } + + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) + goto err; + if (clkspec.np == node && !clk_supplier) { + rc = 0; + goto err; + } + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + pr_warn("clk: couldn't get parent clock %d for %s\n", + index, node->full_name); + rc = PTR_ERR(clk); + goto err; + } + + rc = clk_set_parent(clk, pclk); + if (rc < 0) + pr_err("clk: failed to reparent %s to %s: %d\n", + __clk_get_name(clk), __clk_get_name(pclk), rc); + clk_put(clk); + clk_put(pclk); + } + return 0; +err: + clk_put(pclk); + return rc; +} + +static int __set_clk_rates(struct device_node *node, bool clk_supplier) +{ + struct of_phandle_args clkspec; + struct property *prop; + const __be32 *cur; + int rc, index = 0; + struct clk *clk; + u32 rate; + + of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) { + if (rate) { + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return rc; + } + if (clkspec.np == node && !clk_supplier) + return 0; + + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + pr_warn("clk: couldn't get clock %d for %s\n", + index, node->full_name); + return PTR_ERR(clk); + } + + rc = clk_set_rate(clk, rate); + if (rc < 0) + pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n", + __clk_get_name(clk), rate, rc, + clk_get_rate(clk)); + clk_put(clk); + } + index++; + } + return 0; +} + +/** + * of_clk_set_defaults() - parse and set assigned clocks configuration + * @node: device node to apply clock settings for + * @clk_supplier: true if clocks supplied by @node should also be considered + * + * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties + * and sets any specified clock parents and rates. The @clk_supplier argument + * should be set to true if @node may be also a clock supplier of any clock + * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. + * If @clk_supplier is false the function exits returning 0 as soon as it + * determines the @node is also a supplier of any of the clocks. + */ +int of_clk_set_defaults(struct device_node *node, bool clk_supplier) +{ + int rc; + + if (!node) + return 0; + + rc = __set_clk_parents(node, clk_supplier); + if (rc < 0) + return rc; + + return __set_clk_rates(node, clk_supplier); +} +EXPORT_SYMBOL_GPL(of_clk_set_defaults); + +#endif diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 19377b893c..1566beabda 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include <complete.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/clk/clk-conf.h> static LIST_HEAD(clks); @@ -527,6 +528,7 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) if (force || parent_ready(clk_provider->np)) { clk_provider->clk_init_cb(clk_provider->np); + of_clk_set_defaults(clk_provider->np, true); list_del(&clk_provider->node); free(clk_provider); diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile new file mode 100644 index 0000000000..65d7859ed3 --- /dev/null +++ b/drivers/clk/imx/Makefile @@ -0,0 +1,22 @@ +obj-$(CONFIG_COMMON_CLK) += \ + clk-pllv1.o \ + clk-pllv2.o \ + clk-pllv3.o \ + clk-pfd.o \ + clk-gate2.o \ + clk-gate-exclusive.o \ + clk.o + +obj-$(CONFIG_ARCH_IMX1) += clk-imx1.o +obj-$(CONFIG_ARCH_IMX25) += clk-imx25.o +obj-$(CONFIG_ARCH_IMX21) += clk-imx21.o +obj-$(CONFIG_ARCH_IMX27) += clk-imx27.o +obj-$(CONFIG_ARCH_IMX31) += clk-imx31.o +obj-$(CONFIG_ARCH_IMX35) += clk-imx35.o +obj-$(CONFIG_ARCH_IMX50) += clk-imx5.o +obj-$(CONFIG_ARCH_IMX51) += clk-imx5.o +obj-$(CONFIG_ARCH_IMX53) += clk-imx5.o +obj-$(CONFIG_ARCH_IMX6) += clk-imx6.o +obj-$(CONFIG_ARCH_IMX6SX) += clk-imx6sx.o +obj-$(CONFIG_ARCH_IMX6UL) += clk-imx6ul.o +obj-$(CONFIG_ARCH_VF610) += clk-vf610.o diff --git a/arch/arm/mach-imx/clk-gate-exclusive.c b/drivers/clk/imx/clk-gate-exclusive.c index db88db0237..db88db0237 100644 --- a/arch/arm/mach-imx/clk-gate-exclusive.c +++ b/drivers/clk/imx/clk-gate-exclusive.c diff --git a/arch/arm/mach-imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index faed6313fb..f952f3e3f6 100644 --- a/arch/arm/mach-imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -26,6 +26,7 @@ struct clk_gate2 { struct clk clk; void __iomem *reg; int shift; + u8 cgr_val; const char *parent; #define CLK_GATE_INVERTED (1 << 0) unsigned flags; @@ -43,7 +44,7 @@ static int clk_gate2_enable(struct clk *clk) if (g->flags & CLK_GATE_INVERTED) val &= ~(3 << g->shift); else - val |= 3 << g->shift; + val |= g->cgr_val << g->shift; writel(val, g->reg); @@ -87,12 +88,13 @@ static struct clk_ops clk_gate2_ops = { }; struct clk *clk_gate2_alloc(const char *name, const char *parent, - void __iomem *reg, u8 shift) + void __iomem *reg, u8 shift, u8 cgr_val) { struct clk_gate2 *g = xzalloc(sizeof(*g)); g->parent = parent; g->reg = reg; + g->cgr_val = cgr_val; g->shift = shift; g->clk.ops = &clk_gate2_ops; g->clk.name = name; @@ -111,12 +113,12 @@ void clk_gate2_free(struct clk *clk) } struct clk *clk_gate2(const char *name, const char *parent, void __iomem *reg, - u8 shift) + u8 shift, u8 cgr_val) { struct clk *g; int ret; - g = clk_gate2_alloc(name , parent, reg, shift); + g = clk_gate2_alloc(name , parent, reg, shift, cgr_val); ret = clk_register(g); if (ret) { @@ -133,7 +135,7 @@ struct clk *clk_gate2_inverted(const char *name, const char *parent, struct clk *clk; struct clk_gate2 *g; - clk = clk_gate2(name, parent, reg, shift); + clk = clk_gate2(name, parent, reg, shift, 0x3); if (IS_ERR(clk)) return clk; diff --git a/arch/arm/mach-imx/clk-imx1.c b/drivers/clk/imx/clk-imx1.c index 5f600a9da3..5f600a9da3 100644 --- a/arch/arm/mach-imx/clk-imx1.c +++ b/drivers/clk/imx/clk-imx1.c diff --git a/arch/arm/mach-imx/clk-imx21.c b/drivers/clk/imx/clk-imx21.c index 546461b8ee..546461b8ee 100644 --- a/arch/arm/mach-imx/clk-imx21.c +++ b/drivers/clk/imx/clk-imx21.c diff --git a/arch/arm/mach-imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c index 864d06e195..864d06e195 100644 --- a/arch/arm/mach-imx/clk-imx25.c +++ b/drivers/clk/imx/clk-imx25.c diff --git a/arch/arm/mach-imx/clk-imx27.c b/drivers/clk/imx/clk-imx27.c index 4b63244211..4b63244211 100644 --- a/arch/arm/mach-imx/clk-imx27.c +++ b/drivers/clk/imx/clk-imx27.c diff --git a/arch/arm/mach-imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c index 8d135c9a1f..8d135c9a1f 100644 --- a/arch/arm/mach-imx/clk-imx31.c +++ b/drivers/clk/imx/clk-imx31.c diff --git a/arch/arm/mach-imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index af6c4058d7..af6c4058d7 100644 --- a/arch/arm/mach-imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c diff --git a/arch/arm/mach-imx/clk-imx5.c b/drivers/clk/imx/clk-imx5.c index c4c47a6d87..c4c47a6d87 100644 --- a/arch/arm/mach-imx/clk-imx5.c +++ b/drivers/clk/imx/clk-imx5.c diff --git a/arch/arm/mach-imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c index 8ac43bebb0..8ac43bebb0 100644 --- a/arch/arm/mach-imx/clk-imx6.c +++ b/drivers/clk/imx/clk-imx6.c diff --git a/arch/arm/mach-imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index d758957d4d..d758957d4d 100644 --- a/arch/arm/mach-imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c diff --git a/arch/arm/mach-imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index 72b5fa2b75..72b5fa2b75 100644 --- a/arch/arm/mach-imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c diff --git a/arch/arm/mach-imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c index 8f6d5ad7a8..8f6d5ad7a8 100644 --- a/arch/arm/mach-imx/clk-pfd.c +++ b/drivers/clk/imx/clk-pfd.c diff --git a/arch/arm/mach-imx/clk-pllv1.c b/drivers/clk/imx/clk-pllv1.c index f992134f7e..f992134f7e 100644 --- a/arch/arm/mach-imx/clk-pllv1.c +++ b/drivers/clk/imx/clk-pllv1.c diff --git a/arch/arm/mach-imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c index 5ba07fa5e6..5ba07fa5e6 100644 --- a/arch/arm/mach-imx/clk-pllv2.c +++ b/drivers/clk/imx/clk-pllv2.c diff --git a/arch/arm/mach-imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index e38dcdfdaa..29c0f1c700 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -36,6 +36,7 @@ struct clk_pllv3 { void __iomem *base; bool powerup_set; u32 div_mask; + u32 div_shift; const char *parent; }; @@ -92,7 +93,7 @@ static unsigned long clk_pllv3_recalc_rate(struct clk *clk, unsigned long parent_rate) { struct clk_pllv3 *pll = to_clk_pllv3(clk); - u32 div = readl(pll->base) & pll->div_mask; + u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask; return (div == 1) ? parent_rate * 22 : parent_rate * 20; } @@ -120,8 +121,8 @@ static int clk_pllv3_set_rate(struct clk *clk, unsigned long rate, return -EINVAL; val = readl(pll->base); - val &= ~pll->div_mask; - val |= div; + val &= ~(pll->div_mask << pll->div_shift); + val |= div << pll->div_shift; writel(val, pll->base); return 0; @@ -292,6 +293,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, case IMX_PLLV3_SYS: ops = &clk_pllv3_sys_ops; break; + case IMX_PLLV3_USB_VF610: + pll->div_shift = 1; case IMX_PLLV3_USB: ops = &clk_pllv3_ops; pll->powerup_set = true; diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c new file mode 100644 index 0000000000..9b9ac6cb0a --- /dev/null +++ b/drivers/clk/imx/clk-vf610.c @@ -0,0 +1,446 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <io.h> +#include <of.h> +#include <of_address.h> +#include <linux/clkdev.h> +#include <linux/clk.h> +#include <dt-bindings/clock/vf610-clock.h> + +#include "clk.h" + +#define CCM_CCR (ccm_base + 0x00) +#define CCM_CSR (ccm_base + 0x04) +#define CCM_CCSR (ccm_base + 0x08) +#define CCSR_PLL1_PFDn_EN(n) BIT((n) + 7) +#define CCSR_PLL2_PFDn_EN(n) BIT((n) + 11) +#define CCSR_PLL3_PFDn_EN(n) BIT((n) + 27) +#define CCM_CACRR (ccm_base + 0x0c) +#define CCM_CSCMR1 (ccm_base + 0x10) +#define CCM_CSCDR1 (ccm_base + 0x14) +#define CCM_CSCDR2 (ccm_base + 0x18) +#define CCM_CSCDR3 (ccm_base + 0x1c) +#define CCM_CSCMR2 (ccm_base + 0x20) +#define CCM_CSCDR4 (ccm_base + 0x24) +#define CCM_CLPCR (ccm_base + 0x2c) +#define CCM_CISR (ccm_base + 0x30) +#define CCM_CIMR (ccm_base + 0x34) +#define CCM_CGPR (ccm_base + 0x3c) +#define CCM_CCGR0 (ccm_base + 0x40) +#define CCM_CCGR1 (ccm_base + 0x44) +#define CCM_CCGR2 (ccm_base + 0x48) +#define CCM_CCGR3 (ccm_base + 0x4c) +#define CCM_CCGR4 (ccm_base + 0x50) +#define CCM_CCGR5 (ccm_base + 0x54) +#define CCM_CCGR6 (ccm_base + 0x58) +#define CCM_CCGR7 (ccm_base + 0x5c) +#define CCM_CCGR8 (ccm_base + 0x60) +#define CCM_CCGR9 (ccm_base + 0x64) +#define CCM_CCGR10 (ccm_base + 0x68) +#define CCM_CCGR11 (ccm_base + 0x6c) +#define CCM_CCGRx(x) (CCM_CCGR0 + (x) * 4) +#define CCM_CMEOR0 (ccm_base + 0x70) +#define CCM_CMEOR1 (ccm_base + 0x74) +#define CCM_CMEOR2 (ccm_base + 0x78) +#define CCM_CMEOR3 (ccm_base + 0x7c) +#define CCM_CMEOR4 (ccm_base + 0x80) +#define CCM_CMEOR5 (ccm_base + 0x84) +#define CCM_CPPDSR (ccm_base + 0x88) +#define CCM_CCOWR (ccm_base + 0x8c) +#define CCM_CCPGR0 (ccm_base + 0x90) +#define CCM_CCPGR1 (ccm_base + 0x94) +#define CCM_CCPGR2 (ccm_base + 0x98) +#define CCM_CCPGR3 (ccm_base + 0x9c) + +#define CCM_CCGRx_CGn(n) ((n) * 2) + +#define PFD_PLL1_BASE (anatop_base + 0x2b0) +#define PFD_PLL2_BASE (anatop_base + 0x100) +#define PFD_PLL3_BASE (anatop_base + 0xf0) +#define PLL1_CTRL (anatop_base + 0x270) +#define PLL2_CTRL (anatop_base + 0x30) +#define PLL3_CTRL (anatop_base + 0x10) +#define PLL4_CTRL (anatop_base + 0x70) +#define PLL5_CTRL (anatop_base + 0xe0) +#define PLL6_CTRL (anatop_base + 0xa0) +#define PLL7_CTRL (anatop_base + 0x20) +#define ANA_MISC1 (anatop_base + 0x160) + +static void __iomem *anatop_base; +static void __iomem *ccm_base; + +/* sources for multiplexer clocks, this is used multiple times */ +static const char *fast_sels[] = { "firc", "fxosc", }; +static const char *slow_sels[] = { "sirc_32k", "sxosc", }; +static const char *pll1_sels[] = { "pll1_sys", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", }; +static const char *pll2_sels[] = { "pll2_bus", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", }; +static const char *pll_bypass_src_sels[] = { "fast_clk_sel", "lvds1_in", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; +static const char *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_bus", "pll1_pfd_sel", "pll3_usb_otg", }; +static const char *ddr_sels[] = { "pll2_pfd2", "sys_sel", }; +static const char *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", }; +static const char *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", }; +static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", }; +static const char *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", }; +static const char *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", }; +static const char *qspi_sels[] = { "pll3_usb_otg", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", }; +static const char *esdhc_sels[] = { "pll3_usb_otg", "pll3_pfd3", "pll1_pfd3", "platform_bus", }; +static const char *dcu_sels[] = { "pll1_pfd2", "pll3_usb_otg", }; +static const char *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", }; +static const char *vadc_sels[] = { "pll6_video_div", "pll3_usb_otg_div", "pll3_usb_otg", }; +/* FTM counter clock source, not module clock */ +static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", }; +static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", }; + + +static struct clk_div_table pll4_audio_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 6 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 10 }, + { .val = 5, .div = 12 }, + { .val = 6, .div = 14 }, + { .val = 7, .div = 16 }, + { } +}; + +static struct clk *clk[VF610_CLK_END]; +static struct clk_onecell_data clk_data; + +static unsigned int const clks_init_on[] __initconst = { + VF610_CLK_SYS_BUS, + VF610_CLK_DDR_SEL, + VF610_CLK_DAP, + VF610_CLK_DDRMC, + VF610_CLK_WKPU, +}; + +static void __init vf610_clocks_init(struct device_node *ccm_node) +{ + u32 ccsr; + struct device_node *np; + int i; + + clk[VF610_CLK_DUMMY] = clk_fixed("dummy", 0); + clk[VF610_CLK_SIRC_128K] = clk_fixed("sirc_128k", 128000); + clk[VF610_CLK_SIRC_32K] = clk_fixed("sirc_32k", 32000); + clk[VF610_CLK_FIRC] = clk_fixed("firc", 24000000); + + clk[VF610_CLK_SXOSC] = of_clk_get_by_name(ccm_node, "sxosc"); + clk[VF610_CLK_FXOSC] = of_clk_get_by_name(ccm_node, "fxosc"); + clk[VF610_CLK_AUDIO_EXT] = of_clk_get_by_name(ccm_node, "audio_ext"); + clk[VF610_CLK_ENET_EXT] = of_clk_get_by_name(ccm_node, "enet_ext"); + + /* Clock source from external clock via LVDs PAD */ + clk[VF610_CLK_ANACLK1] = of_clk_get_by_name(ccm_node, "anaclk1"); + + clk[VF610_CLK_FXOSC_HALF] = imx_clk_fixed_factor("fxosc_half", "fxosc", 1, 2); + + np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); + anatop_base = of_iomap(np, 0); + BUG_ON(!anatop_base); + + np = ccm_node; + ccm_base = of_iomap(np, 0); + BUG_ON(!ccm_base); + + ccsr = readl(CCM_CCSR); + ccsr |= CCSR_PLL3_PFDn_EN(1) | + CCSR_PLL3_PFDn_EN(2) | + CCSR_PLL3_PFDn_EN(3) | + CCSR_PLL3_PFDn_EN(4) | + CCSR_PLL2_PFDn_EN(1) | + CCSR_PLL2_PFDn_EN(2) | + CCSR_PLL2_PFDn_EN(3) | + CCSR_PLL2_PFDn_EN(4) | + CCSR_PLL1_PFDn_EN(1) | + CCSR_PLL1_PFDn_EN(2) | + CCSR_PLL1_PFDn_EN(3) | + CCSR_PLL1_PFDn_EN(4); + writel(ccsr, CCM_CCSR); + + clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels)); + clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels)); + + clk[VF610_CLK_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", PLL1_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", PLL2_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL3_BYPASS_SRC] = imx_clk_mux("pll3_bypass_src", PLL3_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL4_BYPASS_SRC] = imx_clk_mux("pll4_bypass_src", PLL4_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL5_BYPASS_SRC] = imx_clk_mux("pll5_bypass_src", PLL5_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1); + clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1); + clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2); + clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f); + clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3); + clk[VF610_CLK_PLL6] = imx_clk_pllv3(IMX_PLLV3_AV, "pll6", "pll6_bypass_src", PLL6_CTRL, 0x7f); + clk[VF610_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll7", "pll7_bypass_src", PLL7_CTRL, 0x2); + + clk[VF610_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", PLL1_CTRL, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", PLL2_CTRL, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL3_BYPASS] = imx_clk_mux_flags("pll3_bypass", PLL3_CTRL, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL4_BYPASS] = imx_clk_mux_flags("pll4_bypass", PLL4_CTRL, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL5_BYPASS] = imx_clk_mux_flags("pll5_bypass", PLL5_CTRL, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL6_BYPASS] = imx_clk_mux_flags("pll6_bypass", PLL6_CTRL, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL7_BYPASS] = imx_clk_mux_flags("pll7_bypass", PLL7_CTRL, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + /* Do not bypass PLLs initially */ + clk_set_parent(clk[VF610_PLL1_BYPASS], clk[VF610_CLK_PLL1]); + clk_set_parent(clk[VF610_PLL2_BYPASS], clk[VF610_CLK_PLL2]); + clk_set_parent(clk[VF610_PLL3_BYPASS], clk[VF610_CLK_PLL3]); + clk_set_parent(clk[VF610_PLL4_BYPASS], clk[VF610_CLK_PLL4]); + clk_set_parent(clk[VF610_PLL5_BYPASS], clk[VF610_CLK_PLL5]); + clk_set_parent(clk[VF610_PLL6_BYPASS], clk[VF610_CLK_PLL6]); + clk_set_parent(clk[VF610_PLL7_BYPASS], clk[VF610_CLK_PLL7]); + + clk[VF610_CLK_PLL1_SYS] = imx_clk_gate("pll1_sys", "pll1_bypass", PLL1_CTRL, 13); + clk[VF610_CLK_PLL2_BUS] = imx_clk_gate("pll2_bus", "pll2_bypass", PLL2_CTRL, 13); + clk[VF610_CLK_PLL3_USB_OTG] = imx_clk_gate("pll3_usb_otg", "pll3_bypass", PLL3_CTRL, 13); + clk[VF610_CLK_PLL4_AUDIO] = imx_clk_gate("pll4_audio", "pll4_bypass", PLL4_CTRL, 13); + clk[VF610_CLK_PLL5_ENET] = imx_clk_gate("pll5_enet", "pll5_bypass", PLL5_CTRL, 13); + clk[VF610_CLK_PLL6_VIDEO] = imx_clk_gate("pll6_video", "pll6_bypass", PLL6_CTRL, 13); + clk[VF610_CLK_PLL7_USB_HOST] = imx_clk_gate("pll7_usb_host", "pll7_bypass", PLL7_CTRL, 13); + + clk[VF610_CLK_LVDS1_IN] = imx_clk_gate_exclusive("lvds1_in", "anaclk1", ANA_MISC1, 12, BIT(10)); + + clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_sys", PFD_PLL1_BASE, 0); + clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_sys", PFD_PLL1_BASE, 1); + clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_sys", PFD_PLL1_BASE, 2); + clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_sys", PFD_PLL1_BASE, 3); + + clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_bus", PFD_PLL2_BASE, 0); + clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_bus", PFD_PLL2_BASE, 1); + clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_bus", PFD_PLL2_BASE, 2); + clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_bus", PFD_PLL2_BASE, 3); + + clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_usb_otg", PFD_PLL3_BASE, 0); + clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", PFD_PLL3_BASE, 1); + clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", PFD_PLL3_BASE, 2); + clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_usb_otg", PFD_PLL3_BASE, 3); + + clk[VF610_CLK_PLL1_PFD_SEL] = imx_clk_mux("pll1_pfd_sel", CCM_CCSR, 16, 3, pll1_sels, 5); + clk[VF610_CLK_PLL2_PFD_SEL] = imx_clk_mux("pll2_pfd_sel", CCM_CCSR, 19, 3, pll2_sels, 5); + clk[VF610_CLK_SYS_SEL] = imx_clk_mux("sys_sel", CCM_CCSR, 0, 3, sys_sels, ARRAY_SIZE(sys_sels)); + clk[VF610_CLK_DDR_SEL] = imx_clk_mux("ddr_sel", CCM_CCSR, 6, 1, ddr_sels, ARRAY_SIZE(ddr_sels)); + clk[VF610_CLK_SYS_BUS] = imx_clk_divider("sys_bus", "sys_sel", CCM_CACRR, 0, 3); + clk[VF610_CLK_PLATFORM_BUS] = imx_clk_divider("platform_bus", "sys_bus", CCM_CACRR, 3, 3); + clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2); + + clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_usb_otg_div", "pll3_usb_otg", CCM_CACRR, 20, 1); + clk[VF610_CLK_PLL4_MAIN_DIV] = clk_divider_table("pll4_audio_div", "pll4_audio", CCM_CACRR, 6, 3, pll4_audio_div_table, 0); + clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1); + + clk[VF610_CLK_DDRMC] = imx_clk_gate2_cgr("ddrmc", "ddr_sel", CCM_CCGR6, CCM_CCGRx_CGn(14), 0x2); + clk[VF610_CLK_WKPU] = imx_clk_gate2_cgr("wkpu", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(10), 0x2); + + clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6); + clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6); + + clk[VF610_CLK_USBC0] = imx_clk_gate2("usbc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_USBC1] = imx_clk_gate2("usbc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI0_SEL] = imx_clk_mux("qspi0_sel", CCM_CSCMR1, 22, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI0_EN] = imx_clk_gate("qspi0_en", "qspi0_sel", CCM_CSCDR3, 4); + clk[VF610_CLK_QSPI0_X4_DIV] = imx_clk_divider("qspi0_x4", "qspi0_en", CCM_CSCDR3, 0, 2); + clk[VF610_CLK_QSPI0_X2_DIV] = imx_clk_divider("qspi0_x2", "qspi0_x4", CCM_CSCDR3, 2, 1); + clk[VF610_CLK_QSPI0_X1_DIV] = imx_clk_divider("qspi0_x1", "qspi0_x2", CCM_CSCDR3, 3, 1); + clk[VF610_CLK_QSPI0] = imx_clk_gate2("qspi0", "qspi0_x1", CCM_CCGR2, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", CCM_CSCMR1, 24, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI1_EN] = imx_clk_gate("qspi1_en", "qspi1_sel", CCM_CSCDR3, 12); + clk[VF610_CLK_QSPI1_X4_DIV] = imx_clk_divider("qspi1_x4", "qspi1_en", CCM_CSCDR3, 8, 2); + clk[VF610_CLK_QSPI1_X2_DIV] = imx_clk_divider("qspi1_x2", "qspi1_x4", CCM_CSCDR3, 10, 1); + clk[VF610_CLK_QSPI1_X1_DIV] = imx_clk_divider("qspi1_x1", "qspi1_x2", CCM_CSCDR3, 11, 1); + clk[VF610_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_x1", CCM_CCGR8, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_enet", 1, 10); + clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_enet", 1, 20); + clk[VF610_CLK_ENET_SEL] = imx_clk_mux("enet_sel", CCM_CSCMR2, 4, 2, rmii_sels, 4); + clk[VF610_CLK_ENET_TS_SEL] = imx_clk_mux("enet_ts_sel", CCM_CSCMR2, 0, 3, enet_ts_sels, 7); + clk[VF610_CLK_ENET] = imx_clk_gate("enet", "enet_sel", CCM_CSCDR1, 24); + clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23); + clk[VF610_CLK_ENET0] = imx_clk_gate2("enet0", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_ENET1] = imx_clk_gate2("enet1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_UART0] = imx_clk_gate2_cgr("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7), 0x2); + clk[VF610_CLK_UART1] = imx_clk_gate2_cgr("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8), 0x2); + clk[VF610_CLK_UART2] = imx_clk_gate2_cgr("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9), 0x2); + clk[VF610_CLK_UART3] = imx_clk_gate2_cgr("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10), 0x2); + clk[VF610_CLK_UART4] = imx_clk_gate2_cgr("uart4", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(9), 0x2); + clk[VF610_CLK_UART5] = imx_clk_gate2_cgr("uart5", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(10), 0x2); + + clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6)); + clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7)); + clk[VF610_CLK_I2C2] = imx_clk_gate2("i2c2", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(6)); + clk[VF610_CLK_I2C3] = imx_clk_gate2("i2c3", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_DSPI0] = imx_clk_gate2("dspi0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI1] = imx_clk_gate2("dspi1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_DSPI2] = imx_clk_gate2("dspi2", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI3] = imx_clk_gate2("dspi3", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_WDT] = imx_clk_gate2("wdt", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(14)); + + clk[VF610_CLK_ESDHC0_SEL] = imx_clk_mux("esdhc0_sel", CCM_CSCMR1, 16, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC0_EN] = imx_clk_gate("esdhc0_en", "esdhc0_sel", CCM_CSCDR2, 28); + clk[VF610_CLK_ESDHC0_DIV] = imx_clk_divider("esdhc0_div", "esdhc0_en", CCM_CSCDR2, 16, 4); + clk[VF610_CLK_ESDHC0] = imx_clk_gate2("eshc0", "esdhc0_div", CCM_CCGR7, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_ESDHC1_SEL] = imx_clk_mux("esdhc1_sel", CCM_CSCMR1, 18, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC1_EN] = imx_clk_gate("esdhc1_en", "esdhc1_sel", CCM_CSCDR2, 29); + clk[VF610_CLK_ESDHC1_DIV] = imx_clk_divider("esdhc1_div", "esdhc1_en", CCM_CSCDR2, 20, 4); + clk[VF610_CLK_ESDHC1] = imx_clk_gate2("eshc1", "esdhc1_div", CCM_CCGR7, CCM_CCGRx_CGn(2)); + + /* + * ftm_ext_clk and ftm_fix_clk are FTM timer counter's + * selectable clock sources, both use a common enable bit + * in CCM_CSCDR1, selecting "dummy" clock as parent of + * "ftm0_ext_fix" make it serve only for enable/disable. + */ + clk[VF610_CLK_FTM0_EXT_SEL] = imx_clk_mux("ftm0_ext_sel", CCM_CSCMR2, 6, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM0_FIX_SEL] = imx_clk_mux("ftm0_fix_sel", CCM_CSCMR2, 14, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM0_EXT_FIX_EN] = imx_clk_gate("ftm0_ext_fix_en", "dummy", CCM_CSCDR1, 25); + clk[VF610_CLK_FTM1_EXT_SEL] = imx_clk_mux("ftm1_ext_sel", CCM_CSCMR2, 8, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM1_FIX_SEL] = imx_clk_mux("ftm1_fix_sel", CCM_CSCMR2, 15, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM1_EXT_FIX_EN] = imx_clk_gate("ftm1_ext_fix_en", "dummy", CCM_CSCDR1, 26); + clk[VF610_CLK_FTM2_EXT_SEL] = imx_clk_mux("ftm2_ext_sel", CCM_CSCMR2, 10, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM2_FIX_SEL] = imx_clk_mux("ftm2_fix_sel", CCM_CSCMR2, 16, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM2_EXT_FIX_EN] = imx_clk_gate("ftm2_ext_fix_en", "dummy", CCM_CSCDR1, 27); + clk[VF610_CLK_FTM3_EXT_SEL] = imx_clk_mux("ftm3_ext_sel", CCM_CSCMR2, 12, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM3_FIX_SEL] = imx_clk_mux("ftm3_fix_sel", CCM_CSCMR2, 17, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM3_EXT_FIX_EN] = imx_clk_gate("ftm3_ext_fix_en", "dummy", CCM_CSCDR1, 28); + + /* ftm(n)_clk are FTM module operation clock */ + clk[VF610_CLK_FTM0] = imx_clk_gate2("ftm0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM1] = imx_clk_gate2("ftm1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(9)); + clk[VF610_CLK_FTM2] = imx_clk_gate2("ftm2", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM3] = imx_clk_gate2("ftm3", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(9)); + + clk[VF610_CLK_DCU0_SEL] = imx_clk_mux("dcu0_sel", CCM_CSCMR1, 28, 1, dcu_sels, 2); + clk[VF610_CLK_DCU0_EN] = imx_clk_gate("dcu0_en", "dcu0_sel", CCM_CSCDR3, 19); + clk[VF610_CLK_DCU0_DIV] = imx_clk_divider("dcu0_div", "dcu0_en", CCM_CSCDR3, 16, 3); + clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "ipg_bus", CCM_CCGR3, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_DCU1_SEL] = imx_clk_mux("dcu1_sel", CCM_CSCMR1, 29, 1, dcu_sels, 2); + clk[VF610_CLK_DCU1_EN] = imx_clk_gate("dcu1_en", "dcu1_sel", CCM_CSCDR3, 23); + clk[VF610_CLK_DCU1_DIV] = imx_clk_divider("dcu1_div", "dcu1_en", CCM_CSCDR3, 20, 3); + clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(8)); + + clk[VF610_CLK_TCON0] = imx_clk_gate2("tcon0", "platform_bus", CCM_CCGR1, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_TCON1] = imx_clk_gate2("tcon1", "platform_bus", CCM_CCGR7, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", CCM_CSCMR1, 20, 2, esai_sels, 4); + clk[VF610_CLK_ESAI_EN] = imx_clk_gate("esai_en", "esai_sel", CCM_CSCDR2, 30); + clk[VF610_CLK_ESAI_DIV] = imx_clk_divider("esai_div", "esai_en", CCM_CSCDR2, 24, 4); + clk[VF610_CLK_ESAI] = imx_clk_gate2("esai", "esai_div", CCM_CCGR4, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_SAI0_SEL] = imx_clk_mux("sai0_sel", CCM_CSCMR1, 0, 2, sai_sels, 4); + clk[VF610_CLK_SAI0_EN] = imx_clk_gate("sai0_en", "sai0_sel", CCM_CSCDR1, 16); + clk[VF610_CLK_SAI0_DIV] = imx_clk_divider("sai0_div", "sai0_en", CCM_CSCDR1, 0, 4); + clk[VF610_CLK_SAI0] = imx_clk_gate2("sai0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_SAI1_SEL] = imx_clk_mux("sai1_sel", CCM_CSCMR1, 2, 2, sai_sels, 4); + clk[VF610_CLK_SAI1_EN] = imx_clk_gate("sai1_en", "sai1_sel", CCM_CSCDR1, 17); + clk[VF610_CLK_SAI1_DIV] = imx_clk_divider("sai1_div", "sai1_en", CCM_CSCDR1, 4, 4); + clk[VF610_CLK_SAI1] = imx_clk_gate2("sai1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_SAI2_SEL] = imx_clk_mux("sai2_sel", CCM_CSCMR1, 4, 2, sai_sels, 4); + clk[VF610_CLK_SAI2_EN] = imx_clk_gate("sai2_en", "sai2_sel", CCM_CSCDR1, 18); + clk[VF610_CLK_SAI2_DIV] = imx_clk_divider("sai2_div", "sai2_en", CCM_CSCDR1, 8, 4); + clk[VF610_CLK_SAI2] = imx_clk_gate2("sai2", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_SAI3_SEL] = imx_clk_mux("sai3_sel", CCM_CSCMR1, 6, 2, sai_sels, 4); + clk[VF610_CLK_SAI3_EN] = imx_clk_gate("sai3_en", "sai3_sel", CCM_CSCDR1, 19); + clk[VF610_CLK_SAI3_DIV] = imx_clk_divider("sai3_div", "sai3_en", CCM_CSCDR1, 12, 4); + clk[VF610_CLK_SAI3] = imx_clk_gate2("sai3", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_NFC_SEL] = imx_clk_mux("nfc_sel", CCM_CSCMR1, 12, 2, nfc_sels, 4); + clk[VF610_CLK_NFC_EN] = imx_clk_gate("nfc_en", "nfc_sel", CCM_CSCDR2, 9); + clk[VF610_CLK_NFC_PRE_DIV] = imx_clk_divider("nfc_pre_div", "nfc_en", CCM_CSCDR3, 13, 3); + clk[VF610_CLK_NFC_FRAC_DIV] = imx_clk_divider("nfc_frac_div", "nfc_pre_div", CCM_CSCDR2, 4, 4); + clk[VF610_CLK_NFC] = imx_clk_gate2("nfc", "nfc_frac_div", CCM_CCGR10, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_GPU_SEL] = imx_clk_mux("gpu_sel", CCM_CSCMR1, 14, 1, gpu_sels, 2); + clk[VF610_CLK_GPU_EN] = imx_clk_gate("gpu_en", "gpu_sel", CCM_CSCDR2, 10); + clk[VF610_CLK_GPU2D] = imx_clk_gate2("gpu", "gpu_en", CCM_CCGR8, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_VADC_SEL] = imx_clk_mux("vadc_sel", CCM_CSCMR1, 8, 2, vadc_sels, 3); + clk[VF610_CLK_VADC_EN] = imx_clk_gate("vadc_en", "vadc_sel", CCM_CSCDR1, 22); + clk[VF610_CLK_VADC_DIV] = imx_clk_divider("vadc_div", "vadc_en", CCM_CSCDR1, 20, 2); + clk[VF610_CLK_VADC_DIV_HALF] = imx_clk_fixed_factor("vadc_div_half", "vadc_div", 1, 2); + clk[VF610_CLK_VADC] = imx_clk_gate2("vadc", "vadc_div", CCM_CCGR8, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_ADC0] = imx_clk_gate2("adc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_ADC1] = imx_clk_gate2("adc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_DAC0] = imx_clk_gate2("dac0", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DAC1] = imx_clk_gate2("dac1", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_ASRC] = imx_clk_gate2("asrc", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_FLEXCAN0_EN] = imx_clk_gate("flexcan0_en", "ipg_bus", CCM_CSCDR2, 11); + clk[VF610_CLK_FLEXCAN0] = imx_clk_gate2("flexcan0", "flexcan0_en", CCM_CCGR0, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_FLEXCAN1_EN] = imx_clk_gate("flexcan1_en", "ipg_bus", CCM_CSCDR2, 12); + clk[VF610_CLK_FLEXCAN1] = imx_clk_gate2("flexcan1", "flexcan1_en", CCM_CCGR9, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_DMAMUX0] = imx_clk_gate2("dmamux0", "platform_bus", CCM_CCGR0, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_DMAMUX1] = imx_clk_gate2("dmamux1", "platform_bus", CCM_CCGR0, CCM_CCGRx_CGn(5)); + clk[VF610_CLK_DMAMUX2] = imx_clk_gate2("dmamux2", "platform_bus", CCM_CCGR6, CCM_CCGRx_CGn(1)); + clk[VF610_CLK_DMAMUX3] = imx_clk_gate2("dmamux3", "platform_bus", CCM_CCGR6, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_SNVS] = imx_clk_gate2("snvs-rtc", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(7)); + clk[VF610_CLK_DAP] = imx_clk_gate("dap", "platform_bus", CCM_CCSR, 24); + clk[VF610_CLK_OCOTP] = imx_clk_gate("ocotp", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(5)); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_set_parent(clk[VF610_CLK_QSPI0_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI0_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_QSPI1_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI1_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_ESDHC1_SEL], clk[VF610_CLK_PLL1_PFD3]); + clk_set_rate(clk[VF610_CLK_ESDHC1_DIV], clk_get_rate(clk[VF610_CLK_PLL1_PFD3]) / 9); + + clk_set_parent(clk[VF610_CLK_ESDHC0_SEL], clk[VF610_CLK_PLL1_PFD3]); + clk_set_rate(clk[VF610_CLK_ESDHC0_DIV], clk_get_rate(clk[VF610_CLK_PLL1_PFD3]) / 9); + + clk_set_parent(clk[VF610_CLK_SAI0_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI1_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI2_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI3_SEL], clk[VF610_CLK_AUDIO_EXT]); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_enable(clk[clks_init_on[i]]); + + /* Add the clocks to provider list */ + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(vf610, "fsl,vf610-ccm", vf610_clocks_init); diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c new file mode 100644 index 0000000000..7488dfcdc8 --- /dev/null +++ b/drivers/clk/imx/clk.c @@ -0,0 +1,21 @@ +#include <common.h> +#include <init.h> +#include <driver.h> +#include <linux/clk.h> +#include <io.h> +#include <of.h> +#include <linux/clkdev.h> +#include <linux/err.h> + +#include "clk.h" + +void __init imx_check_clocks(struct clk *clks[], unsigned int count) +{ + unsigned i; + + for (i = 0; i < count; i++) + if (IS_ERR(clks[i])) + pr_err("i.MX clk %u: register failed with %ld\n", + i, PTR_ERR(clks[i])); +} + diff --git a/arch/arm/mach-imx/clk.h b/drivers/clk/imx/clk.h index c5913e1879..970f65c7d1 100644 --- a/arch/arm/mach-imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -2,7 +2,7 @@ #define __IMX_CLK_H struct clk *clk_gate2(const char *name, const char *parent, void __iomem *reg, - u8 shift); + u8 shift, u8 cgr_val); static inline struct clk *imx_clk_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width) @@ -30,6 +30,14 @@ static inline struct clk *imx_clk_fixed_factor(const char *name, return clk_fixed_factor(name, parent, mult, div, CLK_SET_RATE_PARENT); } +static inline struct clk *imx_clk_mux_flags(const char *name, void __iomem *reg, + u8 shift, u8 width, + const char **parents, u8 num_parents, + unsigned long flags) +{ + return clk_mux(name, reg, shift, width, parents, num_parents, flags); +} + static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parents, u8 num_parents) { @@ -51,9 +59,16 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent, static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { - return clk_gate2(name, parent, reg, shift); + return clk_gate2(name, parent, reg, shift, 0x3); } +static inline struct clk *imx_clk_gate2_cgr(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 cgr_val) +{ + return clk_gate2(name, parent, reg, shift, cgr_val); +} + + struct clk *imx_clk_pllv1(const char *name, const char *parent, void __iomem *base); @@ -64,6 +79,7 @@ enum imx_pllv3_type { IMX_PLLV3_GENERIC, IMX_PLLV3_SYS, IMX_PLLV3_USB, + IMX_PLLV3_USB_VF610, IMX_PLLV3_AV, IMX_PLLV3_ENET, IMX_PLLV3_MLB, @@ -101,4 +117,7 @@ static inline struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, struct clk *imx_clk_gate_exclusive(const char *name, const char *parent, void __iomem *reg, u8 shift, u32 exclusive_mask); +void imx_check_clocks(struct clk *clks[], unsigned int count); + + #endif /* __IMX_CLK_H */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 078deee4e5..434c5688b4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -49,6 +49,9 @@ config GPIO_GENERIC_PLATFORM config GPIO_IMX def_bool ARCH_IMX +config GPIO_VF610 + def_bool ARCH_VF610 + config GPIO_MXS def_bool ARCH_MXS diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2d5142d41c..f37dd08f1a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_DESIGNWARE) += gpio-dw.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o +obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c new file mode 100644 index 0000000000..7c8d1e4c98 --- /dev/null +++ b/drivers/gpio/gpio-vf610.c @@ -0,0 +1,166 @@ +/* + * vf610 GPIO support through PORT and GPIO module + * + * Copyright (c) 2014 Toradex AG. + * + * Author: Stefan Agner <stefan@agner.ch>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <errno.h> +#include <io.h> +#include <of.h> +#include <gpio.h> +#include <init.h> +#include <pinctrl.h> + +#define VF610_GPIO_PER_PORT 32 +#define PINCTRL_BASE 2 +#define COUNT 3 + +struct vf610_gpio_port { + struct gpio_chip chip; + void __iomem *gpio_base; + unsigned int pinctrl_base; +}; + +#define GPIO_PDOR 0x00 +#define GPIO_PSOR 0x04 +#define GPIO_PCOR 0x08 +#define GPIO_PTOR 0x0c +#define GPIO_PDIR 0x10 + +static const struct of_device_id vf610_gpio_dt_ids[] = { + { .compatible = "fsl,vf610-gpio" }, + { /* sentinel */ } +}; + + +static int vf610_gpio_get_value(struct gpio_chip *chip, unsigned int gpio) +{ + struct vf610_gpio_port *port = + container_of(chip, struct vf610_gpio_port, chip); + + return !!(readl(port->gpio_base + GPIO_PDIR) & BIT(gpio)); +} + +static void vf610_gpio_set_value(struct gpio_chip *chip, + unsigned int gpio, int val) +{ + struct vf610_gpio_port *port = + container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + + writel(mask, port->gpio_base + ((val) ? GPIO_PSOR : GPIO_PCOR)); +} + +static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct vf610_gpio_port *port = + container_of(chip, struct vf610_gpio_port, chip); + + return pinctrl_gpio_direction_input(port->pinctrl_base + gpio); +} + +static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct vf610_gpio_port *port = + container_of(chip, struct vf610_gpio_port, chip); + + vf610_gpio_set_value(chip, gpio, value); + + return pinctrl_gpio_direction_output(port->pinctrl_base + gpio); +} + +static int vf610_gpio_get_direction(struct gpio_chip *chip, unsigned gpio) +{ + struct vf610_gpio_port *port = + container_of(chip, struct vf610_gpio_port, chip); + + return pinctrl_gpio_get_direction(port->pinctrl_base + gpio); +} + +static struct gpio_ops vf610_gpio_ops = { + .direction_input = vf610_gpio_direction_input, + .direction_output = vf610_gpio_direction_output, + .get = vf610_gpio_get_value, + .set = vf610_gpio_set_value, + .get_direction = vf610_gpio_get_direction, +}; + +static int vf610_gpio_probe(struct device_d *dev) +{ + int ret, size; + struct resource *iores; + struct vf610_gpio_port *port; + const __be32 *gpio_ranges; + + port = xzalloc(sizeof(*port)); + if (!port) + return -ENOMEM; + + gpio_ranges = of_get_property(dev->device_node, "gpio-ranges", &size); + if (!gpio_ranges) { + dev_err(dev, "Couldn't read 'gpio-ranges' propery of %s\n", + dev->device_node->full_name); + ret = -EINVAL; + goto free_port; + } + + port->pinctrl_base = be32_to_cpu(gpio_ranges[PINCTRL_BASE]); + port->chip.ngpio = be32_to_cpu(gpio_ranges[COUNT]); + + iores = dev_request_mem_resource(dev, 1); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + dev_dbg(dev, "Failed to request memory resource\n"); + goto free_port; + } + + port->gpio_base = IOMEM(iores->start); + + port->chip.ops = &vf610_gpio_ops; + if (dev->id < 0) { + port->chip.base = of_alias_get_id(dev->device_node, "gpio"); + if (port->chip.base < 0) { + ret = port->chip.base; + dev_dbg(dev, "Failed to get GPIO alias\n"); + goto free_port; + } + } else { + port->chip.base = dev->id; + } + + + port->chip.base *= VF610_GPIO_PER_PORT; + port->chip.dev = dev; + gpiochip_add(&port->chip); + + return 0; + +free_port: + free(port); + return ret; +} + +static struct driver_d vf610_gpio_driver = { + .name = "gpio-vf610", + .probe = vf610_gpio_probe, + .of_compatible = DRV_OF_COMPAT(vf610_gpio_dt_ids), +}; + +static int __init gpio_vf610_init(void) +{ + return platform_driver_register(&vf610_gpio_driver); +} +core_initcall(gpio_vf610_init); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index e407896394..74f046d00c 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -43,6 +43,7 @@ #include <linux/err.h> #include <pinctrl.h> #include <of_gpio.h> +#include <of_device.h> #include <io.h> #include <i2c/i2c.h> @@ -54,14 +55,25 @@ /* Default value */ #define FSL_I2C_BIT_RATE 100000 /* 100kHz */ -/* FSL I2C registers */ +/* IMX I2C registers: + * the I2C register offset is different between SoCs, + * to provid support for all these chips, split the + * register offset into a fixed base address and a + * variable shift value, then the full register offset + * will be calculated by + * reg_off = ( reg_base_addr << reg_shift) + */ #define FSL_I2C_IADR 0x00 /* i2c slave address */ -#define FSL_I2C_IFDR 0x04 /* i2c frequency divider */ -#define FSL_I2C_I2CR 0x08 /* i2c control */ -#define FSL_I2C_I2SR 0x0C /* i2c status */ -#define FSL_I2C_I2DR 0x10 /* i2c transfer data */ +#define FSL_I2C_IFDR 0x01 /* i2c frequency divider */ +#define FSL_I2C_I2CR 0x02 /* i2c control */ +#define FSL_I2C_I2SR 0x03 /* i2c status */ +#define FSL_I2C_I2DR 0x04 /* i2c transfer data */ #define FSL_I2C_DFSRR 0x14 /* i2c digital filter sampling rate */ +#define IMX_I2C_REGSHIFT 2 +#define VF610_I2C_REGSHIFT 0 + + /* Bits of FSL I2C registers */ #define I2SR_RXAK 0x01 #define I2SR_IIF 0x02 @@ -77,6 +89,22 @@ #define I2CR_IIEN 0x40 #define I2CR_IEN 0x80 +/* register bits different operating codes definition: + * 1) I2SR: Interrupt flags clear operation differ between SoCs: + * - write zero to clear(w0c) INT flag on i.MX, + * - but write one to clear(w1c) INT flag on Vybrid. + * 2) I2CR: I2C module enable operation also differ between SoCs: + * - set I2CR_IEN bit enable the module on i.MX, + * - but clear I2CR_IEN bit enable the module on Vybrid. + */ +#define I2SR_CLR_OPCODE_W0C 0x0 +#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF) +#define I2CR_IEN_OPCODE_0 0x0 +#define I2CR_IEN_OPCODE_1 I2CR_IEN + +#define I2C_PM_TIMEOUT 10 /* ms */ + + /* * sorted list of clock divider, register value pairs * taken from table 26-5, p.26-9, Freescale i.MX @@ -85,8 +113,12 @@ * * Duplicated divider values removed from list */ -#ifndef CONFIG_PPC -static u16 i2c_clk_div[50][2] = { +struct fsl_i2c_clk_pair { + u16 div; + u16 val; +}; + +static struct fsl_i2c_clk_pair imx_i2c_clk_div[] = { { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, @@ -101,7 +133,33 @@ static u16 i2c_clk_div[50][2] = { { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, { 3072, 0x1E }, { 3840, 0x1F } }; -#endif + +/* Vybrid VF610 clock divider, register value pairs */ +static struct fsl_i2c_clk_pair vf610_i2c_clk_div[] = { + { 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 }, + { 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 }, + { 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D }, + { 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 }, + { 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 }, + { 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 }, + { 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 }, + { 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 }, + { 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 }, + { 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B }, + { 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 }, + { 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 }, + { 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B }, + { 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A }, + { 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E }, +}; + +struct fsl_i2c_hwdata { + unsigned regshift; + struct fsl_i2c_clk_pair *clk_div; + unsigned ndivs; + unsigned i2sr_clr_opcode; + unsigned i2cr_ien_opcode; +}; struct fsl_i2c_struct { void __iomem *base; @@ -112,17 +170,33 @@ struct fsl_i2c_struct { unsigned int ifdr; /* FSL_I2C_IFDR */ unsigned int dfsrr; /* FSL_I2C_DFSRR */ struct i2c_bus_recovery_info rinfo; + const struct fsl_i2c_hwdata *hwdata; }; #define to_fsl_i2c_struct(a) container_of(a, struct fsl_i2c_struct, adapter) +static inline void fsl_i2c_write_reg(unsigned int val, + struct fsl_i2c_struct *i2c_fsl, + unsigned int reg) +{ + reg <<= i2c_fsl->hwdata->regshift; + writeb(val, i2c_fsl->base + reg); +} + +static inline unsigned char fsl_i2c_read_reg(struct fsl_i2c_struct *i2c_fsl, + unsigned int reg) +{ + reg <<= i2c_fsl->hwdata->regshift; + return readb(i2c_fsl->base + reg); +} + #ifdef CONFIG_I2C_DEBUG static void i2c_fsl_dump_reg(struct i2c_adapter *adapter) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); u32 reg_cr, reg_sr; - reg_cr = readb(i2c_fsl->base + FSL_I2C_I2CR); - reg_sr = readb(i2c_fsl->base + FSL_I2C_I2SR); + reg_cr = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); + reg_sr = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR); dev_dbg(adapter->dev, "CONTROL:\t" "IEN =%d, IIEN=%d, MSTA=%d, MTX =%d, TXAK=%d, RSTA=%d\n", @@ -144,16 +218,16 @@ static inline void i2c_fsl_dump_reg(struct i2c_adapter *adapter) } #endif + static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; uint64_t start; unsigned int temp; start = get_time_ns(); while (1) { - temp = readb(base + FSL_I2C_I2SR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR); if (for_busy && (temp & I2SR_IBB)) break; if (!for_busy && !(temp & I2SR_IBB)) @@ -172,12 +246,11 @@ static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy) static int i2c_fsl_trx_complete(struct i2c_adapter *adapter) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; uint64_t start; start = get_time_ns(); while (1) { - unsigned int reg = readb(base + FSL_I2C_I2SR); + unsigned int reg = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR); if (reg & I2SR_IIF) break; @@ -186,7 +259,9 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter) return -EIO; } } - writeb(0, base + FSL_I2C_I2SR); + + fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode, + i2c_fsl, FSL_I2C_I2SR); return 0; } @@ -194,12 +269,11 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter) static int i2c_fsl_acked(struct i2c_adapter *adapter) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; uint64_t start; start = get_time_ns(); while (1) { - unsigned int reg = readb(base + FSL_I2C_I2SR); + unsigned int reg = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR); if (!(reg & I2SR_RXAK)) break; @@ -215,25 +289,28 @@ static int i2c_fsl_acked(struct i2c_adapter *adapter) static int i2c_fsl_start(struct i2c_adapter *adapter) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; unsigned int temp = 0; int result; - writeb(i2c_fsl->ifdr, base + FSL_I2C_IFDR); + fsl_i2c_write_reg(i2c_fsl->ifdr, i2c_fsl, FSL_I2C_IFDR); +#ifdef CONFIG_PPC if (i2c_fsl->dfsrr != -1) - writeb(i2c_fsl->dfsrr, base + FSL_I2C_DFSRR); + fsl_i2c_write_reg(i2c_fsl->dfsrr, i2c_fsl, FSL_I2C_DFSRR); +#endif /* Enable I2C controller */ - writeb(0, base + FSL_I2C_I2SR); - writeb(I2CR_IEN, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode, + i2c_fsl, FSL_I2C_I2SR); + fsl_i2c_write_reg(i2c_fsl->hwdata->i2cr_ien_opcode, + i2c_fsl, FSL_I2C_I2CR); /* Wait controller to be stable */ udelay(100); /* Start I2C transaction */ - temp = readb(base + FSL_I2C_I2CR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); temp |= I2CR_MSTA; - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); result = i2c_fsl_bus_busy(adapter, 1); if (result) { @@ -246,7 +323,7 @@ static int i2c_fsl_start(struct i2c_adapter *adapter) i2c_fsl->stopped = 0; temp |= I2CR_MTX | I2CR_TXAK; - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); return result; } @@ -254,14 +331,13 @@ static int i2c_fsl_start(struct i2c_adapter *adapter) static void i2c_fsl_stop(struct i2c_adapter *adapter) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; unsigned int temp = 0; if (!i2c_fsl->stopped) { /* Stop I2C transaction */ - temp = readb(base + FSL_I2C_I2CR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); /* wait for the stop condition to be send, otherwise the i2c * controller is disabled before the STOP is sent completely */ i2c_fsl->stopped = i2c_fsl_bus_busy(adapter, 0) ? 0 : 1; @@ -273,14 +349,14 @@ static void i2c_fsl_stop(struct i2c_adapter *adapter) } /* Disable I2C controller, and force our state to stopped */ - writeb(0, base + FSL_I2C_I2CR); + temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); } #ifdef CONFIG_PPC static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, unsigned int rate) { - void __iomem *base; unsigned int i2c_clk; unsigned short divider; /* @@ -295,7 +371,6 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, unsigned long c_div, est_div; fdr = 0x31; /* Default if no FDR found */ - base = i2c_fsl->base; i2c_clk = fsl_get_i2c_freq(); divider = min((unsigned short)(i2c_clk / rate), (unsigned short) -1); @@ -349,6 +424,7 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, unsigned int rate) { + struct fsl_i2c_clk_pair *i2c_clk_div = i2c_fsl->hwdata->clk_div; unsigned int i2c_clk_rate; unsigned int div; int i; @@ -356,16 +432,16 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, /* Divider value calculation */ i2c_clk_rate = clk_get_rate(i2c_fsl->clk); div = (i2c_clk_rate + rate - 1) / rate; - if (div < i2c_clk_div[0][0]) + if (div < i2c_clk_div[0].div) i = 0; - else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) - i = ARRAY_SIZE(i2c_clk_div) - 1; + else if (div > i2c_clk_div[i2c_fsl->hwdata->ndivs - 1].div) + i = i2c_clk_div[i2c_fsl->hwdata->ndivs - 1].div - 1; else - for (i = 0; i2c_clk_div[i][0] < div; i++) + for (i = 0; i2c_clk_div[i].div < div; i++) ; /* Store divider value */ - i2c_fsl->ifdr = i2c_clk_div[i][1]; + i2c_fsl->ifdr = i2c_clk_div[i].val; /* * There dummy delay is calculated. @@ -374,20 +450,19 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, * to fix chip hardware bug. */ i2c_fsl->disable_delay = - (500000U * i2c_clk_div[i][0] + (i2c_clk_rate / 2) - 1) / + (500000U * i2c_clk_div[i].div + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); dev_dbg(&i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", __func__, i2c_clk_rate, div); dev_dbg(&i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", - __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]); + __func__, i2c_clk_div[i].val, i2c_clk_div[i].div); } #endif static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; int i, result; if ( !(msgs->flags & I2C_M_DATA_ONLY) ) { @@ -396,7 +471,7 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) __func__, msgs->addr << 1); /* write slave address */ - writeb(msgs->addr << 1, base + FSL_I2C_I2DR); + fsl_i2c_write_reg(msgs->addr << 1, i2c_fsl, FSL_I2C_I2DR); result = i2c_fsl_trx_complete(adapter); if (result) @@ -411,7 +486,7 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) dev_dbg(&adapter->dev, "<%s> write byte: B%d=0x%02X\n", __func__, i, msgs->buf[i]); - writeb(msgs->buf[i], base + FSL_I2C_I2DR); + fsl_i2c_write_reg(msgs->buf[i], i2c_fsl, FSL_I2C_I2DR); result = i2c_fsl_trx_complete(adapter); if (result) @@ -426,12 +501,12 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; int i, result; unsigned int temp; /* clear IIF */ - writeb(0x0, base + FSL_I2C_I2SR); + fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode, + i2c_fsl, FSL_I2C_I2SR); if ( !(msgs->flags & I2C_M_DATA_ONLY) ) { dev_dbg(&adapter->dev, @@ -439,7 +514,7 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) __func__, (msgs->addr << 1) | 0x01); /* write slave address */ - writeb((msgs->addr << 1) | 0x01, base + FSL_I2C_I2DR); + fsl_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_fsl, FSL_I2C_I2DR); result = i2c_fsl_trx_complete(adapter); if (result) @@ -450,13 +525,13 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) } /* setup bus to read data */ - temp = readb(base + FSL_I2C_I2CR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); temp &= ~I2CR_MTX; if (msgs->len - 1) temp &= ~I2CR_TXAK; - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); - readb(base + FSL_I2C_I2DR); /* dummy read */ + fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR); /* dummy read */ /* read data */ for (i = 0; i < msgs->len; i++) { @@ -469,9 +544,9 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) * It must generate STOP before read I2DR to prevent * controller from generating another clock cycle */ - temp = readb(base + FSL_I2C_I2CR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); /* * adding this delay helps on low bitrates @@ -481,11 +556,11 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) i2c_fsl_bus_busy(adapter, 0); i2c_fsl->stopped = 1; } else if (i == (msgs->len - 2)) { - temp = readb(base + FSL_I2C_I2CR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); temp |= I2CR_TXAK; - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); } - msgs->buf[i] = readb(base + FSL_I2C_I2DR); + msgs->buf[i] = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR); dev_dbg(&adapter->dev, "<%s> read byte: B%d=0x%02X\n", __func__, i, msgs->buf[i]); @@ -497,7 +572,6 @@ static int i2c_fsl_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); - void __iomem *base = i2c_fsl->base; unsigned int i, temp; int result; @@ -514,9 +588,9 @@ static int i2c_fsl_xfer(struct i2c_adapter *adapter, /* read/write data */ for (i = 0; i < num; i++) { if (i && !(msgs[i].flags & I2C_M_DATA_ONLY)) { - temp = readb(base + FSL_I2C_I2CR); + temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR); temp |= I2CR_RSTA; - writeb(temp, base + FSL_I2C_I2CR); + fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); result = i2c_fsl_bus_busy(adapter, 1); if (result) @@ -600,6 +674,13 @@ static int __init i2c_fsl_probe(struct device_d *pdev) goto fail; } #endif + + i2c_fsl->hwdata = of_device_get_match_data(pdev); + if (!i2c_fsl->hwdata) { + ret = -EINVAL; + goto fail; + } + /* Setup i2c_fsl driver structure */ i2c_fsl->adapter.master_xfer = i2c_fsl_xfer; i2c_fsl->adapter.nr = pdev->id; @@ -623,8 +704,9 @@ static int __init i2c_fsl_probe(struct device_d *pdev) i2c_fsl_set_clk(i2c_fsl, FSL_I2C_BIT_RATE); /* Set up chip registers to defaults */ - writeb(0, i2c_fsl->base + FSL_I2C_I2CR); - writeb(0, i2c_fsl->base + FSL_I2C_I2SR); + fsl_i2c_write_reg(i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + i2c_fsl, FSL_I2C_I2CR); + fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode, i2c_fsl, FSL_I2C_I2SR); /* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_fsl->adapter); @@ -640,17 +722,33 @@ fail: return ret; } +static const struct fsl_i2c_hwdata imx21_i2c_hwdata = { + .regshift = IMX_I2C_REGSHIFT, + .clk_div = imx_i2c_clk_div, + .ndivs = ARRAY_SIZE(imx_i2c_clk_div), + .i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C, + .i2cr_ien_opcode = I2CR_IEN_OPCODE_1, +}; + +static const struct fsl_i2c_hwdata vf610_i2c_hwdata = { + .regshift = VF610_I2C_REGSHIFT, + .clk_div = vf610_i2c_clk_div, + .ndivs = ARRAY_SIZE(vf610_i2c_clk_div), + .i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C, + .i2cr_ien_opcode = I2CR_IEN_OPCODE_0, +}; + static __maybe_unused struct of_device_id imx_i2c_dt_ids[] = { - { - .compatible = "fsl,imx21-i2c", - }, { - /* sentinel */ - } + { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, }, + { .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, }, + { /* sentinel */ } }; static struct driver_d i2c_fsl_driver = { .probe = i2c_fsl_probe, .name = DRIVER_NAME, +#ifndef CONFIG_PPC .of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids), +#endif }; coredevice_platform_driver(i2c_fsl_driver); diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index 41f0562e3c..951ac4501b 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -35,28 +35,81 @@ #include <mach/generic.h> #include <mach/esdhc.h> #include <gpio.h> +#include <of_device.h> #include "sdhci.h" #include "imx-esdhc.h" +/* + * The CMDTYPE of the CMD register (offset 0xE) should be set to + * "11" when the STOP CMD12 is issued on imx53 to abort one + * open ended multi-blk IO. Otherwise the TC INT wouldn't + * be generated. + * In exact block transfer, the controller doesn't complete the + * operations automatically as required at the end of the + * transfer and remains on hold if the abort command is not sent. + * As a result, the TC flag is not asserted and SW received timeout + * exeception. Bit1 of Vendor Spec registor is used to fix it. + */ +#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1) +/* + * The flag enables the workaround for ESDHC errata ENGcm07207 which + * affects i.MX25 and i.MX35. + */ +#define ESDHC_FLAG_ENGCM07207 BIT(2) +/* + * The flag tells that the ESDHC controller is an USDHC block that is + * integrated on the i.MX6 series. + */ +#define ESDHC_FLAG_USDHC BIT(3) +/* The IP supports manual tuning process */ +#define ESDHC_FLAG_MAN_TUNING BIT(4) +/* The IP supports standard tuning process */ +#define ESDHC_FLAG_STD_TUNING BIT(5) +/* The IP has SDHCI_CAPABILITIES_1 register */ +#define ESDHC_FLAG_HAVE_CAP1 BIT(6) +/* + * The IP has errata ERR004536 + * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow, + * when reading data from the card + */ +#define ESDHC_FLAG_ERR004536 BIT(7) +/* The IP supports HS200 mode */ +#define ESDHC_FLAG_HS200 BIT(8) +/* The IP supports HS400 mode */ +#define ESDHC_FLAG_HS400 BIT(9) + + #define IMX_SDHCI_WML 0x44 #define IMX_SDHCI_MIXCTRL 0x48 #define IMX_SDHCI_DLL_CTRL 0x60 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL (BIT(25)) +struct esdhc_soc_data { + u32 flags; +}; + struct fsl_esdhc_host { struct mci_host mci; void __iomem *regs; struct device_d *dev; struct clk *clk; + const struct esdhc_soc_data *socdata; }; #define to_fsl_esdhc(mci) container_of(mci, struct fsl_esdhc_host, mci) #define SDHCI_CMD_ABORTCMD (0xC0 << 16) +static inline int esdhc_is_usdhc(struct fsl_esdhc_host *data) +{ + return !!(data->socdata->flags & ESDHC_FLAG_USDHC); +} + + /* Return the XFERTYP flags for a given command and data packet */ -static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data) +static u32 esdhc_xfertyp(struct fsl_esdhc_host *host, + struct mci_cmd *cmd, struct mci_data *data) { u32 xfertyp = 0; @@ -85,8 +138,8 @@ static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data) xfertyp |= COMMAND_RSPTYP_48_BUSY; else if (cmd->resp_type & MMC_RSP_PRESENT) xfertyp |= COMMAND_RSPTYP_48; - if ((cpu_is_mx50() || cpu_is_mx51() || cpu_is_mx53()) && - cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + if ((host->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && + (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)) xfertyp |= SDHCI_CMD_ABORTCMD; return COMMAND_CMD(cmd->cmdidx) | xfertyp; @@ -273,12 +326,12 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) } /* Figure out the transfer arguments */ - xfertyp = esdhc_xfertyp(cmd, data); + xfertyp = esdhc_xfertyp(host, cmd, data); /* Send the command */ esdhc_write32(regs + SDHCI_ARGUMENT, cmd->cmdarg); - if (cpu_is_mx6()) { + if (esdhc_is_usdhc(host)) { /* write lower-half of xfertyp to mixctrl */ mixctrl = xfertyp & 0xFFFF; /* Keep the bits 22-25 of the register as is */ @@ -525,7 +578,7 @@ static int esdhc_reset(struct fsl_esdhc_host *host) SYSCTL_RSTA); /* extra register reset for i.MX6 Solo/DualLite */ - if (cpu_is_mx6()) { + if (esdhc_is_usdhc(host)) { /* reset bit FBCLK_SEL */ val = esdhc_read32(regs + IMX_SDHCI_MIXCTRL); val &= ~IMX_SDHCI_MIX_CTRL_FBCLK_SEL; @@ -570,7 +623,11 @@ static int fsl_esdhc_probe(struct device_d *dev) host = xzalloc(sizeof(*host)); mci = &host->mci; - host->clk = clk_get(dev, NULL); + host->socdata = of_device_get_match_data(dev); + if (!host->socdata) + return -EINVAL; + + host->clk = clk_get(dev, "per"); if (IS_ERR(host->clk)) return PTR_ERR(host->clk); @@ -634,24 +691,48 @@ static int fsl_esdhc_probe(struct device_d *dev) return mci_register(&host->mci); } +static struct esdhc_soc_data esdhc_imx25_data = { + .flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx50_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, + /* .flags = 0, */ +}; + +static struct esdhc_soc_data esdhc_imx51_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, + /* .flags = 0, */ +}; + +static struct esdhc_soc_data esdhc_imx53_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, +}; + +static struct esdhc_soc_data usdhc_imx6q_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING, +}; + +static struct esdhc_soc_data usdhc_imx6sl_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536 + | ESDHC_FLAG_HS200, +}; + +static struct esdhc_soc_data usdhc_imx6sx_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, +}; + static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = { - { - .compatible = "fsl,imx25-esdhc", - }, { - .compatible = "fsl,imx50-esdhc", - }, { - .compatible = "fsl,imx51-esdhc", - }, { - .compatible = "fsl,imx53-esdhc", - }, { - .compatible = "fsl,imx6q-usdhc", - }, { - .compatible = "fsl,imx6sl-usdhc", - }, { - .compatible = "fsl,imx6sx-usdhc", - }, { - /* sentinel */ - } + { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data }, + { .compatible = "fsl,imx50-esdhc", .data = &esdhc_imx50_data }, + { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data }, + { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data }, + { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data }, + { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data }, + { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data }, + { /* sentinel */ } }; static struct driver_d fsl_esdhc_driver = { diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 8bc7c2993a..e2b25fe375 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -51,7 +51,7 @@ static int fec_miibus_read(struct mii_bus *bus, int phyAddr, int regAddr) uint32_t phy; /* convenient holder for the PHY */ uint64_t start; - writel(((clk_get_rate(fec->clk) >> 20) / 5) << 1, + writel(((clk_get_rate(fec->clk[FEC_CLK_IPG]) >> 20) / 5) << 1, fec->regs + FEC_MII_SPEED); /* * reading from any PHY's register is done by properly @@ -94,7 +94,7 @@ static int fec_miibus_write(struct mii_bus *bus, int phyAddr, uint32_t phy; /* convenient holder for the PHY */ uint64_t start; - writel(((clk_get_rate(fec->clk) >> 20) / 5) << 1, + writel(((clk_get_rate(fec->clk[FEC_CLK_IPG]) >> 20) / 5) << 1, fec->regs + FEC_MII_SPEED); reg = regAddr << FEC_MII_DATA_RA_SHIFT; @@ -287,7 +287,7 @@ static int fec_init(struct eth_device *dev) * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock * and do not drop the Preamble. */ - writel(((clk_get_rate(fec->clk) >> 20) / 5) << 1, + writel(((clk_get_rate(fec->clk[FEC_CLK_IPG]) >> 20) / 5) << 1, fec->regs + FEC_MII_SPEED); if (fec->interface == PHY_INTERFACE_MODE_RMII) { @@ -651,6 +651,59 @@ static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec) return -ENODEV; } #endif + +static int fec_clk_enable(struct fec_priv *fec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fec->clk); i++) { + const int err = clk_enable(fec->clk[i]); + if (err < 0) + return err; + } + + return 0; +} + +static void fec_clk_disable(struct fec_priv *fec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fec->clk); i++) { + if (!IS_ERR_OR_NULL(fec->clk[i])) + clk_disable(fec->clk[i]); + } +} + +static void fec_clk_put(struct fec_priv *fec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fec->clk); i++) { + if (!IS_ERR_OR_NULL(fec->clk[i])) + clk_put(fec->clk[i]); + } +} + +static int fec_clk_get(struct fec_priv *fec) +{ + int i, err = 0; + static const char *clk_names[ARRAY_SIZE(fec->clk)] = { + "ipg", "ahb", "ptp" + }; + + for (i = 0; i < ARRAY_SIZE(fec->clk); i++) { + fec->clk[i] = clk_get(fec->edev.parent, clk_names[i]); + if (IS_ERR(fec->clk[i])) { + err = PTR_ERR(fec->clk[i]); + fec_clk_put(fec); + break; + } + } + + return err; +} + static int fec_probe(struct device_d *dev) { struct resource *iores; @@ -681,13 +734,11 @@ static int fec_probe(struct device_d *dev) edev->set_ethaddr = fec_set_hwaddr; edev->parent = dev; - fec->clk = clk_get(dev, NULL); - if (IS_ERR(fec->clk)) { - ret = PTR_ERR(fec->clk); + ret = fec_clk_get(fec); + if (ret < 0) goto err_free; - } - ret = clk_enable(fec->clk); + ret = fec_clk_enable(fec); if (ret < 0) goto put_clk; @@ -787,9 +838,9 @@ free_gpio: release_res: release_region(iores); disable_clk: - clk_disable(fec->clk); + fec_clk_disable(fec); put_clk: - clk_put(fec->clk); + fec_clk_put(fec); err_free: free(fec); return ret; @@ -819,6 +870,9 @@ static __maybe_unused struct of_device_id imx_fec_dt_ids[] = { .compatible = "fsl,imx6sx-fec", .data = (void *)FEC_TYPE_IMX6, }, { + .compatible = "fsl,mvf600-fec", + .data = (void *)FEC_TYPE_IMX6, + }, { /* sentinel */ } }; diff --git a/drivers/net/fec_imx.h b/drivers/net/fec_imx.h index 1947e60f69..85d51bad60 100644 --- a/drivers/net/fec_imx.h +++ b/drivers/net/fec_imx.h @@ -129,6 +129,14 @@ enum fec_type { FEC_TYPE_IMX6, }; +enum fec_clock { + FEC_CLK_IPG, + FEC_CLK_AHB, + FEC_CLK_PTP, + + FEC_CLK_NUM +}; + /** * @brief i.MX27-FEC private structure */ @@ -144,7 +152,7 @@ struct fec_priv { u32 phy_flags; struct mii_bus miibus; void (*phy_init)(struct phy_device *dev); - struct clk *clk; + struct clk *clk[FEC_CLK_NUM]; enum fec_type type; }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index b112d7ee04..4eab437ea5 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -137,4 +137,9 @@ config DRIVER_SERIAL_DIGIC bool "Canon DIGIC serial driver" depends on ARCH_DIGIC +config DRIVER_SERIAL_LPUART + depends on ARCH_VF610 + default y + bool "LPUART serial driver" + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 189e777777..7d1bae195f 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_DRIVER_SERIAL_AUART) += serial_auart.o obj-$(CONFIG_DRIVER_SERIAL_CADENCE) += serial_cadence.o obj-$(CONFIG_DRIVER_SERIAL_EFI_STDIO) += efi-stdio.o obj-$(CONFIG_DRIVER_SERIAL_DIGIC) += serial_digic.o +obj-$(CONFIG_DRIVER_SERIAL_LPUART) += serial_lpuart.o diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c new file mode 100644 index 0000000000..52fb6d39c0 --- /dev/null +++ b/drivers/serial/serial_lpuart.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2016 Zodiac Inflight Innovation + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * Based on analogous driver from U-Boot + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> +#include <notifier.h> +#include <io.h> +#include <of.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <serial/lpuart.h> + +struct lpuart { + struct console_device cdev; + int baudrate; + int dte_mode; + struct notifier_block notify; + struct resource *io; + void __iomem *base; + struct clk *clk; +}; + +static struct lpuart *cdev_to_lpuart(struct console_device *cdev) +{ + return container_of(cdev, struct lpuart, cdev); +} + +static struct lpuart *nb_to_lpuart(struct notifier_block *nb) +{ + return container_of(nb, struct lpuart, notify); +} + +static void lpuart_enable(struct lpuart *lpuart, bool on) +{ + u8 ctrl; + + ctrl = readb(lpuart->base + UARTCR2); + if (on) + ctrl |= UARTCR2_TE | UARTCR2_RE; + else + ctrl &= ~(UARTCR2_TE | UARTCR2_RE); + writeb(ctrl, lpuart->base + UARTCR2); +} + +static int lpuart_serial_setbaudrate(struct console_device *cdev, + int baudrate) +{ + struct lpuart *lpuart = cdev_to_lpuart(cdev); + + lpuart_enable(lpuart, false); + + lpuart_setbrg(lpuart->base, + clk_get_rate(lpuart->clk), + baudrate); + + lpuart_enable(lpuart, true); + + lpuart->baudrate = baudrate; + + return 0; +} + +static int lpuart_serial_getc(struct console_device *cdev) +{ + bool ready; + struct lpuart *lpuart = cdev_to_lpuart(cdev); + + do { + const u8 sr1 = readb(lpuart->base + UARTSR1); + ready = !!(sr1 & (UARTSR1_OR | UARTSR1_RDRF)); + } while (!ready); + + return readb(lpuart->base + UARTDR); +} + +static void lpuart_serial_putc(struct console_device *cdev, char c) +{ + lpuart_putc(cdev_to_lpuart(cdev)->base, c); +} + +/* Test whether a character is in the RX buffer */ +static int lpuart_serial_tstc(struct console_device *cdev) +{ + return !!readb(cdev_to_lpuart(cdev)->base + UARTRCFIFO); +} + +static void lpuart_serial_flush(struct console_device *cdev) +{ + bool tx_empty; + struct lpuart *lpuart = cdev_to_lpuart(cdev); + + do { + const u8 sr1 = readb(lpuart->base + UARTSR1); + tx_empty = !!(sr1 & UARTSR1_TDRE); + } while (!tx_empty); +} + +static int lpuart_clocksource_clock_change(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct lpuart *lpuart = nb_to_lpuart(nb); + + return lpuart_serial_setbaudrate(&lpuart->cdev, lpuart->baudrate); +} + +static int lpuart_serial_probe(struct device_d *dev) +{ + int ret; + struct console_device *cdev; + struct lpuart *lpuart; + const char *devname; + + lpuart = xzalloc(sizeof(*lpuart)); + cdev = &lpuart->cdev; + dev->priv = lpuart; + + lpuart->io = dev_request_mem_resource(dev, 0); + if (IS_ERR(lpuart->io)) { + ret = PTR_ERR(lpuart->io); + goto err_free; + } + lpuart->base = IOMEM(lpuart->io->start); + + lpuart->clk = clk_get(dev, NULL); + if (IS_ERR(lpuart->clk)) { + ret = PTR_ERR(lpuart->clk); + dev_err(dev, "Failed to get UART clock %d\n", ret); + goto io_release; + } + + ret = clk_enable(lpuart->clk); + if (ret) { + dev_err(dev, "Failed to enable UART clock %d\n", ret); + goto io_release; + } + + cdev->dev = dev; + cdev->tstc = lpuart_serial_tstc; + cdev->putc = lpuart_serial_putc; + cdev->getc = lpuart_serial_getc; + cdev->flush = lpuart_serial_flush; + cdev->setbrg = lpuart_serial_setbaudrate; + + if (dev->device_node) { + devname = of_alias_get(dev->device_node); + if (devname) { + cdev->devname = xstrdup(devname); + cdev->devid = DEVICE_ID_SINGLE; + } + } + + cdev->linux_console_name = "ttyLP"; + + lpuart_setup_with_fifo(lpuart->base, + clk_get_rate(lpuart->clk), + 15); + + ret = console_register(cdev); + if (!ret) { + lpuart->notify.notifier_call = lpuart_clocksource_clock_change; + clock_register_client(&lpuart->notify); + + return 0; + } + + clk_put(lpuart->clk); +io_release: + release_region(lpuart->io); +err_free: + free(lpuart); + + return ret; +} + +static void lpuart_serial_remove(struct device_d *dev) +{ + struct lpuart *lpuart = dev->priv; + + lpuart_serial_flush(&lpuart->cdev); + console_unregister(&lpuart->cdev); + release_region(lpuart->io); + clk_put(lpuart->clk); + + free(lpuart); +} + +static struct of_device_id lpuart_serial_dt_ids[] = { + { .compatible = "fsl,vf610-lpuart" }, + {} +}; + +static struct driver_d lpuart_serial_driver = { + .name = "lpuart-serial", + .probe = lpuart_serial_probe, + .remove = lpuart_serial_remove, + .of_compatible = DRV_OF_COMPAT(lpuart_serial_dt_ids), +}; +console_platform_driver(lpuart_serial_driver); diff --git a/include/linux/clk/clk-conf.h b/include/linux/clk/clk-conf.h new file mode 100644 index 0000000000..8f4382e6c6 --- /dev/null +++ b/include/linux/clk/clk-conf.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER) + +#include <linux/types.h> + +struct device_node; +int of_clk_set_defaults(struct device_node *node, bool clk_supplier); + +#endif |