summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig22
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/clk-divider.c155
-rw-r--r--drivers/clk/clk-fixed-factor.c80
-rw-r--r--drivers/clk/clk-mux.c172
-rw-r--r--drivers/clk/clk-uclass.c60
-rw-r--r--drivers/clk/clk.c57
-rw-r--r--drivers/clk/clk_fixed_factor.c3
-rw-r--r--drivers/clk/clk_fixed_rate.c8
-rw-r--r--drivers/clk/clk_sandbox_ccf.c185
-rw-r--r--drivers/clk/imx/Kconfig16
-rw-r--r--drivers/clk/imx/Makefile2
-rw-r--r--drivers/clk/imx/clk-gate2.c103
-rw-r--r--drivers/clk/imx/clk-imx6q.c179
-rw-r--r--drivers/clk/imx/clk-pfd.c90
-rw-r--r--drivers/clk/imx/clk-pllv3.c82
-rw-r--r--drivers/clk/imx/clk.h69
-rw-r--r--drivers/gpio/mxc_gpio.c2
-rw-r--r--drivers/gpio/mxs_gpio.c148
-rw-r--r--drivers/misc/imx8/scu_api.c28
-rw-r--r--drivers/mmc/mmc_spi.c4
-rw-r--r--drivers/mtd/nand/raw/mxs_nand.c2
-rw-r--r--drivers/mtd/nand/raw/mxs_nand.h73
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_dt.c2
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_spl.c2
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c4
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--drivers/net/fec_mxc.c1
-rw-r--r--drivers/pinctrl/nxp/Kconfig10
-rw-r--r--drivers/pinctrl/nxp/Makefile1
-rw-r--r--drivers/pinctrl/nxp/pinctrl-mxs.c190
-rw-r--r--drivers/pinctrl/nxp/pinctrl-mxs.h61
-rw-r--r--drivers/power/pmic/bd71837.c32
-rw-r--r--drivers/power/regulator/Kconfig17
-rw-r--r--drivers/power/regulator/Makefile1
-rw-r--r--drivers/power/regulator/bd71837.c468
-rw-r--r--drivers/pwm/Kconfig5
-rw-r--r--drivers/pwm/pwm-imx.c110
-rw-r--r--drivers/serial/serial_mxc.c4
-rw-r--r--drivers/spi/Kconfig21
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/fsl_qspi.c18
-rw-r--r--drivers/spi/mtk_qspi.c359
-rw-r--r--drivers/spi/mtk_snfi_spi.c318
-rw-r--r--drivers/spi/mxs_spi.c400
-rw-r--r--drivers/spi/spi-mem.c8
-rw-r--r--drivers/spi/spi-sifive.c370
-rw-r--r--drivers/thermal/Kconfig9
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/imx_scu_thermal.c203
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/imx_watchdog.c119
53 files changed, 3710 insertions, 583 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 7b81eacf50..5e92446c18 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -46,6 +46,20 @@ config CLK_BOSTON
help
Enable this to support the clocks
+config SPL_CLK_CCF
+ bool "SPL Common Clock Framework [CCF] support "
+ depends on SPL_CLK_IMX6Q
+ help
+ Enable this option if you want to (re-)use the Linux kernel's Common
+ Clock Framework [CCF] code in U-Boot's SPL.
+
+config CLK_CCF
+ bool "Common Clock Framework [CCF] support "
+ depends on CLK_IMX6Q || SANDBOX_CLK_CCF
+ help
+ Enable this option if you want to (re-)use the Linux kernel's Common
+ Clock Framework [CCF] code in U-Boot's clock driver.
+
config CLK_STM32F
bool "Enable clock driver support for STM32F family"
depends on CLK && (STM32F7 || STM32F4)
@@ -125,4 +139,12 @@ config CLK_MPC83XX
help
Support for the clock driver of the MPC83xx series of SoCs.
+config SANDBOX_CLK_CCF
+ bool "Sandbox Common Clock Framework [CCF] support "
+ depends on SANDBOX
+ select CLK_CCF
+ help
+ Enable this option if you want to test the Linux kernel's Common
+ Clock Framework [CCF] code in U-Boot's Sandbox clock driver.
+
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f0ced49e5a..b7fec605c6 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -7,6 +7,8 @@
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
obj-y += analogbits/
obj-y += imx/
@@ -37,5 +39,6 @@ obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
+obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
obj-$(CONFIG_STM32H7) += clk_stm32h7.o
obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
new file mode 100644
index 0000000000..6921c76a48
--- /dev/null
+++ b/drivers/clk/clk-divider.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
+
+static unsigned int _get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
+static unsigned int _get_div(const struct clk_div_table *table,
+ unsigned int val, unsigned long flags, u8 width)
+{
+ if (flags & CLK_DIVIDER_ONE_BASED)
+ return val;
+ if (flags & CLK_DIVIDER_POWER_OF_TWO)
+ return 1 << val;
+ if (flags & CLK_DIVIDER_MAX_AT_ZERO)
+ return val ? val : clk_div_mask(width) + 1;
+ if (table)
+ return _get_table_div(table, val);
+ return val + 1;
+}
+
+unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
+ unsigned int val,
+ const struct clk_div_table *table,
+ unsigned long flags, unsigned long width)
+{
+ unsigned int div;
+
+ div = _get_div(table, val, flags, width);
+ if (!div) {
+ WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ clk_hw_get_name(hw));
+ return parent_rate;
+ }
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+}
+
+static ulong clk_divider_recalc_rate(struct clk *clk)
+{
+ struct clk_divider *divider =
+ to_clk_divider(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+ unsigned int val;
+
+#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
+ val = divider->io_divider_val;
+#else
+ val = readl(divider->reg);
+#endif
+ val >>= divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_recalc_rate(clk, parent_rate, val, divider->table,
+ divider->flags, divider->width);
+}
+
+const struct clk_ops clk_divider_ops = {
+ .get_rate = clk_divider_recalc_rate,
+};
+
+static struct clk *_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table)
+{
+ struct clk_divider *div;
+ struct clk *clk;
+ int ret;
+
+ if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
+ if (width + shift > 16) {
+ pr_warn("divider value exceeds LOWORD field\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ /* allocate the divider */
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ /* struct clk_divider assignments */
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->flags = clk_divider_flags;
+ div->table = table;
+#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
+ div->io_divider_val = *(u32 *)reg;
+#endif
+
+ /* register the clock */
+ clk = &div->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
+ if (ret) {
+ kfree(div);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags)
+{
+ struct clk *clk;
+
+ clk = _register_divider(dev, name, parent_name, flags, reg, shift,
+ width, clk_divider_flags, NULL);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return clk;
+}
+
+U_BOOT_DRIVER(ccf_clk_divider) = {
+ .name = UBOOT_DM_CLK_CCF_DIVIDER,
+ .id = UCLASS_CLK,
+ .ops = &clk_divider_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
new file mode 100644
index 0000000000..711b0588bc
--- /dev/null
+++ b/drivers/clk/clk-fixed-factor.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ */
+#include <common.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_FIXED_FACTOR "ccf_clk_fixed_factor"
+
+static ulong clk_factor_recalc_rate(struct clk *clk)
+{
+ struct clk_fixed_factor *fix =
+ to_clk_fixed_factor(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+ unsigned long long int rate;
+
+ rate = (unsigned long long int)parent_rate * fix->mult;
+ do_div(rate, fix->div);
+ return (ulong)rate;
+}
+
+const struct clk_ops ccf_clk_fixed_factor_ops = {
+ .get_rate = clk_factor_recalc_rate,
+};
+
+struct clk *clk_hw_register_fixed_factor(struct device *dev,
+ const char *name, const char *parent_name, unsigned long flags,
+ unsigned int mult, unsigned int div)
+{
+ struct clk_fixed_factor *fix;
+ struct clk *clk;
+ int ret;
+
+ fix = kzalloc(sizeof(*fix), GFP_KERNEL);
+ if (!fix)
+ return ERR_PTR(-ENOMEM);
+
+ /* struct clk_fixed_factor assignments */
+ fix->mult = mult;
+ fix->div = div;
+ clk = &fix->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name,
+ parent_name);
+ if (ret) {
+ kfree(fix);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned int mult, unsigned int div)
+{
+ struct clk *clk;
+
+ clk = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
+ div);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return clk;
+}
+
+U_BOOT_DRIVER(imx_clk_fixed_factor) = {
+ .name = UBOOT_DM_CLK_IMX_FIXED_FACTOR,
+ .id = UCLASS_CLK,
+ .ops = &ccf_clk_fixed_factor_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
new file mode 100644
index 0000000000..3c075aa09e
--- /dev/null
+++ b/drivers/clk/clk-mux.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * Simple multiplexer clock implementation
+ */
+
+/*
+ * U-Boot CCF porting node:
+ *
+ * The Linux kernel - as of tag: 5.0-rc3 is using also the imx_clk_fixup_mux()
+ * version of CCF mux. It is used on e.g. imx6q to provide fixes (like
+ * imx_cscmr1_fixup) for broken HW.
+ *
+ * At least for IMX6Q (but NOT IMX6QP) it is important when we set the parent
+ * clock.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux"
+
+int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
+ unsigned int val)
+{
+ struct clk_mux *mux = to_clk_mux(clk);
+ int num_parents = mux->num_parents;
+
+ if (table) {
+ int i;
+
+ for (i = 0; i < num_parents; i++)
+ if (table[i] == val)
+ return i;
+ return -EINVAL;
+ }
+
+ if (val && (flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+ if (val && (flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= num_parents)
+ return -EINVAL;
+
+ return val;
+}
+
+static u8 clk_mux_get_parent(struct clk *clk)
+{
+ struct clk_mux *mux = to_clk_mux(clk);
+ u32 val;
+
+#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
+ val = mux->io_mux_val;
+#else
+ val = readl(mux->reg);
+#endif
+ val >>= mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_val_to_index(clk, mux->table, mux->flags, val);
+}
+
+const struct clk_ops clk_mux_ops = {
+ .get_rate = clk_generic_get_rate,
+};
+
+struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table)
+{
+ struct clk_mux *mux;
+ struct clk *clk;
+ u8 width = 0;
+ int ret;
+
+ if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
+ width = fls(mask) - ffs(mask) + 1;
+ if (width + shift > 16) {
+ pr_err("mux value exceeds LOWORD field\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ /* allocate the mux */
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ /* U-boot specific assignments */
+ mux->parent_names = parent_names;
+ mux->num_parents = num_parents;
+
+ /* struct clk_mux assignments */
+ mux->reg = reg;
+ mux->shift = shift;
+ mux->mask = mask;
+ mux->flags = clk_mux_flags;
+ mux->table = table;
+#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
+ mux->io_mux_val = *(u32 *)reg;
+#endif
+
+ clk = &mux->clk;
+
+ /*
+ * Read the current mux setup - so we assign correct parent.
+ *
+ * Changing parent would require changing internals of udevice struct
+ * for the corresponding clock (to do that define .set_parent() method.
+ */
+ ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name,
+ parent_names[clk_mux_get_parent(clk)]);
+ if (ret) {
+ kfree(mux);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table)
+{
+ struct clk *clk;
+
+ clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, shift, mask, clk_mux_flags,
+ table);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return clk;
+}
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags)
+{
+ u32 mask = BIT(width) - 1;
+
+ return clk_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, shift, mask, clk_mux_flags,
+ NULL);
+}
+
+U_BOOT_DRIVER(ccf_clk_mux) = {
+ .name = UBOOT_DM_CLK_CCF_MUX,
+ .id = UCLASS_CLK,
+ .ops = &clk_mux_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 06a8258d5f..85dfe712f5 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -13,6 +13,7 @@
#include <dm/read.h>
#include <dt-structs.h>
#include <errno.h>
+#include <linux/clk-provider.h>
static inline const struct clk_ops *clk_dev_ops(struct udevice *dev)
{
@@ -381,6 +382,43 @@ ulong clk_get_rate(struct clk *clk)
return ops->get_rate(clk);
}
+struct clk *clk_get_parent(struct clk *clk)
+{
+ struct udevice *pdev;
+ struct clk *pclk;
+
+ debug("%s(clk=%p)\n", __func__, clk);
+
+ pdev = dev_get_parent(clk->dev);
+ pclk = dev_get_clk_ptr(pdev);
+ if (!pclk)
+ return ERR_PTR(-ENODEV);
+
+ return pclk;
+}
+
+long long clk_get_parent_rate(struct clk *clk)
+{
+ const struct clk_ops *ops;
+ struct clk *pclk;
+
+ debug("%s(clk=%p)\n", __func__, clk);
+
+ pclk = clk_get_parent(clk);
+ if (IS_ERR(pclk))
+ return -ENODEV;
+
+ ops = clk_dev_ops(pclk->dev);
+ if (!ops->get_rate)
+ return -ENOSYS;
+
+ /* Read the 'rate' if not already set or if proper flag set*/
+ if (!pclk->rate || pclk->flags & CLK_GET_RATE_NOCACHE)
+ pclk->rate = clk_get_rate(pclk);
+
+ return pclk->rate;
+}
+
ulong clk_set_rate(struct clk *clk, ulong rate)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
@@ -455,6 +493,28 @@ int clk_disable_bulk(struct clk_bulk *bulk)
return 0;
}
+int clk_get_by_id(ulong id, struct clk **clkp)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_CLK, &uc);
+ if (ret)
+ return ret;
+
+ uclass_foreach_dev(dev, uc) {
+ struct clk *clk = dev_get_clk_ptr(dev);
+
+ if (clk && clk->id == id) {
+ *clkp = clk;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
UCLASS_DRIVER(clk) = {
.id = UCLASS_CLK,
.name = "clk",
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
new file mode 100644
index 0000000000..7d748c9fc7
--- /dev/null
+++ b/drivers/clk/clk.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <clk.h>
+
+int clk_register(struct clk *clk, const char *drv_name,
+ const char *name, const char *parent_name)
+{
+ struct udevice *parent;
+ struct driver *drv;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &parent);
+ if (ret)
+ printf("%s: UCLASS parent: 0x%p\n", __func__, parent);
+
+ debug("%s: name: %s parent: %s [0x%p]\n", __func__, name, parent->name,
+ parent);
+
+ drv = lists_driver_lookup_name(drv_name);
+ if (!drv) {
+ printf("%s: %s is not a valid driver name\n",
+ __func__, drv_name);
+ return -ENOENT;
+ }
+
+ ret = device_bind(parent, drv, name, NULL, -1, &clk->dev);
+ if (ret) {
+ printf("%s: CLK: %s driver bind error [%d]!\n", __func__, name,
+ ret);
+ return ret;
+ }
+
+ /* Store back pointer to clk from udevice */
+ clk->dev->uclass_priv = clk;
+
+ return 0;
+}
+
+ulong clk_generic_get_rate(struct clk *clk)
+{
+ return clk_get_parent_rate(clk);
+}
+
+const char *clk_hw_get_name(const struct clk *hw)
+{
+ return hw->dev->name;
+}
diff --git a/drivers/clk/clk_fixed_factor.c b/drivers/clk/clk_fixed_factor.c
index 5fa20a84db..dcdb6ddf5c 100644
--- a/drivers/clk/clk_fixed_factor.c
+++ b/drivers/clk/clk_fixed_factor.c
@@ -24,9 +24,6 @@ static ulong clk_fixed_factor_get_rate(struct clk *clk)
uint64_t rate;
struct clk_fixed_factor *ff = to_clk_fixed_factor(clk->dev);
- if (clk->id != 0)
- return -EINVAL;
-
rate = clk_get_rate(&ff->parent);
if (IS_ERR_VALUE(rate))
return rate;
diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c
index d8d9f86c86..1fdf8c4e54 100644
--- a/drivers/clk/clk_fixed_rate.c
+++ b/drivers/clk/clk_fixed_rate.c
@@ -8,6 +8,7 @@
#include <dm.h>
struct clk_fixed_rate {
+ struct clk clk;
unsigned long fixed_rate;
};
@@ -15,9 +16,6 @@ struct clk_fixed_rate {
static ulong clk_fixed_rate_get_rate(struct clk *clk)
{
- if (clk->id != 0)
- return -EINVAL;
-
return to_clk_fixed_rate(clk->dev)->fixed_rate;
}
@@ -27,10 +25,14 @@ const struct clk_ops clk_fixed_rate_ops = {
static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev)
{
+ struct clk *clk = &to_clk_fixed_rate(dev)->clk;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
to_clk_fixed_rate(dev)->fixed_rate =
dev_read_u32_default(dev, "clock-frequency", 0);
#endif
+ /* Make fixed rate clock accessible from higher level struct clk */
+ dev->uclass_priv = clk;
+ clk->dev = dev;
return 0;
}
diff --git a/drivers/clk/clk_sandbox_ccf.c b/drivers/clk/clk_sandbox_ccf.c
new file mode 100644
index 0000000000..edeb0f2cf3
--- /dev/null
+++ b/drivers/clk/clk_sandbox_ccf.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Common Clock Framework [CCF] driver for Sandbox
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <clk.h>
+#include <asm/clk.h>
+#include <clk-uclass.h>
+#include <linux/clk-provider.h>
+#include <sandbox-clk.h>
+
+/*
+ * Sandbox implementation of CCF primitives necessary for clk-uclass testing
+ *
+ * --- Sandbox PLLv3 ---
+ */
+struct clk_pllv3 {
+ struct clk clk;
+ u32 div_mask;
+ u32 div_shift;
+};
+
+static ulong clk_pllv3_get_rate(struct clk *clk)
+{
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+
+ return parent_rate * 24;
+}
+
+static const struct clk_ops clk_pllv3_generic_ops = {
+ .get_rate = clk_pllv3_get_rate,
+};
+
+struct clk *sandbox_clk_pllv3(enum sandbox_pllv3_type type, const char *name,
+ const char *parent_name, void __iomem *base,
+ u32 div_mask)
+{
+ struct clk_pllv3 *pll;
+ struct clk *clk;
+ char *drv_name = "sandbox_clk_pllv3";
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->div_mask = div_mask;
+ clk = &pll->clk;
+
+ ret = clk_register(clk, drv_name, name, parent_name);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(sandbox_clk_pll_generic) = {
+ .name = "sandbox_clk_pllv3",
+ .id = UCLASS_CLK,
+ .ops = &clk_pllv3_generic_ops,
+};
+
+/* --- Sandbox PLLv3 --- */
+/* --- Sandbox Gate --- */
+struct clk_gate2 {
+ struct clk clk;
+ bool state;
+};
+
+#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
+
+static int clk_gate2_enable(struct clk *clk)
+{
+ struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+
+ gate->state = 1;
+ return 0;
+}
+
+static int clk_gate2_disable(struct clk *clk)
+{
+ struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+
+ gate->state = 0;
+ return 0;
+}
+
+static const struct clk_ops clk_gate2_ops = {
+ .enable = clk_gate2_enable,
+ .disable = clk_gate2_disable,
+ .get_rate = clk_generic_get_rate,
+};
+
+struct clk *sandbox_clk_register_gate2(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags, void __iomem *reg,
+ u8 bit_idx, u8 cgr_val,
+ u8 clk_gate2_flags)
+{
+ struct clk_gate2 *gate;
+ struct clk *clk;
+ int ret;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->state = 0;
+ clk = &gate->clk;
+
+ ret = clk_register(clk, "sandbox_clk_gate2", name, parent_name);
+ if (ret) {
+ kfree(gate);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(sandbox_clk_gate2) = {
+ .name = "sandbox_clk_gate2",
+ .id = UCLASS_CLK,
+ .ops = &clk_gate2_ops,
+};
+
+/* --- Sandbox Gate --- */
+/* The CCF core driver itself */
+static const struct udevice_id sandbox_clk_ccf_test_ids[] = {
+ { .compatible = "sandbox,clk-ccf" },
+ { }
+};
+
+static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", };
+
+static int sandbox_clk_ccf_probe(struct udevice *dev)
+{
+ void *base = NULL;
+ u32 reg;
+
+ clk_dm(SANDBOX_CLK_PLL3,
+ sandbox_clk_pllv3(SANDBOX_PLLV3_USB, "pll3_usb_otg", "osc",
+ base + 0x10, 0x3));
+
+ clk_dm(SANDBOX_CLK_PLL3_60M,
+ sandbox_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
+
+ clk_dm(SANDBOX_CLK_PLL3_80M,
+ sandbox_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6));
+
+ /* The HW adds +1 to the divider value (2+1) is the divider */
+ reg = (2 << 19);
+ clk_dm(SANDBOX_CLK_ECSPI_ROOT,
+ sandbox_clk_divider("ecspi_root", "pll3_60m", &reg, 19, 6));
+
+ clk_dm(SANDBOX_CLK_ECSPI1,
+ sandbox_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
+
+ /* Select 'pll3_60m' */
+ reg = 0;
+ clk_dm(SANDBOX_CLK_USDHC1_SEL,
+ sandbox_clk_mux("usdhc1_sel", &reg, 16, 1, usdhc_sels,
+ ARRAY_SIZE(usdhc_sels)));
+
+ /* Select 'pll3_80m' */
+ reg = BIT(17);
+ clk_dm(SANDBOX_CLK_USDHC2_SEL,
+ sandbox_clk_mux("usdhc2_sel", &reg, 17, 1, usdhc_sels,
+ ARRAY_SIZE(usdhc_sels)));
+
+ return 0;
+}
+
+U_BOOT_DRIVER(sandbox_clk_ccf) = {
+ .name = "sandbox_clk_ccf",
+ .id = UCLASS_CLK,
+ .probe = sandbox_clk_ccf_probe,
+ .of_match = sandbox_clk_ccf_test_ids,
+};
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index a6fb58d6cf..3e6a980c8c 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -1,3 +1,19 @@
+config SPL_CLK_IMX6Q
+ bool "SPL clock support for i.MX6Q"
+ depends on ARCH_MX6 && SPL
+ select SPL_CLK
+ select SPL_CLK_CCF
+ help
+ This enables SPL DM/DTS support for clock driver in i.MX6Q platforms.
+
+config CLK_IMX6Q
+ bool "Clock support for i.MX6Q"
+ depends on ARCH_MX6
+ select CLK
+ select CLK_CCF
+ help
+ This enables DM/DTS support for clock driver in i.MX6Q platforms.
+
config CLK_IMX8
bool "Clock support for i.MX8"
depends on ARCH_IMX8
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index eb379c188a..105a58ca90 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -2,6 +2,8 @@
#
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-gate2.o clk-pllv3.o clk-pfd.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_IMX6Q) += clk-imx6q.o
obj-$(CONFIG_CLK_IMX8) += clk-imx8.o
ifdef CONFIG_CLK_IMX8
diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c
new file mode 100644
index 0000000000..571be32088
--- /dev/null
+++ b/drivers/clk/imx/clk-gate2.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Gated clock implementation
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2"
+
+struct clk_gate2 {
+ struct clk clk;
+ void __iomem *reg;
+ u8 bit_idx;
+ u8 cgr_val;
+ u8 flags;
+};
+
+#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
+
+static int clk_gate2_enable(struct clk *clk)
+{
+ struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ u32 reg;
+
+ reg = readl(gate->reg);
+ reg &= ~(3 << gate->bit_idx);
+ reg |= gate->cgr_val << gate->bit_idx;
+ writel(reg, gate->reg);
+
+ return 0;
+}
+
+static int clk_gate2_disable(struct clk *clk)
+{
+ struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ u32 reg;
+
+ reg = readl(gate->reg);
+ reg &= ~(3 << gate->bit_idx);
+ writel(reg, gate->reg);
+
+ return 0;
+}
+
+static const struct clk_ops clk_gate2_ops = {
+ .enable = clk_gate2_enable,
+ .disable = clk_gate2_disable,
+ .get_rate = clk_generic_get_rate,
+};
+
+struct clk *clk_register_gate2(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx, u8 cgr_val,
+ u8 clk_gate2_flags)
+{
+ struct clk_gate2 *gate;
+ struct clk *clk;
+ int ret;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ gate->cgr_val = cgr_val;
+ gate->flags = clk_gate2_flags;
+
+ clk = &gate->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name);
+ if (ret) {
+ kfree(gate);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_gate2) = {
+ .name = UBOOT_DM_CLK_IMX_GATE2,
+ .id = UCLASS_CLK,
+ .ops = &clk_gate2_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
new file mode 100644
index 0000000000..92e9337d44
--- /dev/null
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <dt-bindings/clock/imx6qdl-clock.h>
+
+#include "clk.h"
+
+static int imx6q_check_id(ulong id)
+{
+ if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) {
+ printf("%s: Invalid clk ID #%lu\n", __func__, id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ulong imx6q_clk_get_rate(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ debug("%s(#%lu)\n", __func__, clk->id);
+
+ ret = imx6q_check_id(clk->id);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_get_rate(c);
+}
+
+static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
+
+ return rate;
+}
+
+static int __imx6q_clk_enable(struct clk *clk, bool enable)
+{
+ struct clk *c;
+ int ret = 0;
+
+ debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
+
+ ret = imx6q_check_id(clk->id);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ if (enable)
+ ret = clk_enable(c);
+ else
+ ret = clk_disable(c);
+
+ return ret;
+}
+
+static int imx6q_clk_disable(struct clk *clk)
+{
+ return __imx6q_clk_enable(clk, 0);
+}
+
+static int imx6q_clk_enable(struct clk *clk)
+{
+ return __imx6q_clk_enable(clk, 1);
+}
+
+static struct clk_ops imx6q_clk_ops = {
+ .set_rate = imx6q_clk_set_rate,
+ .get_rate = imx6q_clk_get_rate,
+ .enable = imx6q_clk_enable,
+ .disable = imx6q_clk_disable,
+};
+
+static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
+
+static int imx6q_clk_probe(struct udevice *dev)
+{
+ void *base;
+
+ /* Anatop clocks */
+ base = (void *)ANATOP_BASE_ADDR;
+
+ clk_dm(IMX6QDL_CLK_PLL2,
+ imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
+ base + 0x30, 0x1));
+ clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
+ imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
+ base + 0x10, 0x3));
+ clk_dm(IMX6QDL_CLK_PLL3_60M,
+ imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
+ clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
+ imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
+ clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
+ imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
+
+ /* CCM clocks */
+ base = dev_read_addr_ptr(dev);
+ if (base == (void *)FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ clk_dm(IMX6QDL_CLK_USDHC1_SEL,
+ imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ clk_dm(IMX6QDL_CLK_USDHC2_SEL,
+ imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ clk_dm(IMX6QDL_CLK_USDHC3_SEL,
+ imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ clk_dm(IMX6QDL_CLK_USDHC4_SEL,
+ imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+
+ clk_dm(IMX6QDL_CLK_USDHC1_PODF,
+ imx_clk_divider("usdhc1_podf", "usdhc1_sel",
+ base + 0x24, 11, 3));
+ clk_dm(IMX6QDL_CLK_USDHC2_PODF,
+ imx_clk_divider("usdhc2_podf", "usdhc2_sel",
+ base + 0x24, 16, 3));
+ clk_dm(IMX6QDL_CLK_USDHC3_PODF,
+ imx_clk_divider("usdhc3_podf", "usdhc3_sel",
+ base + 0x24, 19, 3));
+ clk_dm(IMX6QDL_CLK_USDHC4_PODF,
+ imx_clk_divider("usdhc4_podf", "usdhc4_sel",
+ base + 0x24, 22, 3));
+
+ clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
+ imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
+
+ clk_dm(IMX6QDL_CLK_ECSPI1,
+ imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
+ clk_dm(IMX6QDL_CLK_ECSPI2,
+ imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
+ clk_dm(IMX6QDL_CLK_ECSPI3,
+ imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
+ clk_dm(IMX6QDL_CLK_ECSPI4,
+ imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
+ clk_dm(IMX6QDL_CLK_USDHC1,
+ imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
+ clk_dm(IMX6QDL_CLK_USDHC2,
+ imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
+ clk_dm(IMX6QDL_CLK_USDHC3,
+ imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
+ clk_dm(IMX6QDL_CLK_USDHC4,
+ imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
+
+ return 0;
+}
+
+static const struct udevice_id imx6q_clk_ids[] = {
+ { .compatible = "fsl,imx6q-ccm" },
+ { },
+};
+
+U_BOOT_DRIVER(imx6q_clk) = {
+ .name = "clk_imx6q",
+ .id = UCLASS_CLK,
+ .of_match = imx6q_clk_ids,
+ .ops = &imx6q_clk_ops,
+ .probe = imx6q_clk_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c
new file mode 100644
index 0000000000..188b2b3b90
--- /dev/null
+++ b/drivers/clk/imx/clk-pfd.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd"
+
+struct clk_pfd {
+ struct clk clk;
+ void __iomem *reg;
+ u8 idx;
+};
+
+#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk)
+
+#define SET 0x4
+#define CLR 0x8
+#define OTG 0xc
+
+static unsigned long clk_pfd_recalc_rate(struct clk *clk)
+{
+ struct clk_pfd *pfd =
+ to_clk_pfd(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+ u64 tmp = parent_rate;
+ u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
+
+ tmp *= 18;
+ do_div(tmp, frac);
+
+ return tmp;
+}
+
+static const struct clk_ops clk_pfd_ops = {
+ .get_rate = clk_pfd_recalc_rate,
+};
+
+struct clk *imx_clk_pfd(const char *name, const char *parent_name,
+ void __iomem *reg, u8 idx)
+{
+ struct clk_pfd *pfd;
+ struct clk *clk;
+ int ret;
+
+ pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
+ if (!pfd)
+ return ERR_PTR(-ENOMEM);
+
+ pfd->reg = reg;
+ pfd->idx = idx;
+
+ /* register the clock */
+ clk = &pfd->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, name, parent_name);
+ if (ret) {
+ kfree(pfd);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_pfd) = {
+ .name = UBOOT_DM_CLK_IMX_PFD,
+ .id = UCLASS_CLK,
+ .ops = &clk_pfd_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
new file mode 100644
index 0000000000..fbb7b24d5e
--- /dev/null
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PLLV3 "imx_clk_pllv3"
+
+struct clk_pllv3 {
+ struct clk clk;
+ void __iomem *base;
+ u32 div_mask;
+ u32 div_shift;
+};
+
+#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
+
+static ulong clk_pllv3_get_rate(struct clk *clk)
+{
+ struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+
+ u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
+
+ return (div == 1) ? parent_rate * 22 : parent_rate * 20;
+}
+
+static const struct clk_ops clk_pllv3_generic_ops = {
+ .get_rate = clk_pllv3_get_rate,
+};
+
+struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
+ const char *parent_name, void __iomem *base,
+ u32 div_mask)
+{
+ struct clk_pllv3 *pll;
+ struct clk *clk;
+ char *drv_name;
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ switch (type) {
+ case IMX_PLLV3_GENERIC:
+ case IMX_PLLV3_USB:
+ drv_name = UBOOT_DM_CLK_IMX_PLLV3;
+ break;
+ default:
+ kfree(pll);
+ return ERR_PTR(-ENOTSUPP);
+ }
+
+ pll->base = base;
+ pll->div_mask = div_mask;
+ clk = &pll->clk;
+
+ ret = clk_register(clk, drv_name, name, parent_name);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_pllv3_generic) = {
+ .name = UBOOT_DM_CLK_IMX_PLLV3,
+ .id = UCLASS_CLK,
+ .ops = &clk_pllv3_generic_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
new file mode 100644
index 0000000000..e6d51830e8
--- /dev/null
+++ b/drivers/clk/imx/clk.h
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+#ifndef __MACH_IMX_CLK_H
+#define __MACH_IMX_CLK_H
+
+#include <linux/clk-provider.h>
+
+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_ENET_IMX7,
+ IMX_PLLV3_SYS_VF610,
+ IMX_PLLV3_DDR_IMX7,
+};
+
+struct clk *clk_register_gate2(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx, u8 cgr_val,
+ u8 clk_gate_flags);
+
+struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
+ const char *parent_name, void __iomem *base,
+ u32 div_mask);
+
+static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
+ void __iomem *reg, u8 shift)
+{
+ return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ shift, 0x3, 0);
+}
+
+static inline struct clk *imx_clk_fixed_factor(const char *name,
+ const char *parent, unsigned int mult, unsigned int div)
+{
+ return clk_register_fixed_factor(NULL, name, parent,
+ CLK_SET_RATE_PARENT, mult, div);
+}
+
+static inline struct clk *imx_clk_divider(const char *name, const char *parent,
+ void __iomem *reg, u8 shift, u8 width)
+{
+ return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+ reg, shift, width, 0);
+}
+
+struct clk *imx_clk_pfd(const char *name, const char *parent_name,
+ void __iomem *reg, u8 idx);
+
+struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
+ u8 shift, u8 width, const char * const *parents,
+ int num_parents, void (*fixup)(u32 *val));
+
+static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
+ u8 shift, u8 width, const char * const *parents,
+ int num_parents)
+{
+ return clk_register_mux(NULL, name, parents, num_parents,
+ CLK_SET_RATE_NO_REPARENT, reg, shift,
+ width, 0);
+}
+
+#endif /* __MACH_IMX_CLK_H */
diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c
index 8bd30c75b2..64ab7a303f 100644
--- a/drivers/gpio/mxc_gpio.c
+++ b/drivers/gpio/mxc_gpio.c
@@ -31,7 +31,7 @@ struct mxc_bank_info {
};
#ifndef CONFIG_DM_GPIO
-#define GPIO_TO_PORT(n) (n / 32)
+#define GPIO_TO_PORT(n) ((n) / 32)
/* GPIO port description */
static unsigned long gpio_ports[] = {
diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c
index c2c8a25886..b2451fdda8 100644
--- a/drivers/gpio/mxs_gpio.c
+++ b/drivers/gpio/mxs_gpio.c
@@ -51,6 +51,7 @@ void mxs_gpio_init(void)
}
}
+#if !CONFIG_IS_ENABLED(DM_GPIO)
int gpio_get_value(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
@@ -127,3 +128,150 @@ int name_to_gpio(const char *name)
return (bank << MXS_PAD_BANK_SHIFT) | (pin << MXS_PAD_PIN_SHIFT);
}
+#else /* CONFIG_DM_GPIO */
+#include <dm.h>
+#include <asm/gpio.h>
+#include <asm/arch/gpio.h>
+#define MXS_MAX_GPIO_PER_BANK 32
+
+DECLARE_GLOBAL_DATA_PTR;
+/*
+ * According to i.MX28 Reference Manual:
+ * 'i.MX28 Applications Processor Reference Manual, Rev. 1, 2010'
+ * The i.MX28 has following number of GPIOs available:
+ * Bank 0: 0-28 -> 29 PINS
+ * Bank 1: 0-31 -> 32 PINS
+ * Bank 2: 0-27 -> 28 PINS
+ * Bank 3: 0-30 -> 31 PINS
+ * Bank 4: 0-20 -> 21 PINS
+ */
+
+struct mxs_gpio_priv {
+ unsigned int bank;
+};
+
+static int mxs_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DIN(priv->bank));
+
+ return (readl(&reg->reg) >> offset) & 1;
+}
+
+static int mxs_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOUT(priv->bank));
+ if (value)
+ writel(BIT(offset), &reg->reg_set);
+ else
+ writel(BIT(offset), &reg->reg_clr);
+
+ return 0;
+}
+
+static int mxs_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOE(priv->bank));
+
+ writel(BIT(offset), &reg->reg_clr);
+
+ return 0;
+}
+
+static int mxs_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOE(priv->bank));
+
+ mxs_gpio_set_value(dev, offset, value);
+
+ writel(BIT(offset), &reg->reg_set);
+
+ return 0;
+}
+
+static int mxs_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOE(priv->bank));
+ bool is_output = !!(readl(&reg->reg) >> offset);
+
+ return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_mxs_ops = {
+ .direction_input = mxs_gpio_direction_input,
+ .direction_output = mxs_gpio_direction_output,
+ .get_value = mxs_gpio_get_value,
+ .set_value = mxs_gpio_set_value,
+ .get_function = mxs_gpio_get_function,
+};
+
+static int mxs_gpio_probe(struct udevice *dev)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct fdtdec_phandle_args args;
+ int node = dev_of_offset(dev);
+ char name[16], *str;
+ fdt_addr_t addr;
+ int ret;
+
+ addr = devfdt_get_addr(dev);
+ if (addr == FDT_ADDR_T_NONE) {
+ printf("%s: No 'reg' property defined!\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->bank = (unsigned int)addr;
+
+ snprintf(name, sizeof(name), "GPIO%d_", priv->bank);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+
+ ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges",
+ NULL, 3, 0, &args);
+ if (ret)
+ printf("%s: 'gpio-ranges' not defined - using default!\n",
+ __func__);
+
+ uc_priv->gpio_count = ret == 0 ? args.args[2] : MXS_MAX_GPIO_PER_BANK;
+
+ debug("%s: %s: %d pins\n", __func__, uc_priv->bank_name,
+ uc_priv->gpio_count);
+
+ return 0;
+}
+
+static const struct udevice_id mxs_gpio_ids[] = {
+ { .compatible = "fsl,imx23-gpio" },
+ { .compatible = "fsl,imx28-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_mxs) = {
+ .name = "gpio_mxs",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mxs_ops,
+ .probe = mxs_gpio_probe,
+ .priv_auto_alloc_size = sizeof(struct mxs_gpio_priv),
+ .of_match = mxs_gpio_ids,
+};
+#endif /* CONFIG_DM_GPIO */
diff --git a/drivers/misc/imx8/scu_api.c b/drivers/misc/imx8/scu_api.c
index d9c4d5d784..031bc0048b 100644
--- a/drivers/misc/imx8/scu_api.c
+++ b/drivers/misc/imx8/scu_api.c
@@ -273,6 +273,34 @@ int sc_misc_otp_fuse_read(sc_ipc_t ipc, u32 word, u32 *val)
return 0;
}
+int sc_misc_get_temp(sc_ipc_t ipc, sc_rsrc_t resource, sc_misc_temp_t temp,
+ s16 *celsius, s8 *tenths)
+{
+ struct udevice *dev = gd->arch.scu_dev;
+ int size = sizeof(struct sc_rpc_msg_s);
+ struct sc_rpc_msg_s msg;
+ int ret;
+
+ RPC_VER(&msg) = SC_RPC_VERSION;
+ RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
+ RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_TEMP;
+ RPC_U16(&msg, 0U) = (u16)resource;
+ RPC_U8(&msg, 2U) = (u8)temp;
+ RPC_SIZE(&msg) = 2U;
+
+ ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
+ if (ret < 0)
+ return ret;
+
+ if (celsius)
+ *celsius = RPC_I16(&msg, 0U);
+
+ if (tenths)
+ *tenths = RPC_I8(&msg, 2U);
+
+ return 0;
+}
+
/* RM */
sc_bool_t sc_rm_is_memreg_owned(sc_ipc_t ipc, sc_rm_mr_t mr)
{
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
index f3d687ae80..350812a04b 100644
--- a/drivers/mmc/mmc_spi.c
+++ b/drivers/mmc/mmc_spi.c
@@ -84,7 +84,7 @@ static int mmc_spi_sendcmd(struct udevice *dev,
cmdo[4] = cmdarg >> 8;
cmdo[5] = cmdarg;
cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
- ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, 0);
+ ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, SPI_XFER_BEGIN);
if (ret)
return ret;
@@ -360,6 +360,8 @@ static int dm_mmc_spi_request(struct udevice *dev, struct mmc_cmd *cmd,
}
done:
+ dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
+
dm_spi_release_bus(dev);
return ret;
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
index b93d77a395..a41b9620d0 100644
--- a/drivers/mtd/nand/raw/mxs_nand.c
+++ b/drivers/mtd/nand/raw/mxs_nand.c
@@ -25,7 +25,7 @@
#include <asm/mach-imx/regs-bch.h>
#include <asm/mach-imx/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
-#include "mxs_nand.h"
+#include <mxs_nand.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
diff --git a/drivers/mtd/nand/raw/mxs_nand.h b/drivers/mtd/nand/raw/mxs_nand.h
deleted file mode 100644
index 4bd65cded9..0000000000
--- a/drivers/mtd/nand/raw/mxs_nand.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * NXP GPMI NAND flash driver
- *
- * Copyright (C) 2018 Toradex
- * Authors:
- * Stefan Agner <stefan.agner@toradex.com>
- */
-
-#include <linux/mtd/mtd.h>
-#include <asm/cache.h>
-#include <nand.h>
-#include <asm/mach-imx/dma.h>
-
-/**
- * @gf_len: The length of Galois Field. (e.g., 13 or 14)
- * @ecc_strength: A number that describes the strength of the ECC
- * algorithm.
- * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
- * the first chunk in the page includes both data and
- * metadata, so it's a bit larger than this value.
- * @ecc_chunk_count: The number of ECC chunks in the page,
- * @block_mark_byte_offset: The byte offset in the ECC-based page view at
- * which the underlying physical block mark appears.
- * @block_mark_bit_offset: The bit offset into the ECC-based page view at
- * which the underlying physical block mark appears.
- */
-struct bch_geometry {
- unsigned int gf_len;
- unsigned int ecc_strength;
- unsigned int ecc_chunk_size;
- unsigned int ecc_chunk_count;
- unsigned int block_mark_byte_offset;
- unsigned int block_mark_bit_offset;
-};
-
-struct mxs_nand_info {
- struct nand_chip chip;
- struct udevice *dev;
- unsigned int max_ecc_strength_supported;
- bool use_minimum_ecc;
- int cur_chip;
-
- uint32_t cmd_queue_len;
- uint32_t data_buf_size;
- struct bch_geometry bch_geometry;
-
- uint8_t *cmd_buf;
- uint8_t *data_buf;
- uint8_t *oob_buf;
-
- uint8_t marking_block_bad;
- uint8_t raw_oob_mode;
-
- struct mxs_gpmi_regs *gpmi_regs;
- struct mxs_bch_regs *bch_regs;
-
- /* Functions with altered behaviour */
- int (*hooked_read_oob)(struct mtd_info *mtd,
- loff_t from, struct mtd_oob_ops *ops);
- int (*hooked_write_oob)(struct mtd_info *mtd,
- loff_t to, struct mtd_oob_ops *ops);
- int (*hooked_block_markbad)(struct mtd_info *mtd,
- loff_t ofs);
-
- /* DMA descriptors */
- struct mxs_dma_desc **desc;
- uint32_t desc_index;
-};
-
-int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
-int mxs_nand_init_spl(struct nand_chip *nand);
-int mxs_nand_setup_ecc(struct mtd_info *mtd);
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c
index 44dec5dedf..8ad7d618c6 100644
--- a/drivers/mtd/nand/raw/mxs_nand_dt.c
+++ b/drivers/mtd/nand/raw/mxs_nand_dt.c
@@ -15,7 +15,7 @@
#include <linux/ioport.h>
#include <linux/printk.h>
-#include "mxs_nand.h"
+#include <mxs_nand.h>
struct mxs_nand_dt_data {
unsigned int max_ecc_strength_supported;
diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
index ee7d9cb957..975a91a37d 100644
--- a/drivers/mtd/nand/raw/mxs_nand_spl.c
+++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
@@ -6,7 +6,7 @@
#include <common.h>
#include <nand.h>
#include <malloc.h>
-#include "mxs_nand.h"
+#include <mxs_nand.h>
static struct mtd_info *mtd;
static struct nand_chip nand_chip;
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index ec929760ee..a3920ba520 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -163,11 +163,15 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("n25q128a13", 0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
{ INFO("n25q256a", 0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("n25q256ax1", 0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { INFO6("mt25qu512a", 0x20bb20, 0x104400, 64 * 1024, 1024,
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("n25q512a", 0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("n25q512ax3", 0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("n25q00", 0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
+ { INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_4B_OPCODES) },
+ { INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_4B_OPCODES) },
#endif
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
/* Spansion/Cypress -- single (large) sector size only, at least
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 883b849b78..0a1d228a88 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -235,7 +235,7 @@ config FEC_MXC_MDIO_BASE
config FEC_MXC
bool "FEC Ethernet controller"
- depends on MX5 || MX6 || MX7 || IMX8 || VF610
+ depends on MX28 || MX5 || MX6 || MX7 || IMX8 || VF610
help
This driver supports the 10/100 Fast Ethernet controller for
NXP i.MX processors.
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
index d7c080943a..96e3ad9a1a 100644
--- a/drivers/net/fec_mxc.c
+++ b/drivers/net/fec_mxc.c
@@ -1485,6 +1485,7 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev)
}
static const struct udevice_id fecmxc_ids[] = {
+ { .compatible = "fsl,imx28-fec" },
{ .compatible = "fsl,imx6q-fec" },
{ .compatible = "fsl,imx6sl-fec" },
{ .compatible = "fsl,imx6sx-fec" },
diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
index 61f93be42d..f2e67ca231 100644
--- a/drivers/pinctrl/nxp/Kconfig
+++ b/drivers/pinctrl/nxp/Kconfig
@@ -89,6 +89,16 @@ config PINCTRL_IMX8M
only parses the 'fsl,pins' property and configure related
registers.
+config PINCTRL_MXS
+ bool "NXP MXS pinctrl driver"
+ depends on ARCH_MX28 && PINCTRL_FULL
+ help
+ Say Y here to enable the i.MX mxs pinctrl driver
+
+ This option provides a simple pinctrl driver for i.MX mxs SoC
+ familiy, e.g. i.MX28. This feature depends on device tree
+ configuration.
+
config PINCTRL_VYBRID
bool "Vybrid (vf610) pinctrl driver"
depends on ARCH_VF610 && PINCTRL_FULL
diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile
index b340d9448a..b86448aac9 100644
--- a/drivers/pinctrl/nxp/Makefile
+++ b/drivers/pinctrl/nxp/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o
obj-$(CONFIG_PINCTRL_IMX_SCU) += pinctrl-scu.o
obj-$(CONFIG_PINCTRL_IMX8) += pinctrl-imx8.o
obj-$(CONFIG_PINCTRL_IMX8M) += pinctrl-imx8m.o
+obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_VYBRID) += pinctrl-vf610.o
diff --git a/drivers/pinctrl/nxp/pinctrl-mxs.c b/drivers/pinctrl/nxp/pinctrl-mxs.c
new file mode 100644
index 0000000000..6f6ca84674
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-mxs.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include "pinctrl-mxs.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct mxs_pinctrl_priv {
+ void __iomem *base;
+ const struct mxs_regs *regs;
+};
+
+static unsigned long mxs_dt_node_to_map(struct udevice *conf)
+{
+ unsigned long config = 0;
+ int ret;
+ u32 val;
+
+ ret = dev_read_u32(conf, "fsl,drive-strength", &val);
+ if (!ret)
+ config = val | MA_PRESENT;
+
+ ret = dev_read_u32(conf, "fsl,voltage", &val);
+ if (!ret)
+ config |= val << VOL_SHIFT | VOL_PRESENT;
+
+ ret = dev_read_u32(conf, "fsl,pull-up", &val);
+ if (!ret)
+ config |= val << PULL_SHIFT | PULL_PRESENT;
+
+ return config;
+}
+
+static int mxs_pinctrl_set_mux(struct udevice *dev, u32 val, int bank, int pin)
+{
+ struct mxs_pinctrl_priv *iomux = dev_get_priv(dev);
+ int muxsel = MUXID_TO_MUXSEL(val), shift;
+ void __iomem *reg;
+
+ reg = iomux->base + iomux->regs->muxsel;
+ reg += bank * 0x20 + pin / 16 * 0x10;
+ shift = pin % 16 * 2;
+
+ mxs_pinctrl_rmwl(muxsel, 0x3, shift, reg);
+ debug(" mux %d,", muxsel);
+
+ return 0;
+}
+
+static int mxs_pinctrl_set_state(struct udevice *dev, struct udevice *conf)
+{
+ struct mxs_pinctrl_priv *iomux = dev_get_priv(dev);
+ u32 *pin_data, val, ma, vol, pull;
+ int npins, size, i, ret;
+ unsigned long config;
+
+ debug("\n%s: set state: %s\n", __func__, conf->name);
+
+ size = dev_read_size(conf, "fsl,pinmux-ids");
+ if (size < 0)
+ return size;
+
+ if (!size || size % sizeof(int)) {
+ dev_err(dev, "Invalid fsl,pinmux-ids property in %s\n",
+ conf->name);
+ return -EINVAL;
+ }
+
+ npins = size / sizeof(int);
+
+ pin_data = devm_kzalloc(dev, size, 0);
+ if (!pin_data)
+ return -ENOMEM;
+
+ ret = dev_read_u32_array(conf, "fsl,pinmux-ids", pin_data, npins);
+ if (ret) {
+ dev_err(dev, "Error reading pin data.\n");
+ devm_kfree(dev, pin_data);
+ return -EINVAL;
+ }
+
+ config = mxs_dt_node_to_map(conf);
+
+ ma = CONFIG_TO_MA(config);
+ vol = CONFIG_TO_VOL(config);
+ pull = CONFIG_TO_PULL(config);
+
+ for (i = 0; i < npins; i++) {
+ int pinid, bank, pin, shift;
+ void __iomem *reg;
+
+ val = pin_data[i];
+
+ pinid = MUXID_TO_PINID(val);
+ bank = PINID_TO_BANK(pinid);
+ pin = PINID_TO_PIN(pinid);
+
+ debug("(val: 0x%x) pin %d,", val, pinid);
+ /* Setup pinmux */
+ mxs_pinctrl_set_mux(dev, val, bank, pin);
+
+ debug(" ma: %d, vol: %d, pull: %d\n", ma, vol, pull);
+
+ /* drive */
+ reg = iomux->base + iomux->regs->drive;
+ reg += bank * 0x40 + pin / 8 * 0x10;
+
+ /* mA */
+ if (config & MA_PRESENT) {
+ shift = pin % 8 * 4;
+ mxs_pinctrl_rmwl(ma, 0x3, shift, reg);
+ }
+
+ /* vol */
+ if (config & VOL_PRESENT) {
+ shift = pin % 8 * 4 + 2;
+ if (vol)
+ writel(1 << shift, reg + SET);
+ else
+ writel(1 << shift, reg + CLR);
+ }
+
+ /* pull */
+ if (config & PULL_PRESENT) {
+ reg = iomux->base + iomux->regs->pull;
+ reg += bank * 0x10;
+ shift = pin;
+ if (pull)
+ writel(1 << shift, reg + SET);
+ else
+ writel(1 << shift, reg + CLR);
+ }
+ }
+
+ devm_kfree(dev, pin_data);
+ return 0;
+}
+
+static struct pinctrl_ops mxs_pinctrl_ops = {
+ .set_state = mxs_pinctrl_set_state,
+};
+
+static int mxs_pinctrl_probe(struct udevice *dev)
+{
+ struct mxs_pinctrl_priv *iomux = dev_get_priv(dev);
+
+ iomux->base = dev_read_addr_ptr(dev);
+ iomux->regs = (struct mxs_regs *)dev_get_driver_data(dev);
+
+ return 0;
+}
+
+static const struct mxs_regs imx23_regs = {
+ .muxsel = 0x100,
+ .drive = 0x200,
+ .pull = 0x400,
+};
+
+static const struct mxs_regs imx28_regs = {
+ .muxsel = 0x100,
+ .drive = 0x300,
+ .pull = 0x600,
+};
+
+static const struct udevice_id mxs_pinctrl_match[] = {
+ { .compatible = "fsl,imx23-pinctrl", .data = (ulong)&imx23_regs },
+ { .compatible = "fsl,imx28-pinctrl", .data = (ulong)&imx28_regs },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(mxs_pinctrl) = {
+ .name = "mxs-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = of_match_ptr(mxs_pinctrl_match),
+ .probe = mxs_pinctrl_probe,
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+ .bind = dm_scan_fdt_dev,
+#endif
+ .priv_auto_alloc_size = sizeof(struct mxs_pinctrl_priv),
+ .ops = &mxs_pinctrl_ops,
+};
diff --git a/drivers/pinctrl/nxp/pinctrl-mxs.h b/drivers/pinctrl/nxp/pinctrl-mxs.h
new file mode 100644
index 0000000000..a398e43cbe
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-mxs.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ */
+
+#ifndef __PINCTRL_MXS_H
+#define __PINCTRL_MXS_H
+
+#include <dm/pinctrl.h>
+
+#define SET 0x4
+#define CLR 0x8
+#define TOG 0xc
+
+#define MXS_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin)
+#define PINID(bank, pin) ((bank) * 32 + (pin))
+
+/*
+ * pinmux-id bit field definitions
+ *
+ * bank: 15..12 (4)
+ * pin: 11..4 (8)
+ * muxsel: 3..0 (4)
+ */
+#define MUXID_TO_PINID(m) PINID((m) >> 12 & 0xf, (m) >> 4 & 0xff)
+#define MUXID_TO_MUXSEL(m) ((m) & 0xf)
+
+#define PINID_TO_BANK(p) ((p) >> 5)
+#define PINID_TO_PIN(p) ((p) % 32)
+
+/*
+ * pin config bit field definitions
+ *
+ * pull-up: 6..5 (2)
+ * voltage: 4..3 (2)
+ * mA: 2..0 (3)
+ *
+ * MSB of each field is presence bit for the config.
+ */
+#define PULL_PRESENT (1 << 6)
+#define PULL_SHIFT 5
+#define VOL_PRESENT (1 << 4)
+#define VOL_SHIFT 3
+#define MA_PRESENT (1 << 2)
+#define MA_SHIFT 0
+#define CONFIG_TO_PULL(c) ((c) >> PULL_SHIFT & 0x1)
+#define CONFIG_TO_VOL(c) ((c) >> VOL_SHIFT & 0x1)
+#define CONFIG_TO_MA(c) ((c) >> MA_SHIFT & 0x3)
+
+struct mxs_regs {
+ u16 muxsel;
+ u16 drive;
+ u16 pull;
+};
+
+static inline void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift,
+ void __iomem *reg)
+{
+ clrsetbits_le32(reg, mask << shift, value << shift);
+}
+#endif /* __PINCTRL_MXS_H */
diff --git a/drivers/power/pmic/bd71837.c b/drivers/power/pmic/bd71837.c
index 24d9f7fab7..e292d42a8c 100644
--- a/drivers/power/pmic/bd71837.c
+++ b/drivers/power/pmic/bd71837.c
@@ -3,6 +3,8 @@
* Copyright 2018 NXP
*/
+#define DEBUG
+
#include <common.h>
#include <errno.h>
#include <dm.h>
@@ -15,15 +17,15 @@ DECLARE_GLOBAL_DATA_PTR;
static const struct pmic_child_info pmic_children_info[] = {
/* buck */
- { .prefix = "b", .driver = BD71837_REGULATOR_DRIVER},
+ { .prefix = "b", .driver = BD718XX_REGULATOR_DRIVER},
/* ldo */
- { .prefix = "l", .driver = BD71837_REGULATOR_DRIVER},
+ { .prefix = "l", .driver = BD718XX_REGULATOR_DRIVER},
{ },
};
static int bd71837_reg_count(struct udevice *dev)
{
- return BD71837_REG_NUM;
+ return BD718XX_MAX_REGISTER - 1;
}
static int bd71837_write(struct udevice *dev, uint reg, const uint8_t *buff,
@@ -54,7 +56,7 @@ static int bd71837_bind(struct udevice *dev)
regulators_node = dev_read_subnode(dev, "regulators");
if (!ofnode_valid(regulators_node)) {
- debug("%s: %s regulators subnode not found!", __func__,
+ debug("%s: %s regulators subnode not found!\n", __func__,
dev->name);
return -ENXIO;
}
@@ -69,6 +71,24 @@ static int bd71837_bind(struct udevice *dev)
return 0;
}
+static int bd718x7_probe(struct udevice *dev)
+{
+ int ret;
+ uint8_t mask = BD718XX_REGLOCK_PWRSEQ | BD718XX_REGLOCK_VREG;
+
+ /* Unlock the PMIC regulator control before probing the children */
+ ret = pmic_clrsetbits(dev, BD718XX_REGLOCK, mask, 0);
+ if (ret) {
+ debug("%s: %s Failed to unlock regulator control\n", __func__,
+ dev->name);
+ return ret;
+ }
+ debug("%s: '%s' - BD718x7 PMIC registers unlocked\n", __func__,
+ dev->name);
+
+ return 0;
+}
+
static struct dm_pmic_ops bd71837_ops = {
.reg_count = bd71837_reg_count,
.read = bd71837_read,
@@ -76,7 +96,8 @@ static struct dm_pmic_ops bd71837_ops = {
};
static const struct udevice_id bd71837_ids[] = {
- { .compatible = "rohm,bd71837", .data = 0x4b, },
+ { .compatible = "rohm,bd71837", .data = ROHM_CHIP_TYPE_BD71837, },
+ { .compatible = "rohm,bd71847", .data = ROHM_CHIP_TYPE_BD71847, },
{ }
};
@@ -85,5 +106,6 @@ U_BOOT_DRIVER(pmic_bd71837) = {
.id = UCLASS_PMIC,
.of_match = bd71837_ids,
.bind = bd71837_bind,
+ .probe = bd718x7_probe,
.ops = &bd71837_ops,
};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 337e9e7471..9aa00fad42 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -43,6 +43,23 @@ config REGULATOR_AS3722
but does not yet support change voltages. Currently this must be
done using direct register writes to the PMIC.
+config DM_REGULATOR_BD71837
+ bool "Enable Driver Model for ROHM BD71837/BD71847 regulators"
+ depends on DM_REGULATOR && DM_PMIC_BD71837
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on ROHM BD71837 and BD71847 PMICs.
+ BD71837 contains 8 bucks and 7 LDOS. BD71847 is reduced version
+ containing 6 bucks and 6 LDOs. The driver implements get/set api for
+ value and enable.
+
+config SPL_DM_REGULATOR_BD71837
+ bool "Enable Driver Model for ROHM BD71837/BD71847 regulators in SPL"
+ depends on DM_REGULATOR_BD71837
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on ROHM BD71837 and BD71847 in SPL.
+
config DM_REGULATOR_PFUZE100
bool "Enable Driver Model for REGULATOR PFUZE100"
depends on DM_REGULATOR && DM_PMIC_PFUZE100
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index e728b73aee..6a3d4bbee4 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o
+obj-$(CONFIG_$(SPL_)DM_REGULATOR_BD71837) += bd71837.o
obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o
obj-$(CONFIG_$(SPL_)DM_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_$(SPL_)DM_REGULATOR_COMMON) += regulator_common.o
diff --git a/drivers/power/regulator/bd71837.c b/drivers/power/regulator/bd71837.c
new file mode 100644
index 0000000000..575429aa2d
--- /dev/null
+++ b/drivers/power/regulator/bd71837.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 ROHM Semiconductors
+ *
+ * ROHM BD71837 regulator driver
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <power/bd71837.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+#define HW_STATE_CONTROL 0
+#define DEBUG
+
+/**
+ * struct bd71837_vrange - describe linear range of voltages
+ *
+ * @min_volt: smallest voltage in range
+ * @step: how much voltage changes at each selector step
+ * @min_sel: smallest selector in the range
+ * @max_sel: maximum selector in the range
+ * @rangeval: register value used to select this range if selectible
+ * ranges are supported
+ */
+struct bd71837_vrange {
+ unsigned int min_volt;
+ unsigned int step;
+ u8 min_sel;
+ u8 max_sel;
+ u8 rangeval;
+};
+
+/**
+ * struct bd71837_platdata - describe regulator control registers
+ *
+ * @name: name of the regulator. Used for matching the dt-entry
+ * @enable_reg: register address used to enable/disable regulator
+ * @enablemask: register mask used to enable/disable regulator
+ * @volt_reg: register address used to configure regulator voltage
+ * @volt_mask: register mask used to configure regulator voltage
+ * @ranges: pointer to ranges of regulator voltages and matching register
+ * values
+ * @numranges: number of voltage ranges pointed by ranges
+ * @rangemask: mask for selecting used ranges if multiple ranges are supported
+ * @sel_mask: bit to toggle in order to transfer the register control to SW
+ * @dvs: whether the voltage can be changed when regulator is enabled
+ */
+struct bd71837_platdata {
+ const char *name;
+ u8 enable_reg;
+ u8 enablemask;
+ u8 volt_reg;
+ u8 volt_mask;
+ struct bd71837_vrange *ranges;
+ unsigned int numranges;
+ u8 rangemask;
+ u8 sel_mask;
+ bool dvs;
+};
+
+#define BD_RANGE(_min, _vstep, _sel_low, _sel_hi, _range_sel) \
+{ \
+ .min_volt = (_min), .step = (_vstep), .min_sel = (_sel_low), \
+ .max_sel = (_sel_hi), .rangeval = (_range_sel) \
+}
+
+#define BD_DATA(_name, enreg, enmask, vreg, vmask, _range, rmask, _dvs, sel) \
+{ \
+ .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \
+ .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \
+ .numranges = ARRAY_SIZE(_range), .rangemask = (rmask), .dvs = (_dvs), \
+ .sel_mask = (sel) \
+}
+
+static struct bd71837_vrange dvs_buck_vranges[] = {
+ BD_RANGE(700000, 10000, 0, 0x3c, 0),
+ BD_RANGE(1300000, 0, 0x3d, 0x3f, 0),
+};
+
+static struct bd71837_vrange bd71847_buck3_vranges[] = {
+ BD_RANGE(700000, 100000, 0x00, 0x03, 0),
+ BD_RANGE(1050000, 50000, 0x04, 0x05, 0),
+ BD_RANGE(1200000, 150000, 0x06, 0x07, 0),
+ BD_RANGE(550000, 50000, 0x0, 0x7, 0x40),
+ BD_RANGE(675000, 100000, 0x0, 0x3, 0x80),
+ BD_RANGE(1025000, 50000, 0x4, 0x5, 0x80),
+ BD_RANGE(1175000, 150000, 0x6, 0x7, 0x80),
+};
+
+static struct bd71837_vrange bd71847_buck4_vranges[] = {
+ BD_RANGE(3000000, 100000, 0x00, 0x03, 0),
+ BD_RANGE(2600000, 100000, 0x00, 0x03, 40),
+};
+
+static struct bd71837_vrange bd71837_buck5_vranges[] = {
+ BD_RANGE(700000, 100000, 0, 0x3, 0),
+ BD_RANGE(1050000, 50000, 0x04, 0x05, 0),
+ BD_RANGE(1200000, 150000, 0x06, 0x07, 0),
+ BD_RANGE(675000, 100000, 0x0, 0x3, 0x80),
+ BD_RANGE(1025000, 50000, 0x04, 0x05, 0x80),
+ BD_RANGE(1175000, 150000, 0x06, 0x07, 0x80),
+};
+
+static struct bd71837_vrange bd71837_buck6_vranges[] = {
+ BD_RANGE(3000000, 100000, 0x00, 0x03, 0),
+};
+
+static struct bd71837_vrange nodvs_buck3_vranges[] = {
+ BD_RANGE(1605000, 90000, 0, 1, 0),
+ BD_RANGE(1755000, 45000, 2, 4, 0),
+ BD_RANGE(1905000, 45000, 5, 7, 0),
+};
+
+static struct bd71837_vrange nodvs_buck4_vranges[] = {
+ BD_RANGE(800000, 10000, 0x00, 0x3C, 0),
+};
+
+static struct bd71837_vrange ldo1_vranges[] = {
+ BD_RANGE(3000000, 100000, 0x00, 0x03, 0),
+ BD_RANGE(1600000, 100000, 0x00, 0x03, 0x20),
+};
+
+static struct bd71837_vrange ldo2_vranges[] = {
+ BD_RANGE(900000, 0, 0, 0, 0),
+ BD_RANGE(800000, 0, 1, 1, 0),
+};
+
+static struct bd71837_vrange ldo3_vranges[] = {
+ BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
+};
+
+static struct bd71837_vrange ldo4_vranges[] = {
+ BD_RANGE(900000, 100000, 0x00, 0x09, 0),
+};
+
+static struct bd71837_vrange bd71837_ldo5_vranges[] = {
+ BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
+};
+
+static struct bd71837_vrange bd71847_ldo5_vranges[] = {
+ BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
+ BD_RANGE(800000, 100000, 0x00, 0x0f, 0x20),
+};
+
+static struct bd71837_vrange ldo6_vranges[] = {
+ BD_RANGE(900000, 100000, 0x00, 0x09, 0),
+};
+
+static struct bd71837_vrange ldo7_vranges[] = {
+ BD_RANGE(1800000, 100000, 0x00, 0x0f, 0),
+};
+
+/*
+ * We use enable mask 'HW_STATE_CONTROL' to indicate that this regulator
+ * must not be enabled or disabled by SW. The typical use-case for BD71837
+ * is powering NXP i.MX8. In this use-case we (for now) only allow control
+ * for BUCK3 and BUCK4 which are not boot critical.
+ */
+static struct bd71837_platdata bd71837_reg_data[] = {
+/* Bucks 1-4 which support dynamic voltage scaling */
+ BD_DATA("BUCK1", BD718XX_BUCK1_CTRL, HW_STATE_CONTROL,
+ BD718XX_BUCK1_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
+ true, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK2", BD718XX_BUCK2_CTRL, HW_STATE_CONTROL,
+ BD718XX_BUCK2_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
+ true, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK3", BD71837_BUCK3_CTRL, BD718XX_BUCK_EN,
+ BD71837_BUCK3_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
+ true, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK4", BD71837_BUCK4_CTRL, BD718XX_BUCK_EN,
+ BD71837_BUCK4_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
+ true, BD718XX_BUCK_SEL),
+/* Bucks 5-8 which do not support dynamic voltage scaling */
+ BD_DATA("BUCK5", BD718XX_1ST_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_1ST_NODVS_BUCK_VOLT, BD718XX_1ST_NODVS_BUCK_MASK,
+ bd71837_buck5_vranges, 0x80, false, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK6", BD718XX_2ND_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_2ND_NODVS_BUCK_VOLT, BD71837_BUCK6_MASK,
+ bd71837_buck6_vranges, 0, false, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK7", BD718XX_3RD_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_3RD_NODVS_BUCK_VOLT, BD718XX_3RD_NODVS_BUCK_MASK,
+ nodvs_buck3_vranges, 0, false, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK8", BD718XX_4TH_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_4TH_NODVS_BUCK_VOLT, BD718XX_4TH_NODVS_BUCK_MASK,
+ nodvs_buck4_vranges, 0, false, BD718XX_BUCK_SEL),
+/* LDOs */
+ BD_DATA("LDO1", BD718XX_LDO1_VOLT, HW_STATE_CONTROL, BD718XX_LDO1_VOLT,
+ BD718XX_LDO1_MASK, ldo1_vranges, 0x20, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO2", BD718XX_LDO2_VOLT, HW_STATE_CONTROL, BD718XX_LDO2_VOLT,
+ BD718XX_LDO2_MASK, ldo2_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO3", BD718XX_LDO3_VOLT, HW_STATE_CONTROL, BD718XX_LDO3_VOLT,
+ BD718XX_LDO3_MASK, ldo3_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO4", BD718XX_LDO4_VOLT, HW_STATE_CONTROL, BD718XX_LDO4_VOLT,
+ BD718XX_LDO4_MASK, ldo4_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO5", BD718XX_LDO5_VOLT, HW_STATE_CONTROL, BD718XX_LDO5_VOLT,
+ BD71837_LDO5_MASK, bd71837_ldo5_vranges, 0, false,
+ BD718XX_LDO_SEL),
+ BD_DATA("LDO6", BD718XX_LDO6_VOLT, HW_STATE_CONTROL, BD718XX_LDO6_VOLT,
+ BD718XX_LDO6_MASK, ldo6_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO7", BD71837_LDO7_VOLT, HW_STATE_CONTROL, BD71837_LDO7_VOLT,
+ BD71837_LDO7_MASK, ldo7_vranges, 0, false, BD718XX_LDO_SEL),
+};
+
+static struct bd71837_platdata bd71847_reg_data[] = {
+/* Bucks 1 and 2 which support dynamic voltage scaling */
+ BD_DATA("BUCK1", BD718XX_BUCK1_CTRL, HW_STATE_CONTROL,
+ BD718XX_BUCK1_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
+ true, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK2", BD718XX_BUCK2_CTRL, HW_STATE_CONTROL,
+ BD718XX_BUCK2_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0,
+ true, BD718XX_BUCK_SEL),
+/* Bucks 3-6 which do not support dynamic voltage scaling */
+ BD_DATA("BUCK3", BD718XX_1ST_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_1ST_NODVS_BUCK_VOLT, BD718XX_1ST_NODVS_BUCK_MASK,
+ bd71847_buck3_vranges, 0xc0, false, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK4", BD718XX_2ND_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_2ND_NODVS_BUCK_VOLT, BD71837_BUCK6_MASK,
+ bd71847_buck4_vranges, 0x40, false, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK5", BD718XX_3RD_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_3RD_NODVS_BUCK_VOLT, BD718XX_3RD_NODVS_BUCK_MASK,
+ nodvs_buck3_vranges, 0, false, BD718XX_BUCK_SEL),
+ BD_DATA("BUCK6", BD718XX_4TH_NODVS_BUCK_CTRL, HW_STATE_CONTROL,
+ BD718XX_4TH_NODVS_BUCK_VOLT, BD718XX_4TH_NODVS_BUCK_MASK,
+ nodvs_buck4_vranges, 0, false, BD718XX_BUCK_SEL),
+/* LDOs */
+ BD_DATA("LDO1", BD718XX_LDO1_VOLT, HW_STATE_CONTROL, BD718XX_LDO1_VOLT,
+ BD718XX_LDO1_MASK, ldo1_vranges, 0x20, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO2", BD718XX_LDO2_VOLT, HW_STATE_CONTROL, BD718XX_LDO2_VOLT,
+ BD718XX_LDO2_MASK, ldo2_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO3", BD718XX_LDO3_VOLT, HW_STATE_CONTROL, BD718XX_LDO3_VOLT,
+ BD718XX_LDO3_MASK, ldo3_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO4", BD718XX_LDO4_VOLT, HW_STATE_CONTROL, BD718XX_LDO4_VOLT,
+ BD718XX_LDO4_MASK, ldo4_vranges, 0, false, BD718XX_LDO_SEL),
+ BD_DATA("LDO5", BD718XX_LDO5_VOLT, HW_STATE_CONTROL, BD718XX_LDO5_VOLT,
+ BD71847_LDO5_MASK, bd71847_ldo5_vranges, 0x20, false,
+ BD718XX_LDO_SEL),
+ BD_DATA("LDO6", BD718XX_LDO6_VOLT, HW_STATE_CONTROL, BD718XX_LDO6_VOLT,
+ BD718XX_LDO6_MASK, ldo6_vranges, 0, false, BD718XX_LDO_SEL),
+};
+
+static int vrange_find_value(struct bd71837_vrange *r, unsigned int sel,
+ unsigned int *val)
+{
+ if (!val || sel < r->min_sel || sel > r->max_sel)
+ return -EINVAL;
+
+ *val = r->min_volt + r->step * (sel - r->min_sel);
+ return 0;
+}
+
+static int vrange_find_selector(struct bd71837_vrange *r, int val,
+ unsigned int *sel)
+{
+ int ret = -EINVAL;
+ int num_vals = r->max_sel - r->min_sel + 1;
+
+ if (val >= r->min_volt &&
+ val <= r->min_volt + r->step * (num_vals - 1)) {
+ if (r->step) {
+ *sel = r->min_sel + ((val - r->min_volt) / r->step);
+ ret = 0;
+ } else {
+ *sel = r->min_sel;
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int bd71837_get_enable(struct udevice *dev)
+{
+ int val;
+ struct bd71837_platdata *plat = dev_get_platdata(dev);
+
+ /*
+ * boot critical regulators on bd71837 must not be controlled by sw
+ * due to the 'feature' which leaves power rails down if bd71837 is
+ * reseted to snvs state. hence we can't get the state here.
+ *
+ * if we are alive it means we probably are on run state and
+ * if the regulator can't be controlled we can assume it is
+ * enabled.
+ */
+ if (plat->enablemask == HW_STATE_CONTROL)
+ return 1;
+
+ val = pmic_reg_read(dev->parent, plat->enable_reg);
+ if (val < 0)
+ return val;
+
+ return (val & plat->enablemask);
+}
+
+static int bd71837_set_enable(struct udevice *dev, bool enable)
+{
+ int val = 0;
+ struct bd71837_platdata *plat = dev_get_platdata(dev);
+
+ /*
+ * boot critical regulators on bd71837 must not be controlled by sw
+ * due to the 'feature' which leaves power rails down if bd71837 is
+ * reseted to snvs state. Hence we can't set the state here.
+ */
+ if (plat->enablemask == HW_STATE_CONTROL)
+ return -EINVAL;
+
+ if (enable)
+ val = plat->enablemask;
+
+ return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask,
+ val);
+}
+
+static int bd71837_set_value(struct udevice *dev, int uvolt)
+{
+ unsigned int sel;
+ unsigned int range;
+ int i;
+ int found = 0;
+ struct bd71837_platdata *plat = dev_get_platdata(dev);
+
+ /*
+ * An under/overshooting may occur if voltage is changed for other
+ * regulators but buck 1,2,3 or 4 when regulator is enabled. Prevent
+ * change to protect the HW
+ */
+ if (!plat->dvs)
+ if (bd71837_get_enable(dev)) {
+ pr_err("Only DVS bucks can be changed when enabled\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < plat->numranges; i++) {
+ struct bd71837_vrange *r = &plat->ranges[i];
+
+ found = !vrange_find_selector(r, uvolt, &sel);
+ if (found) {
+ unsigned int tmp;
+
+ /*
+ * We require exactly the requested value to be
+ * supported - this can be changed later if needed
+ */
+ range = r->rangeval;
+ found = !vrange_find_value(r, sel, &tmp);
+ if (found && tmp == uvolt)
+ break;
+ found = 0;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ sel <<= ffs(plat->volt_mask) - 1;
+
+ if (plat->rangemask)
+ sel |= range;
+
+ return pmic_clrsetbits(dev->parent, plat->volt_reg, plat->volt_mask |
+ plat->rangemask, sel);
+}
+
+static int bd71837_get_value(struct udevice *dev)
+{
+ unsigned int reg, range;
+ unsigned int tmp;
+ struct bd71837_platdata *plat = dev_get_platdata(dev);
+ int i;
+
+ reg = pmic_reg_read(dev->parent, plat->volt_reg);
+ if (((int)reg) < 0)
+ return reg;
+
+ range = reg & plat->rangemask;
+
+ reg &= plat->volt_mask;
+ reg >>= ffs(plat->volt_mask) - 1;
+
+ for (i = 0; i < plat->numranges; i++) {
+ struct bd71837_vrange *r = &plat->ranges[i];
+
+ if (plat->rangemask && ((plat->rangemask & range) !=
+ r->rangeval))
+ continue;
+
+ if (!vrange_find_value(r, reg, &tmp))
+ return tmp;
+ }
+
+ pr_err("Unknown voltage value read from pmic\n");
+
+ return -EINVAL;
+}
+
+static int bd71837_regulator_probe(struct udevice *dev)
+{
+ struct bd71837_platdata *plat = dev_get_platdata(dev);
+ int i, ret;
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ int type;
+ struct bd71837_platdata *init_data;
+ int data_amnt;
+
+ type = dev_get_driver_data(dev_get_parent(dev));
+
+ switch (type) {
+ case ROHM_CHIP_TYPE_BD71837:
+ init_data = bd71837_reg_data;
+ data_amnt = ARRAY_SIZE(bd71837_reg_data);
+ break;
+ case ROHM_CHIP_TYPE_BD71847:
+ init_data = bd71847_reg_data;
+ data_amnt = ARRAY_SIZE(bd71847_reg_data);
+ break;
+ default:
+ debug("Unknown PMIC type\n");
+ init_data = NULL;
+ data_amnt = 0;
+ break;
+ }
+
+ for (i = 0; i < data_amnt; i++) {
+ if (!strcmp(dev->name, init_data[i].name)) {
+ *plat = init_data[i];
+ if (plat->enablemask != HW_STATE_CONTROL) {
+ /*
+ * Take the regulator under SW control. Ensure
+ * the initial state matches dt flags and then
+ * write the SEL bit
+ */
+ uc_pdata = dev_get_uclass_platdata(dev);
+ ret = bd71837_set_enable(dev,
+ !!(uc_pdata->boot_on ||
+ uc_pdata->always_on));
+ if (ret)
+ return ret;
+
+ return pmic_clrsetbits(dev->parent,
+ plat->enable_reg,
+ plat->sel_mask,
+ plat->sel_mask);
+ }
+ return 0;
+ }
+ }
+
+ pr_err("Unknown regulator '%s'\n", dev->name);
+
+ return -ENOENT;
+}
+
+static const struct dm_regulator_ops bd71837_regulator_ops = {
+ .get_value = bd71837_get_value,
+ .set_value = bd71837_set_value,
+ .get_enable = bd71837_get_enable,
+ .set_enable = bd71837_set_enable,
+};
+
+U_BOOT_DRIVER(bd71837_regulator) = {
+ .name = BD718XX_REGULATOR_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &bd71837_regulator_ops,
+ .probe = bd71837_regulator_probe,
+ .platdata_auto_alloc_size = sizeof(struct bd71837_platdata),
+};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 2984b79766..1f36fc78fa 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -18,6 +18,11 @@ config PWM_EXYNOS
used. It provides 5 channels which can be independently
programmed. Channel 4 (the last) is normally used as a timer.
+config PWM_IMX
+ bool "Enable support for i.MX27 and later PWM"
+ help
+ This PWM is found i.MX27 and later i.MX SoCs.
+
config PWM_ROCKCHIP
bool "Enable support for the Rockchip PWM"
depends on DM_PWM
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 83c1bfa820..8d8f3e6f9f 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <div64.h>
+#include <dm.h>
#include <pwm.h>
#include <asm/arch/imx-regs.h>
#include <asm/io.h>
@@ -24,18 +25,12 @@ int pwm_init(int pwm_id, int div, int invert)
return 0;
}
-int pwm_config(int pwm_id, int duty_ns, int period_ns)
+int pwm_config_internal(struct pwm_regs *pwm, unsigned long period_cycles,
+ unsigned long duty_cycles, unsigned long prescale)
{
- struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
- unsigned long period_cycles, duty_cycles, prescale;
u32 cr;
- if (!pwm)
- return -1;
-
- pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
- &prescale);
-
+ writel(0, &pwm->ir);
cr = PWMCR_PRESCALER(prescale) |
PWMCR_DOZEEN | PWMCR_WAITEN |
PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH;
@@ -48,6 +43,20 @@ int pwm_config(int pwm_id, int duty_ns, int period_ns)
return 0;
}
+int pwm_config(int pwm_id, int duty_ns, int period_ns)
+{
+ struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
+ unsigned long period_cycles, duty_cycles, prescale;
+
+ if (!pwm)
+ return -1;
+
+ pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
+ &prescale);
+
+ return pwm_config_internal(pwm, period_cycles, duty_cycles, prescale);
+}
+
int pwm_enable(int pwm_id)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
@@ -68,3 +77,86 @@ void pwm_disable(int pwm_id)
clrbits_le32(&pwm->cr, PWMCR_EN);
}
+
+#if defined(CONFIG_DM_PWM)
+struct imx_pwm_priv {
+ struct pwm_regs *regs;
+ bool invert;
+};
+
+static int imx_pwm_set_invert(struct udevice *dev, uint channel,
+ bool polarity)
+{
+ struct imx_pwm_priv *priv = dev_get_priv(dev);
+
+ debug("%s: polarity=%u\n", __func__, polarity);
+ priv->invert = polarity;
+
+ return 0;
+}
+
+static int imx_pwm_set_config(struct udevice *dev, uint channel,
+ uint period_ns, uint duty_ns)
+{
+ struct imx_pwm_priv *priv = dev_get_priv(dev);
+ struct pwm_regs *regs = priv->regs;
+ unsigned long period_cycles, duty_cycles, prescale;
+
+ debug("%s: Config '%s' channel: %d\n", __func__, dev->name, channel);
+
+ pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
+ &prescale);
+
+ return pwm_config_internal(regs, period_cycles, duty_cycles, prescale);
+};
+
+static int imx_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
+{
+ struct imx_pwm_priv *priv = dev_get_priv(dev);
+ struct pwm_regs *regs = priv->regs;
+
+ debug("%s: Enable '%s' state: %d\n", __func__, dev->name, enable);
+
+ if (enable)
+ setbits_le32(&regs->cr, PWMCR_EN);
+ else
+ clrbits_le32(&regs->cr, PWMCR_EN);
+
+ return 0;
+};
+
+static int imx_pwm_ofdata_to_platdata(struct udevice *dev)
+{
+ struct imx_pwm_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct pwm_regs *)devfdt_get_addr(dev);
+
+ return 0;
+}
+
+static int imx_pwm_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static const struct pwm_ops imx_pwm_ops = {
+ .set_invert = imx_pwm_set_invert,
+ .set_config = imx_pwm_set_config,
+ .set_enable = imx_pwm_set_enable,
+};
+
+static const struct udevice_id imx_pwm_ids[] = {
+ { .compatible = "fsl,imx27-pwm" },
+ { }
+};
+
+U_BOOT_DRIVER(imx_pwm) = {
+ .name = "imx_pwm",
+ .id = UCLASS_PWM,
+ .of_match = imx_pwm_ids,
+ .ops = &imx_pwm_ops,
+ .ofdata_to_platdata = imx_pwm_ofdata_to_platdata,
+ .probe = imx_pwm_probe,
+ .priv_auto_alloc_size = sizeof(struct imx_pwm_priv),
+};
+#endif
diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c
index a435e68005..42abb96a26 100644
--- a/drivers/serial/serial_mxc.c
+++ b/drivers/serial/serial_mxc.c
@@ -342,6 +342,8 @@ static int mxc_serial_ofdata_to_platdata(struct udevice *dev)
}
static const struct udevice_id mxc_serial_ids[] = {
+ { .compatible = "fsl,imx21-uart" },
+ { .compatible = "fsl,imx53-uart" },
{ .compatible = "fsl,imx6sx-uart" },
{ .compatible = "fsl,imx6ul-uart" },
{ .compatible = "fsl,imx7d-uart" },
@@ -360,9 +362,7 @@ U_BOOT_DRIVER(serial_mxc) = {
#endif
.probe = mxc_serial_probe,
.ops = &mxc_serial_ops,
-#if !CONFIG_IS_ENABLED(OF_CONTROL)
.flags = DM_FLAG_PRE_RELOC,
-#endif
};
#endif
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index cc174dd036..f459c0a411 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -158,13 +158,14 @@ config MT7621_SPI
the SPI NOR flash on platforms embedding this Ralink / MediaTek
SPI core, like MT7621/7628/7688.
-config MTK_QSPI
- bool "Mediatek QSPI driver"
- imply SPI_FLASH_BAR
+config MTK_SNFI_SPI
+ bool "Mediatek SPI memory controller driver"
+ depends on SPI_MEM
help
- Enable the Mediatek QSPI driver. This driver can be
- used to access the SPI NOR flash on platforms embedding this
- Mediatek QSPI IP core.
+ Enable the Mediatek SPI memory controller driver. This driver is
+ originally based on the MediaTek SNFI IP core. It can only be
+ used to access SPI memory devices like SPI-NOR or SPI-NAND on
+ platforms embedding this IP core, like MT7622/M7629.
config MVEBU_A3700_SPI
bool "Marvell Armada 3700 SPI driver"
@@ -232,6 +233,14 @@ config SANDBOX_SPI
};
};
+config SPI_SIFIVE
+ bool "SiFive SPI driver"
+ help
+ This driver supports the SiFive SPI IP. If unsure say N.
+ Enable the SiFive SPI controller driver.
+
+ The SiFive SPI controller driver is found on various SiFive SoCs.
+
config SPI_SUNXI
bool "Allwinner SoC SPI controllers"
help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ab84122f08..ae4f2958f8 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o
obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o
obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
-obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o
+obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o
obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o
obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o
obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
@@ -50,6 +50,7 @@ obj-$(CONFIG_PL022_SPI) += pl022_spi.o
obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
+obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
obj-$(CONFIG_SPI_SUNXI) += spi-sunxi.o
obj-$(CONFIG_SH_SPI) += sh_spi.o
obj-$(CONFIG_SH_QSPI) += sh_qspi.o
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c
index 1598c4f698..41abe1996f 100644
--- a/drivers/spi/fsl_qspi.c
+++ b/drivers/spi/fsl_qspi.c
@@ -10,6 +10,7 @@
#include <spi.h>
#include <asm/io.h>
#include <linux/sizes.h>
+#include <linux/iopoll.h>
#include <dm.h>
#include <errno.h>
#include <watchdog.h>
@@ -150,20 +151,13 @@ static void qspi_write32(u32 flags, u32 *addr, u32 val)
static inline int is_controller_busy(const struct fsl_qspi_priv *priv)
{
u32 val;
- const u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK |
- QSPI_SR_IP_ACC_MASK;
- unsigned int retry = 5;
+ u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK |
+ QSPI_SR_IP_ACC_MASK;
- do {
- val = qspi_read32(priv->flags, &priv->regs->sr);
+ if (priv->flags & QSPI_FLAG_REGMAP_ENDIAN_BIG)
+ mask = (u32)cpu_to_be32(mask);
- if ((~val & mask) == mask)
- return 0;
-
- udelay(1);
- } while (--retry);
-
- return -ETIMEDOUT;
+ return readl_poll_timeout(&priv->regs->sr, val, !(val & mask), 1000);
}
/* QSPI support swapping the flash read/write data
diff --git a/drivers/spi/mtk_qspi.c b/drivers/spi/mtk_qspi.c
deleted file mode 100644
index b510733e92..0000000000
--- a/drivers/spi/mtk_qspi.c
+++ /dev/null
@@ -1,359 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2018 MediaTek, Inc.
- * Author : Guochun.Mao@mediatek.com
- */
-
-#include <common.h>
-#include <dm.h>
-#include <malloc.h>
-#include <spi.h>
-#include <asm/io.h>
-#include <linux/iopoll.h>
-#include <linux/ioport.h>
-
-/* Register Offset */
-struct mtk_qspi_regs {
- u32 cmd;
- u32 cnt;
- u32 rdsr;
- u32 rdata;
- u32 radr[3];
- u32 wdata;
- u32 prgdata[6];
- u32 shreg[10];
- u32 cfg[2];
- u32 shreg10;
- u32 mode_mon;
- u32 status[4];
- u32 flash_time;
- u32 flash_cfg;
- u32 reserved_0[3];
- u32 sf_time;
- u32 pp_dw_data;
- u32 reserved_1;
- u32 delsel_0[2];
- u32 intrstus;
- u32 intren;
- u32 reserved_2;
- u32 cfg3;
- u32 reserved_3;
- u32 chksum;
- u32 aaicmd;
- u32 wrprot;
- u32 radr3;
- u32 dual;
- u32 delsel_1[3];
-};
-
-struct mtk_qspi_platdata {
- fdt_addr_t reg_base;
- fdt_addr_t mem_base;
-};
-
-struct mtk_qspi_priv {
- struct mtk_qspi_regs *regs;
- unsigned long *mem_base;
- u8 op;
- u8 tx[3]; /* only record max 3 bytes paras, when it's address. */
- u32 txlen; /* dout buffer length - op code length */
- u8 *rx;
- u32 rxlen;
-};
-
-#define MTK_QSPI_CMD_POLLINGREG_US 500000
-#define MTK_QSPI_WRBUF_SIZE 256
-#define MTK_QSPI_COMMAND_ENABLE 0x30
-
-/* NOR flash controller commands */
-#define MTK_QSPI_RD_TRIGGER BIT(0)
-#define MTK_QSPI_READSTATUS BIT(1)
-#define MTK_QSPI_PRG_CMD BIT(2)
-#define MTK_QSPI_WR_TRIGGER BIT(4)
-#define MTK_QSPI_WRITESTATUS BIT(5)
-#define MTK_QSPI_AUTOINC BIT(7)
-
-#define MTK_QSPI_MAX_RX_TX_SHIFT 0x6
-#define MTK_QSPI_MAX_SHIFT 0x8
-
-#define MTK_QSPI_WR_BUF_ENABLE 0x1
-#define MTK_QSPI_WR_BUF_DISABLE 0x0
-
-static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd)
-{
- u8 tmp;
- u8 val = cmd & ~MTK_QSPI_AUTOINC;
-
- writeb(cmd, &priv->regs->cmd);
-
- return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp),
- MTK_QSPI_CMD_POLLINGREG_US);
-}
-
-static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv)
-{
- int len = 1 + priv->txlen + priv->rxlen;
- int i, ret, idx;
-
- if (len > MTK_QSPI_MAX_SHIFT)
- return -ERR_INVAL;
-
- writeb(len * 8, &priv->regs->cnt);
-
- /* start at PRGDATA5, go down to PRGDATA0 */
- idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1;
-
- /* opcode */
- writeb(priv->op, &priv->regs->prgdata[idx]);
- idx--;
-
- /* program TX data */
- for (i = 0; i < priv->txlen; i++, idx--)
- writeb(priv->tx[i], &priv->regs->prgdata[idx]);
-
- /* clear out rest of TX registers */
- while (idx >= 0) {
- writeb(0, &priv->regs->prgdata[idx]);
- idx--;
- }
-
- ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD);
- if (ret)
- return ret;
-
- /* restart at first RX byte */
- idx = priv->rxlen - 1;
-
- /* read out RX data */
- for (i = 0; i < priv->rxlen; i++, idx--)
- priv->rx[i] = readb(&priv->regs->shreg[idx]);
-
- return 0;
-}
-
-static int mtk_qspi_read(struct mtk_qspi_priv *priv,
- u32 addr, u8 *buf, u32 len)
-{
- memcpy(buf, (u8 *)priv->mem_base + addr, len);
- return 0;
-}
-
-static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr)
-{
- int i;
-
- for (i = 0; i < 3; i++) {
- writeb(addr & 0xff, &priv->regs->radr[i]);
- addr >>= 8;
- }
-}
-
-static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv,
- u32 addr, u32 length, const u8 *data)
-{
- int i, ret;
-
- mtk_qspi_set_addr(priv, addr);
-
- for (i = 0; i < length; i++) {
- writeb(*data++, &priv->regs->wdata);
- ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
- if (ret < 0)
- return ret;
- }
- return 0;
-}
-
-static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr,
- const u8 *buf)
-{
- int i, data;
-
- mtk_qspi_set_addr(priv, addr);
-
- for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) {
- data = buf[i + 3] << 24 | buf[i + 2] << 16 |
- buf[i + 1] << 8 | buf[i];
- writel(data, &priv->regs->pp_dw_data);
- }
-
- return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
-}
-
-static int mtk_qspi_write(struct mtk_qspi_priv *priv,
- u32 addr, const u8 *buf, u32 len)
-{
- int ret;
-
- /* setting pre-fetch buffer for page program */
- writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]);
- while (len >= MTK_QSPI_WRBUF_SIZE) {
- ret = mtk_qspi_write_buffer(priv, addr, buf);
- if (ret < 0)
- return ret;
-
- len -= MTK_QSPI_WRBUF_SIZE;
- addr += MTK_QSPI_WRBUF_SIZE;
- buf += MTK_QSPI_WRBUF_SIZE;
- }
- /* disable pre-fetch buffer for page program */
- writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]);
-
- if (len)
- return mtk_qspi_write_single_byte(priv, addr, len, buf);
-
- return 0;
-}
-
-static int mtk_qspi_claim_bus(struct udevice *dev)
-{
- /* nothing to do */
- return 0;
-}
-
-static int mtk_qspi_release_bus(struct udevice *dev)
-{
- /* nothing to do */
- return 0;
-}
-
-static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
-{
- u32 bytes = DIV_ROUND_UP(bitlen, 8);
- u32 addr;
-
- if (!bytes)
- return -ERR_INVAL;
-
- if (dout) {
- if (flags & SPI_XFER_BEGIN) {
- /* parse op code and potential paras first */
- priv->op = *(u8 *)dout;
- if (bytes > 1)
- memcpy(priv->tx, (u8 *)dout + 1,
- bytes <= 4 ? bytes - 1 : 3);
- priv->txlen = bytes - 1;
- }
-
- if (flags == SPI_XFER_ONCE) {
- /* operations without receiving or sending data.
- * for example: erase, write flash register or write
- * enable...
- */
- priv->rx = NULL;
- priv->rxlen = 0;
- return mtk_qspi_tx_rx(priv);
- }
-
- if (flags & SPI_XFER_END) {
- /* here, dout should be data to be written.
- * and priv->tx should be filled 3Bytes address.
- */
- addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
- priv->tx[2];
- return mtk_qspi_write(priv, addr, (u8 *)dout, bytes);
- }
- }
-
- if (din) {
- if (priv->txlen >= 3) {
- /* if run to here, priv->tx[] should be the address
- * where read data from,
- * and, din is the buf to receive data.
- */
- addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
- priv->tx[2];
- return mtk_qspi_read(priv, addr, (u8 *)din, bytes);
- }
-
- /* should be reading flash's register */
- priv->rx = (u8 *)din;
- priv->rxlen = bytes;
- return mtk_qspi_tx_rx(priv);
- }
-
- return 0;
-}
-
-static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
-{
- struct udevice *bus = dev->parent;
- struct mtk_qspi_priv *priv = dev_get_priv(bus);
-
- return mtk_qspi_transfer(priv, bitlen, dout, din, flags);
-}
-
-static int mtk_qspi_set_speed(struct udevice *bus, uint speed)
-{
- /* nothing to do */
- return 0;
-}
-
-static int mtk_qspi_set_mode(struct udevice *bus, uint mode)
-{
- /* nothing to do */
- return 0;
-}
-
-static int mtk_qspi_ofdata_to_platdata(struct udevice *bus)
-{
- struct resource res_reg, res_mem;
- struct mtk_qspi_platdata *plat = bus->platdata;
- int ret;
-
- ret = dev_read_resource_byname(bus, "reg_base", &res_reg);
- if (ret) {
- debug("can't get reg_base resource(ret = %d)\n", ret);
- return -ENOMEM;
- }
-
- ret = dev_read_resource_byname(bus, "mem_base", &res_mem);
- if (ret) {
- debug("can't get map_base resource(ret = %d)\n", ret);
- return -ENOMEM;
- }
-
- plat->mem_base = res_mem.start;
- plat->reg_base = res_reg.start;
-
- return 0;
-}
-
-static int mtk_qspi_probe(struct udevice *bus)
-{
- struct mtk_qspi_platdata *plat = dev_get_platdata(bus);
- struct mtk_qspi_priv *priv = dev_get_priv(bus);
-
- priv->regs = (struct mtk_qspi_regs *)plat->reg_base;
- priv->mem_base = (unsigned long *)plat->mem_base;
-
- writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot);
-
- return 0;
-}
-
-static const struct dm_spi_ops mtk_qspi_ops = {
- .claim_bus = mtk_qspi_claim_bus,
- .release_bus = mtk_qspi_release_bus,
- .xfer = mtk_qspi_xfer,
- .set_speed = mtk_qspi_set_speed,
- .set_mode = mtk_qspi_set_mode,
-};
-
-static const struct udevice_id mtk_qspi_ids[] = {
- { .compatible = "mediatek,mt7629-qspi" },
- { }
-};
-
-U_BOOT_DRIVER(mtk_qspi) = {
- .name = "mtk_qspi",
- .id = UCLASS_SPI,
- .of_match = mtk_qspi_ids,
- .ops = &mtk_qspi_ops,
- .ofdata_to_platdata = mtk_qspi_ofdata_to_platdata,
- .platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata),
- .priv_auto_alloc_size = sizeof(struct mtk_qspi_priv),
- .probe = mtk_qspi_probe,
-};
diff --git a/drivers/spi/mtk_snfi_spi.c b/drivers/spi/mtk_snfi_spi.c
new file mode 100644
index 0000000000..2a89476515
--- /dev/null
+++ b/drivers/spi/mtk_snfi_spi.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <spi.h>
+#include <spi-mem.h>
+#include <stdbool.h>
+#include <watchdog.h>
+#include <dm/pinctrl.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#define SNFI_MAC_CTL 0x500
+#define MAC_XIO_SEL BIT(4)
+#define SF_MAC_EN BIT(3)
+#define SF_TRIG BIT(2)
+#define WIP_READY BIT(1)
+#define WIP BIT(0)
+
+#define SNFI_MAC_OUTL 0x504
+#define SNFI_MAC_INL 0x508
+
+#define SNFI_MISC_CTL 0x538
+#define SW_RST BIT(28)
+#define FIFO_RD_LTC_SHIFT 25
+#define FIFO_RD_LTC GENMASK(26, 25)
+#define LATCH_LAT_SHIFT 8
+#define LATCH_LAT GENMASK(9, 8)
+#define CS_DESELECT_CYC_SHIFT 0
+#define CS_DESELECT_CYC GENMASK(4, 0)
+
+#define SNF_STA_CTL1 0x550
+#define SPI_STATE GENMASK(3, 0)
+
+#define SNFI_GPRAM_OFFSET 0x800
+#define SNFI_GPRAM_SIZE 0x80
+
+#define SNFI_POLL_INTERVAL 500000
+#define SNFI_RST_POLL_INTERVAL 1000000
+
+struct mtk_snfi_priv {
+ void __iomem *base;
+
+ struct clk nfi_clk;
+ struct clk pad_clk;
+};
+
+static int mtk_snfi_adjust_op_size(struct spi_slave *slave,
+ struct spi_mem_op *op)
+{
+ u32 nbytes;
+
+ /*
+ * When there is input data, it will be appended after the output
+ * data in the GPRAM. So the total size of either pure output data
+ * or the output+input data must not exceed the GPRAM size.
+ */
+
+ nbytes = sizeof(op->cmd.opcode) + op->addr.nbytes +
+ op->dummy.nbytes;
+
+ if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE)
+ return 0;
+
+ if (nbytes >= SNFI_GPRAM_SIZE)
+ return -ENOTSUPP;
+
+ op->data.nbytes = SNFI_GPRAM_SIZE - nbytes;
+
+ return 0;
+}
+
+static bool mtk_snfi_supports_op(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 ||
+ op->dummy.buswidth > 1 || op->data.buswidth > 1)
+ return false;
+
+ return true;
+}
+
+static int mtk_snfi_mac_trigger(struct mtk_snfi_priv *priv,
+ struct udevice *bus, u32 outlen, u32 inlen)
+{
+ int ret;
+ u32 val;
+
+#ifdef CONFIG_PINCTRL
+ pinctrl_select_state(bus, "snfi");
+#endif
+
+ writel(SF_MAC_EN, priv->base + SNFI_MAC_CTL);
+ writel(outlen, priv->base + SNFI_MAC_OUTL);
+ writel(inlen, priv->base + SNFI_MAC_INL);
+
+ writel(SF_MAC_EN | SF_TRIG, priv->base + SNFI_MAC_CTL);
+
+ ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val,
+ val & WIP_READY, SNFI_POLL_INTERVAL);
+ if (ret) {
+ printf("%s: timed out waiting for WIP_READY\n", __func__);
+ goto cleanup;
+ }
+
+ ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val,
+ !(val & WIP), SNFI_POLL_INTERVAL);
+ if (ret)
+ printf("%s: timed out waiting for WIP cleared\n", __func__);
+
+ writel(0, priv->base + SNFI_MAC_CTL);
+
+cleanup:
+#ifdef CONFIG_PINCTRL
+ pinctrl_select_state(bus, "default");
+#endif
+
+ return ret;
+}
+
+static int mtk_snfi_mac_reset(struct mtk_snfi_priv *priv)
+{
+ int ret;
+ u32 val;
+
+ setbits_32(priv->base + SNFI_MISC_CTL, SW_RST);
+
+ ret = readl_poll_timeout(priv->base + SNF_STA_CTL1, val,
+ !(val & SPI_STATE), SNFI_POLL_INTERVAL);
+ if (ret)
+ printf("%s: failed to reset snfi mac\n", __func__);
+
+ writel((2 << FIFO_RD_LTC_SHIFT) |
+ (10 << CS_DESELECT_CYC_SHIFT),
+ priv->base + SNFI_MISC_CTL);
+
+ return ret;
+}
+
+static void mtk_snfi_copy_to_gpram(struct mtk_snfi_priv *priv,
+ const void *data, size_t len)
+{
+ void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET;
+ size_t i, n = (len + sizeof(u32) - 1) / sizeof(u32);
+ const u32 *buff = data;
+
+ /*
+ * The output data will always be copied to the beginning of
+ * the GPRAM. Uses word write for better performace.
+ *
+ * Trailing bytes in the last word are not cared.
+ */
+
+ for (i = 0; i < n; i++)
+ writel(buff[i], gpram + i * sizeof(u32));
+}
+
+static void mtk_snfi_copy_from_gpram(struct mtk_snfi_priv *priv, u8 *cache,
+ void *data, size_t pos, size_t len)
+{
+ void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET;
+ u32 *buff = (u32 *)cache;
+ size_t i, off, end;
+
+ /* Start position in the buffer */
+ off = pos & (sizeof(u32) - 1);
+
+ /* End position for copy */
+ end = (len + pos + sizeof(u32) - 1) & (~(sizeof(u32) - 1));
+
+ /* Start position for copy */
+ pos &= ~(sizeof(u32) - 1);
+
+ /*
+ * Read aligned data from GPRAM to buffer first.
+ * Uses word read for better performace.
+ */
+ i = 0;
+ while (pos < end) {
+ buff[i++] = readl(gpram + pos);
+ pos += sizeof(u32);
+ }
+
+ /* Copy rx data */
+ memcpy(data, cache + off, len);
+}
+
+static int mtk_snfi_exec_op(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ struct udevice *bus = dev_get_parent(slave->dev);
+ struct mtk_snfi_priv *priv = dev_get_priv(bus);
+ u8 gpram_cache[SNFI_GPRAM_SIZE];
+ u32 i, len = 0, inlen = 0;
+ int addr_sh;
+ int ret;
+
+ WATCHDOG_RESET();
+
+ ret = mtk_snfi_mac_reset(priv);
+ if (ret)
+ return ret;
+
+ /* Put opcode */
+ gpram_cache[len++] = op->cmd.opcode;
+
+ /* Put address */
+ addr_sh = (op->addr.nbytes - 1) * 8;
+ while (addr_sh >= 0) {
+ gpram_cache[len++] = (op->addr.val >> addr_sh) & 0xff;
+ addr_sh -= 8;
+ }
+
+ /* Put dummy bytes */
+ for (i = 0; i < op->dummy.nbytes; i++)
+ gpram_cache[len++] = 0;
+
+ /* Put output data */
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) {
+ memcpy(gpram_cache + len, op->data.buf.out, op->data.nbytes);
+ len += op->data.nbytes;
+ }
+
+ /* Copy final output data to GPRAM */
+ mtk_snfi_copy_to_gpram(priv, gpram_cache, len);
+
+ /* Start one SPI transaction */
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
+ inlen = op->data.nbytes;
+
+ ret = mtk_snfi_mac_trigger(priv, bus, len, inlen);
+ if (ret)
+ return ret;
+
+ /* Copy input data from GPRAM */
+ if (inlen)
+ mtk_snfi_copy_from_gpram(priv, gpram_cache, op->data.buf.in,
+ len, inlen);
+
+ return 0;
+}
+
+static int mtk_snfi_spi_probe(struct udevice *bus)
+{
+ struct mtk_snfi_priv *priv = dev_get_priv(bus);
+ int ret;
+
+ priv->base = (void __iomem *)devfdt_get_addr(bus);
+ if (!priv->base)
+ return -EINVAL;
+
+ ret = clk_get_by_name(bus, "nfi_clk", &priv->nfi_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(bus, "pad_clk", &priv->pad_clk);
+ if (ret < 0)
+ return ret;
+
+ clk_enable(&priv->nfi_clk);
+ clk_enable(&priv->pad_clk);
+
+ return 0;
+}
+
+static int mtk_snfi_set_speed(struct udevice *bus, uint speed)
+{
+ /*
+ * The SNFI does not have a bus clock divider.
+ * The bus clock is set in dts (pad_clk, UNIVPLL2_D8 = 50MHz).
+ */
+
+ return 0;
+}
+
+static int mtk_snfi_set_mode(struct udevice *bus, uint mode)
+{
+ /* The SNFI supports only mode 0 */
+
+ if (mode)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct spi_controller_mem_ops mtk_snfi_mem_ops = {
+ .adjust_op_size = mtk_snfi_adjust_op_size,
+ .supports_op = mtk_snfi_supports_op,
+ .exec_op = mtk_snfi_exec_op,
+};
+
+static const struct dm_spi_ops mtk_snfi_spi_ops = {
+ .mem_ops = &mtk_snfi_mem_ops,
+ .set_speed = mtk_snfi_set_speed,
+ .set_mode = mtk_snfi_set_mode,
+};
+
+static const struct udevice_id mtk_snfi_spi_ids[] = {
+ { .compatible = "mediatek,mtk-snfi-spi" },
+ { }
+};
+
+U_BOOT_DRIVER(mtk_snfi_spi) = {
+ .name = "mtk_snfi_spi",
+ .id = UCLASS_SPI,
+ .of_match = mtk_snfi_spi_ids,
+ .ops = &mtk_snfi_spi_ops,
+ .priv_auto_alloc_size = sizeof(struct mtk_snfi_priv),
+ .probe = mtk_snfi_spi_probe,
+};
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 5065e407f8..3a9756fbf1 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -2,6 +2,9 @@
/*
* Freescale i.MX28 SPI driver
*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
@@ -27,6 +30,19 @@
#define MXSSSP_SMALL_TRANSFER 512
+static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
+{
+ writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
+ writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);
+}
+
+static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
+{
+ writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);
+ writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
+}
+
+#if !CONFIG_IS_ENABLED(DM_SPI)
struct mxs_spi_slave {
struct spi_slave slave;
uint32_t max_khz;
@@ -38,94 +54,38 @@ static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
{
return container_of(slave, struct mxs_spi_slave, slave);
}
+#else
+#include <dm.h>
+#include <errno.h>
+struct mxs_spi_platdata {
+ s32 frequency; /* Default clock frequency, -1 for none */
+ fdt_addr_t base; /* SPI IP block base address */
+ int num_cs; /* Number of CSes supported */
+ int dma_id; /* ID of the DMA channel */
+ int clk_id; /* ID of the SSP clock */
+};
-int spi_cs_is_valid(unsigned int bus, unsigned int cs)
-{
- /* MXS SPI: 4 ports and 3 chip selects maximum */
- if (!mxs_ssp_bus_id_valid(bus) || cs > 2)
- return 0;
- else
- return 1;
-}
-
-struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
- unsigned int max_hz, unsigned int mode)
-{
- struct mxs_spi_slave *mxs_slave;
-
- if (!spi_cs_is_valid(bus, cs)) {
- printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
- return NULL;
- }
-
- mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
- if (!mxs_slave)
- return NULL;
-
- if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
- goto err_init;
-
- mxs_slave->max_khz = max_hz / 1000;
- mxs_slave->mode = mode;
- mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
-
- return &mxs_slave->slave;
-
-err_init:
- free(mxs_slave);
- return NULL;
-}
-
-void spi_free_slave(struct spi_slave *slave)
-{
- struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
- free(mxs_slave);
-}
-
-int spi_claim_bus(struct spi_slave *slave)
-{
- struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
- struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
- uint32_t reg = 0;
-
- mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
-
- writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) |
- SSP_CTRL0_BUS_WIDTH_ONE_BIT,
- &ssp_regs->hw_ssp_ctrl0);
-
- reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
- reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
- reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
- writel(reg, &ssp_regs->hw_ssp_ctrl1);
-
- writel(0, &ssp_regs->hw_ssp_cmd0);
-
- mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz);
-
- return 0;
-}
-
-void spi_release_bus(struct spi_slave *slave)
-{
-}
-
-static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
-{
- writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
- writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);
-}
-
-static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
-{
- writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);
- writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
-}
+struct mxs_spi_priv {
+ struct mxs_ssp_regs *regs;
+ unsigned int dma_channel;
+ unsigned int max_freq;
+ unsigned int clk_id;
+ unsigned int mode;
+};
+#endif
+#if !CONFIG_IS_ENABLED(DM_SPI)
static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
char *data, int length, int write, unsigned long flags)
{
struct mxs_ssp_regs *ssp_regs = slave->regs;
+#else
+static int mxs_spi_xfer_pio(struct mxs_spi_priv *priv,
+ char *data, int length, int write,
+ unsigned long flags)
+{
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
+#endif
if (flags & SPI_XFER_BEGIN)
mxs_spi_start_xfer(ssp_regs);
@@ -181,12 +141,19 @@ static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
return 0;
}
+#if !CONFIG_IS_ENABLED(DM_SPI)
static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
char *data, int length, int write, unsigned long flags)
{
+ struct mxs_ssp_regs *ssp_regs = slave->regs;
+#else
+static int mxs_spi_xfer_dma(struct mxs_spi_priv *priv,
+ char *data, int length, int write,
+ unsigned long flags)
+{ struct mxs_ssp_regs *ssp_regs = priv->regs;
+#endif
const int xfer_max_sz = 0xff00;
const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1;
- struct mxs_ssp_regs *ssp_regs = slave->regs;
struct mxs_dma_desc *dp;
uint32_t ctrl0;
uint32_t cache_data_count;
@@ -225,7 +192,11 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
/* Invalidate the area, so no writeback into the RAM races with DMA */
invalidate_dcache_range(dstart, dstart + cache_data_count);
+#if !CONFIG_IS_ENABLED(DM_SPI)
dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus;
+#else
+ dmach = priv->dma_channel;
+#endif
dp = desc;
while (length) {
@@ -302,11 +273,20 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
return ret;
}
+#if !CONFIG_IS_ENABLED(DM_SPI)
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
+#else
+int mxs_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct mxs_spi_priv *priv = dev_get_priv(bus);
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
+#endif
int len = bitlen / 8;
char dummy;
int write = 0;
@@ -350,9 +330,263 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
if (!dma || (len < MXSSSP_SMALL_TRANSFER)) {
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
+#if !CONFIG_IS_ENABLED(DM_SPI)
return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags);
+#else
+ return mxs_spi_xfer_pio(priv, data, len, write, flags);
+#endif
} else {
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
+#if !CONFIG_IS_ENABLED(DM_SPI)
return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags);
+#else
+ return mxs_spi_xfer_dma(priv, data, len, write, flags);
+#endif
}
}
+
+#if !CONFIG_IS_ENABLED(DM_SPI)
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ /* MXS SPI: 4 ports and 3 chip selects maximum */
+ if (!mxs_ssp_bus_id_valid(bus) || cs > 2)
+ return 0;
+ else
+ return 1;
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct mxs_spi_slave *mxs_slave;
+
+ if (!spi_cs_is_valid(bus, cs)) {
+ printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
+ return NULL;
+ }
+
+ mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
+ if (!mxs_slave)
+ return NULL;
+
+ if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
+ goto err_init;
+
+ mxs_slave->max_khz = max_hz / 1000;
+ mxs_slave->mode = mode;
+ mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
+
+ return &mxs_slave->slave;
+
+err_init:
+ free(mxs_slave);
+ return NULL;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
+
+ free(mxs_slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
+ struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
+ u32 reg = 0;
+
+ mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
+
+ writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) |
+ SSP_CTRL0_BUS_WIDTH_ONE_BIT,
+ &ssp_regs->hw_ssp_ctrl0);
+
+ reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
+ reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
+ reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
+ writel(reg, &ssp_regs->hw_ssp_ctrl1);
+
+ writel(0, &ssp_regs->hw_ssp_cmd0);
+
+ mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz);
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+}
+
+#else /* CONFIG_DM_SPI */
+/* Base numbers of i.MX2[38] clk for ssp0 IP block */
+#define MXS_SSP_IMX23_CLKID_SSP0 33
+#define MXS_SSP_IMX28_CLKID_SSP0 46
+
+static int mxs_spi_probe(struct udevice *bus)
+{
+ struct mxs_spi_platdata *plat = dev_get_platdata(bus);
+ struct mxs_spi_priv *priv = dev_get_priv(bus);
+ int ret;
+
+ debug("%s: probe\n", __func__);
+ priv->regs = (struct mxs_ssp_regs *)plat->base;
+ priv->max_freq = plat->frequency;
+
+ priv->dma_channel = plat->dma_id;
+ priv->clk_id = plat->clk_id;
+
+ ret = mxs_dma_init_channel(priv->dma_channel);
+ if (ret) {
+ printf("%s: DMA init channel error %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mxs_spi_claim_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct mxs_spi_priv *priv = dev_get_priv(bus);
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
+ int cs = spi_chip_select(dev);
+
+ /*
+ * i.MX28 supports up to 3 CS (SSn0, SSn1, SSn2)
+ * To set them it uses following tuple (WAIT_FOR_IRQ,WAIT_FOR_CMD),
+ * where:
+ *
+ * WAIT_FOR_IRQ is bit 21 of HW_SSP_CTRL0
+ * WAIT_FOR_CMD is bit 20 (#defined as MXS_SSP_CHIPSELECT_SHIFT here) of
+ * HW_SSP_CTRL0
+ * SSn0 b00
+ * SSn1 b01
+ * SSn2 b10 (which require setting WAIT_FOR_IRQ)
+ *
+ * However, for now i.MX28 SPI driver will support up till 2 CSes
+ * (SSn0, and SSn1).
+ */
+
+ /* Ungate SSP clock and set active CS */
+ clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0,
+ BIT(MXS_SSP_CHIPSELECT_SHIFT) |
+ SSP_CTRL0_CLKGATE, (cs << MXS_SSP_CHIPSELECT_SHIFT));
+
+ return 0;
+}
+
+static int mxs_spi_release_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct mxs_spi_priv *priv = dev_get_priv(bus);
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
+
+ /* Gate SSP clock */
+ setbits_le32(&ssp_regs->hw_ssp_ctrl0, SSP_CTRL0_CLKGATE);
+
+ return 0;
+}
+
+static int mxs_spi_set_speed(struct udevice *bus, uint speed)
+{
+ struct mxs_spi_priv *priv = dev_get_priv(bus);
+#ifdef CONFIG_MX28
+ int clkid = priv->clk_id - MXS_SSP_IMX28_CLKID_SSP0;
+#else /* CONFIG_MX23 */
+ int clkid = priv->clk_id - MXS_SSP_IMX23_CLKID_SSP0;
+#endif
+ if (speed > priv->max_freq)
+ speed = priv->max_freq;
+
+ debug("%s speed: %u [Hz] clkid: %d\n", __func__, speed, clkid);
+ mxs_set_ssp_busclock(clkid, speed / 1000);
+
+ return 0;
+}
+
+static int mxs_spi_set_mode(struct udevice *bus, uint mode)
+{
+ struct mxs_spi_priv *priv = dev_get_priv(bus);
+ struct mxs_ssp_regs *ssp_regs = priv->regs;
+ u32 reg;
+
+ priv->mode = mode;
+ debug("%s: mode 0x%x\n", __func__, mode);
+
+ reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
+ reg |= (priv->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
+ reg |= (priv->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
+ writel(reg, &ssp_regs->hw_ssp_ctrl1);
+
+ /* Single bit SPI support */
+ writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0);
+
+ return 0;
+}
+
+static const struct dm_spi_ops mxs_spi_ops = {
+ .claim_bus = mxs_spi_claim_bus,
+ .release_bus = mxs_spi_release_bus,
+ .xfer = mxs_spi_xfer,
+ .set_speed = mxs_spi_set_speed,
+ .set_mode = mxs_spi_set_mode,
+ /*
+ * cs_info is not needed, since we require all chip selects to be
+ * in the device tree explicitly
+ */
+};
+
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+static int mxs_ofdata_to_platdata(struct udevice *bus)
+{
+ struct mxs_spi_platdata *plat = bus->platdata;
+ u32 prop[2];
+ int ret;
+
+ plat->base = dev_read_addr(bus);
+ plat->frequency =
+ dev_read_u32_default(bus, "spi-max-frequency", 40000000);
+ plat->num_cs = dev_read_u32_default(bus, "num-cs", 2);
+
+ ret = dev_read_u32_array(bus, "dmas", prop, ARRAY_SIZE(prop));
+ if (ret) {
+ printf("%s: Reading 'dmas' property failed!\n", __func__);
+ return ret;
+ }
+ plat->dma_id = prop[1];
+
+ ret = dev_read_u32_array(bus, "clocks", prop, ARRAY_SIZE(prop));
+ if (ret) {
+ printf("%s: Reading 'clocks' property failed!\n", __func__);
+ return ret;
+ }
+ plat->clk_id = prop[1];
+
+ debug("%s: base=0x%x, max-frequency=%d num-cs=%d dma_id=%d clk_id=%d\n",
+ __func__, (uint)plat->base, plat->frequency, plat->num_cs,
+ plat->dma_id, plat->clk_id);
+
+ return 0;
+}
+#endif
+
+static const struct udevice_id mxs_spi_ids[] = {
+ { .compatible = "fsl,imx23-spi" },
+ { .compatible = "fsl,imx28-spi" },
+ { }
+};
+
+U_BOOT_DRIVER(mxs_spi) = {
+ .name = "mxs_spi",
+ .id = UCLASS_SPI,
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+ .of_match = mxs_spi_ids,
+ .ofdata_to_platdata = mxs_ofdata_to_platdata,
+#endif
+ .priv_auto_alloc_size = sizeof(struct mxs_spi_platdata),
+ .ops = &mxs_spi_ops,
+ .priv_auto_alloc_size = sizeof(struct mxs_spi_priv),
+ .probe = mxs_spi_probe,
+};
+#endif
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 7aabebeff5..7788ab9953 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -430,12 +430,14 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
if (slave->max_write_size && len > slave->max_write_size)
return -EINVAL;
- if (op->data.dir == SPI_MEM_DATA_IN && slave->max_read_size)
- op->data.nbytes = min(op->data.nbytes,
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ if (slave->max_read_size)
+ op->data.nbytes = min(op->data.nbytes,
slave->max_read_size);
- else if (slave->max_write_size)
+ } else if (slave->max_write_size) {
op->data.nbytes = min(op->data.nbytes,
slave->max_write_size - len);
+ }
if (!op->data.nbytes)
return -EINVAL;
diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
new file mode 100644
index 0000000000..969bd4b75c
--- /dev/null
+++ b/drivers/spi/spi-sifive.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 SiFive, Inc.
+ * Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com>
+ *
+ * SiFive SPI controller driver (master mode only)
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <linux/log2.h>
+#include <clk.h>
+
+#define SIFIVE_SPI_MAX_CS 32
+
+#define SIFIVE_SPI_DEFAULT_DEPTH 8
+#define SIFIVE_SPI_DEFAULT_BITS 8
+
+/* register offsets */
+#define SIFIVE_SPI_REG_SCKDIV 0x00 /* Serial clock divisor */
+#define SIFIVE_SPI_REG_SCKMODE 0x04 /* Serial clock mode */
+#define SIFIVE_SPI_REG_CSID 0x10 /* Chip select ID */
+#define SIFIVE_SPI_REG_CSDEF 0x14 /* Chip select default */
+#define SIFIVE_SPI_REG_CSMODE 0x18 /* Chip select mode */
+#define SIFIVE_SPI_REG_DELAY0 0x28 /* Delay control 0 */
+#define SIFIVE_SPI_REG_DELAY1 0x2c /* Delay control 1 */
+#define SIFIVE_SPI_REG_FMT 0x40 /* Frame format */
+#define SIFIVE_SPI_REG_TXDATA 0x48 /* Tx FIFO data */
+#define SIFIVE_SPI_REG_RXDATA 0x4c /* Rx FIFO data */
+#define SIFIVE_SPI_REG_TXMARK 0x50 /* Tx FIFO watermark */
+#define SIFIVE_SPI_REG_RXMARK 0x54 /* Rx FIFO watermark */
+#define SIFIVE_SPI_REG_FCTRL 0x60 /* SPI flash interface control */
+#define SIFIVE_SPI_REG_FFMT 0x64 /* SPI flash instruction format */
+#define SIFIVE_SPI_REG_IE 0x70 /* Interrupt Enable Register */
+#define SIFIVE_SPI_REG_IP 0x74 /* Interrupt Pendings Register */
+
+/* sckdiv bits */
+#define SIFIVE_SPI_SCKDIV_DIV_MASK 0xfffU
+
+/* sckmode bits */
+#define SIFIVE_SPI_SCKMODE_PHA BIT(0)
+#define SIFIVE_SPI_SCKMODE_POL BIT(1)
+#define SIFIVE_SPI_SCKMODE_MODE_MASK (SIFIVE_SPI_SCKMODE_PHA | \
+ SIFIVE_SPI_SCKMODE_POL)
+
+/* csmode bits */
+#define SIFIVE_SPI_CSMODE_MODE_AUTO 0U
+#define SIFIVE_SPI_CSMODE_MODE_HOLD 2U
+#define SIFIVE_SPI_CSMODE_MODE_OFF 3U
+
+/* delay0 bits */
+#define SIFIVE_SPI_DELAY0_CSSCK(x) ((u32)(x))
+#define SIFIVE_SPI_DELAY0_CSSCK_MASK 0xffU
+#define SIFIVE_SPI_DELAY0_SCKCS(x) ((u32)(x) << 16)
+#define SIFIVE_SPI_DELAY0_SCKCS_MASK (0xffU << 16)
+
+/* delay1 bits */
+#define SIFIVE_SPI_DELAY1_INTERCS(x) ((u32)(x))
+#define SIFIVE_SPI_DELAY1_INTERCS_MASK 0xffU
+#define SIFIVE_SPI_DELAY1_INTERXFR(x) ((u32)(x) << 16)
+#define SIFIVE_SPI_DELAY1_INTERXFR_MASK (0xffU << 16)
+
+/* fmt bits */
+#define SIFIVE_SPI_FMT_PROTO_SINGLE 0U
+#define SIFIVE_SPI_FMT_PROTO_DUAL 1U
+#define SIFIVE_SPI_FMT_PROTO_QUAD 2U
+#define SIFIVE_SPI_FMT_PROTO_MASK 3U
+#define SIFIVE_SPI_FMT_ENDIAN BIT(2)
+#define SIFIVE_SPI_FMT_DIR BIT(3)
+#define SIFIVE_SPI_FMT_LEN(x) ((u32)(x) << 16)
+#define SIFIVE_SPI_FMT_LEN_MASK (0xfU << 16)
+
+/* txdata bits */
+#define SIFIVE_SPI_TXDATA_DATA_MASK 0xffU
+#define SIFIVE_SPI_TXDATA_FULL BIT(31)
+
+/* rxdata bits */
+#define SIFIVE_SPI_RXDATA_DATA_MASK 0xffU
+#define SIFIVE_SPI_RXDATA_EMPTY BIT(31)
+
+/* ie and ip bits */
+#define SIFIVE_SPI_IP_TXWM BIT(0)
+#define SIFIVE_SPI_IP_RXWM BIT(1)
+
+struct sifive_spi {
+ void *regs; /* base address of the registers */
+ u32 fifo_depth;
+ u32 bits_per_word;
+ u32 cs_inactive; /* Level of the CS pins when inactive*/
+ u32 freq;
+ u32 num_cs;
+};
+
+static void sifive_spi_prep_device(struct sifive_spi *spi,
+ struct dm_spi_slave_platdata *slave)
+{
+ /* Update the chip select polarity */
+ if (slave->mode & SPI_CS_HIGH)
+ spi->cs_inactive &= ~BIT(slave->cs);
+ else
+ spi->cs_inactive |= BIT(slave->cs);
+ writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
+
+ /* Select the correct device */
+ writel(slave->cs, spi->regs + SIFIVE_SPI_REG_CSID);
+}
+
+static int sifive_spi_set_cs(struct sifive_spi *spi,
+ struct dm_spi_slave_platdata *slave)
+{
+ u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD;
+
+ if (slave->mode & SPI_CS_HIGH)
+ cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;
+
+ writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE);
+
+ return 0;
+}
+
+static void sifive_spi_clear_cs(struct sifive_spi *spi)
+{
+ writel(SIFIVE_SPI_CSMODE_MODE_AUTO, spi->regs + SIFIVE_SPI_REG_CSMODE);
+}
+
+static void sifive_spi_prep_transfer(struct sifive_spi *spi,
+ bool is_rx_xfer,
+ struct dm_spi_slave_platdata *slave)
+{
+ u32 cr;
+
+ /* Modify the SPI protocol mode */
+ cr = readl(spi->regs + SIFIVE_SPI_REG_FMT);
+
+ /* Bits per word ? */
+ cr &= ~SIFIVE_SPI_FMT_LEN_MASK;
+ cr |= SIFIVE_SPI_FMT_LEN(spi->bits_per_word);
+
+ /* LSB first? */
+ cr &= ~SIFIVE_SPI_FMT_ENDIAN;
+ if (slave->mode & SPI_LSB_FIRST)
+ cr |= SIFIVE_SPI_FMT_ENDIAN;
+
+ /* Number of wires ? */
+ cr &= ~SIFIVE_SPI_FMT_PROTO_MASK;
+ if ((slave->mode & SPI_TX_QUAD) || (slave->mode & SPI_RX_QUAD))
+ cr |= SIFIVE_SPI_FMT_PROTO_QUAD;
+ else if ((slave->mode & SPI_TX_DUAL) || (slave->mode & SPI_RX_DUAL))
+ cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
+ else
+ cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
+
+ /* SPI direction in/out ? */
+ cr &= ~SIFIVE_SPI_FMT_DIR;
+ if (!is_rx_xfer)
+ cr |= SIFIVE_SPI_FMT_DIR;
+
+ writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
+}
+
+static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr)
+{
+ u32 data;
+
+ do {
+ data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA);
+ } while (data & SIFIVE_SPI_RXDATA_EMPTY);
+
+ if (rx_ptr)
+ *rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK;
+}
+
+static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
+{
+ u32 data;
+ u8 tx_data = (tx_ptr) ? *tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK :
+ SIFIVE_SPI_TXDATA_DATA_MASK;
+
+ do {
+ data = readl(spi->regs + SIFIVE_SPI_REG_TXDATA);
+ } while (data & SIFIVE_SPI_TXDATA_FULL);
+
+ writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
+}
+
+static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev->parent;
+ struct sifive_spi *spi = dev_get_priv(bus);
+ struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
+ const unsigned char *tx_ptr = dout;
+ u8 *rx_ptr = din;
+ u32 remaining_len;
+ int ret;
+
+ if (flags & SPI_XFER_BEGIN) {
+ sifive_spi_prep_device(spi, slave);
+
+ ret = sifive_spi_set_cs(spi, slave);
+ if (ret)
+ return ret;
+ }
+
+ sifive_spi_prep_transfer(spi, true, slave);
+
+ remaining_len = bitlen / 8;
+
+ while (remaining_len) {
+ int n_words, tx_words, rx_words;
+
+ n_words = min(remaining_len, spi->fifo_depth);
+
+ /* Enqueue n_words for transmission */
+ if (tx_ptr) {
+ for (tx_words = 0; tx_words < n_words; ++tx_words) {
+ sifive_spi_tx(spi, tx_ptr);
+ sifive_spi_rx(spi, NULL);
+ tx_ptr++;
+ }
+ }
+
+ /* Read out all the data from the RX FIFO */
+ if (rx_ptr) {
+ for (rx_words = 0; rx_words < n_words; ++rx_words) {
+ sifive_spi_tx(spi, NULL);
+ sifive_spi_rx(spi, rx_ptr);
+ rx_ptr++;
+ }
+ }
+
+ remaining_len -= n_words;
+ }
+
+ if (flags & SPI_XFER_END)
+ sifive_spi_clear_cs(spi);
+
+ return 0;
+}
+
+static int sifive_spi_set_speed(struct udevice *bus, uint speed)
+{
+ struct sifive_spi *spi = dev_get_priv(bus);
+ u32 scale;
+
+ if (speed > spi->freq)
+ speed = spi->freq;
+
+ /* Cofigure max speed */
+ scale = (DIV_ROUND_UP(spi->freq >> 1, speed) - 1)
+ & SIFIVE_SPI_SCKDIV_DIV_MASK;
+ writel(scale, spi->regs + SIFIVE_SPI_REG_SCKDIV);
+
+ return 0;
+}
+
+static int sifive_spi_set_mode(struct udevice *bus, uint mode)
+{
+ struct sifive_spi *spi = dev_get_priv(bus);
+ u32 cr;
+
+ /* Switch clock mode bits */
+ cr = readl(spi->regs + SIFIVE_SPI_REG_SCKMODE) &
+ ~SIFIVE_SPI_SCKMODE_MODE_MASK;
+ if (mode & SPI_CPHA)
+ cr |= SIFIVE_SPI_SCKMODE_PHA;
+ if (mode & SPI_CPOL)
+ cr |= SIFIVE_SPI_SCKMODE_POL;
+
+ writel(cr, spi->regs + SIFIVE_SPI_REG_SCKMODE);
+
+ return 0;
+}
+
+static int sifive_spi_cs_info(struct udevice *bus, uint cs,
+ struct spi_cs_info *info)
+{
+ struct sifive_spi *spi = dev_get_priv(bus);
+
+ if (cs >= spi->num_cs)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void sifive_spi_init_hw(struct sifive_spi *spi)
+{
+ u32 cs_bits;
+
+ /* probe the number of CS lines */
+ spi->cs_inactive = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
+ writel(0xffffffffU, spi->regs + SIFIVE_SPI_REG_CSDEF);
+ cs_bits = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
+ writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
+ if (!cs_bits) {
+ printf("Could not auto probe CS lines\n");
+ return;
+ }
+
+ spi->num_cs = ilog2(cs_bits) + 1;
+ if (spi->num_cs > SIFIVE_SPI_MAX_CS) {
+ printf("Invalid number of spi slaves\n");
+ return;
+ }
+
+ /* Watermark interrupts are disabled by default */
+ writel(0, spi->regs + SIFIVE_SPI_REG_IE);
+
+ /* Set CS/SCK Delays and Inactive Time to defaults */
+ writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
+ spi->regs + SIFIVE_SPI_REG_DELAY0);
+ writel(SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0),
+ spi->regs + SIFIVE_SPI_REG_DELAY1);
+
+ /* Exit specialized memory-mapped SPI flash mode */
+ writel(0, spi->regs + SIFIVE_SPI_REG_FCTRL);
+}
+
+static int sifive_spi_probe(struct udevice *bus)
+{
+ struct sifive_spi *spi = dev_get_priv(bus);
+ struct clk clkdev;
+ int ret;
+
+ spi->regs = (void *)(ulong)dev_remap_addr(bus);
+ if (!spi->regs)
+ return -ENODEV;
+
+ spi->fifo_depth = dev_read_u32_default(bus,
+ "sifive,fifo-depth",
+ SIFIVE_SPI_DEFAULT_DEPTH);
+
+ spi->bits_per_word = dev_read_u32_default(bus,
+ "sifive,max-bits-per-word",
+ SIFIVE_SPI_DEFAULT_BITS);
+
+ ret = clk_get_by_index(bus, 0, &clkdev);
+ if (ret)
+ return ret;
+ spi->freq = clk_get_rate(&clkdev);
+
+ /* init the sifive spi hw */
+ sifive_spi_init_hw(spi);
+
+ return 0;
+}
+
+static const struct dm_spi_ops sifive_spi_ops = {
+ .xfer = sifive_spi_xfer,
+ .set_speed = sifive_spi_set_speed,
+ .set_mode = sifive_spi_set_mode,
+ .cs_info = sifive_spi_cs_info,
+};
+
+static const struct udevice_id sifive_spi_ids[] = {
+ { .compatible = "sifive,spi0" },
+ { }
+};
+
+U_BOOT_DRIVER(sifive_spi) = {
+ .name = "sifive_spi",
+ .id = UCLASS_SPI,
+ .of_match = sifive_spi_ids,
+ .ops = &sifive_spi_ops,
+ .priv_auto_alloc_size = sizeof(struct sifive_spi),
+ .probe = sifive_spi_probe,
+};
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a71b9be5fb..bdf8dc6fef 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -17,6 +17,15 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
+config IMX_SCU_THERMAL
+ bool "Temperature sensor driver for NXP i.MX8"
+ depends on ARCH_IMX8
+ help
+ Support for Temperature sensors on NXP i.MX8.
+ It supports one critical trip point and one passive trip point. The
+ boot is hold to the cool device to throttle CPUs when the passive
+ trip is crossed
+
config TI_DRA7_THERMAL
bool "Temperature sensor driver for TI dra7xx SOCs"
help
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index cc75e387e4..ef2929d180 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -5,4 +5,5 @@
obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
+obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o
diff --git a/drivers/thermal/imx_scu_thermal.c b/drivers/thermal/imx_scu_thermal.c
new file mode 100644
index 0000000000..7e17377b69
--- /dev/null
+++ b/drivers/thermal/imx_scu_thermal.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 NXP
+ */
+
+#include <config.h>
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <thermal.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+#include <asm/arch/sci/sci.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct imx_sc_thermal_plat {
+ int critical;
+ int alert;
+ int polling_delay;
+ int id;
+ bool zone_node;
+};
+
+static int read_temperature(struct udevice *dev, int *temp)
+{
+ s16 celsius;
+ s8 tenths;
+ int ret;
+
+ sc_rsrc_t *sensor_rsrc = (sc_rsrc_t *)dev_get_driver_data(dev);
+
+ struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
+
+ if (!temp)
+ return -EINVAL;
+
+ ret = sc_misc_get_temp(-1, sensor_rsrc[pdata->id], SC_C_TEMP,
+ &celsius, &tenths);
+ if (ret) {
+ printf("Error: get temperature failed! (error = %d)\n", ret);
+ return ret;
+ }
+
+ *temp = celsius * 1000 + tenths * 100;
+
+ return 0;
+}
+
+int imx_sc_thermal_get_temp(struct udevice *dev, int *temp)
+{
+ struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
+ int cpu_temp = 0;
+ int ret;
+
+ ret = read_temperature(dev, &cpu_temp);
+ if (ret)
+ return ret;
+
+ while (cpu_temp >= pdata->alert) {
+ printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)",
+ cpu_temp, pdata->alert, pdata->critical);
+ puts(" waiting...\n");
+ mdelay(pdata->polling_delay);
+ ret = read_temperature(dev, &cpu_temp);
+ if (ret)
+ return ret;
+ }
+
+ *temp = cpu_temp / 1000;
+
+ return 0;
+}
+
+static const struct dm_thermal_ops imx_sc_thermal_ops = {
+ .get_temp = imx_sc_thermal_get_temp,
+};
+
+static int imx_sc_thermal_probe(struct udevice *dev)
+{
+ debug("%s dev name %s\n", __func__, dev->name);
+ return 0;
+}
+
+static int imx_sc_thermal_bind(struct udevice *dev)
+{
+ struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
+ int reg, ret;
+ int offset;
+ const char *name;
+ const void *prop;
+
+ debug("%s dev name %s\n", __func__, dev->name);
+
+ prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "compatible",
+ NULL);
+ if (!prop)
+ return 0;
+
+ pdata->zone_node = 1;
+
+ reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "tsens-num", 0);
+ if (reg == 0) {
+ printf("%s: no temp sensor number provided!\n", __func__);
+ return -EINVAL;
+ }
+
+ offset = fdt_subnode_offset(gd->fdt_blob, 0, "thermal-zones");
+ fdt_for_each_subnode(offset, gd->fdt_blob, offset) {
+ /* Bind the subnode to this driver */
+ name = fdt_get_name(gd->fdt_blob, offset, NULL);
+
+ ret = device_bind_with_driver_data(dev, dev->driver, name,
+ dev->driver_data,
+ offset_to_ofnode(offset),
+ NULL);
+ if (ret)
+ printf("Error binding driver '%s': %d\n",
+ dev->driver->name, ret);
+ }
+ return 0;
+}
+
+static int imx_sc_thermal_ofdata_to_platdata(struct udevice *dev)
+{
+ struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
+ struct fdtdec_phandle_args args;
+ const char *type;
+ int ret;
+ int trips_np;
+
+ debug("%s dev name %s\n", __func__, dev->name);
+
+ if (pdata->zone_node)
+ return 0;
+
+ ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
+ "thermal-sensors",
+ "#thermal-sensor-cells",
+ 0, 0, &args);
+ if (ret)
+ return ret;
+
+ if (args.node != dev_of_offset(dev->parent))
+ return -EFAULT;
+
+ if (args.args_count >= 1)
+ pdata->id = args.args[0];
+ else
+ pdata->id = 0;
+
+ debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
+
+ pdata->polling_delay = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "polling-delay", 1000);
+
+ trips_np = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(dev),
+ "trips");
+ fdt_for_each_subnode(trips_np, gd->fdt_blob, trips_np) {
+ type = fdt_getprop(gd->fdt_blob, trips_np, "type", NULL);
+ if (type) {
+ if (strcmp(type, "critical") == 0) {
+ pdata->critical = fdtdec_get_int(gd->fdt_blob,
+ trips_np,
+ "temperature",
+ 85);
+ } else if (strcmp(type, "passive") == 0) {
+ pdata->alert = fdtdec_get_int(gd->fdt_blob,
+ trips_np,
+ "temperature",
+ 80);
+ }
+ }
+ }
+
+ debug("id %d polling_delay %d, critical %d, alert %d\n", pdata->id,
+ pdata->polling_delay, pdata->critical, pdata->alert);
+
+ return 0;
+}
+
+static const sc_rsrc_t imx8qxp_sensor_rsrc[] = {
+ SC_R_SYSTEM, SC_R_DRC_0, SC_R_PMIC_0,
+ SC_R_PMIC_1, SC_R_PMIC_2,
+};
+
+static const struct udevice_id imx_sc_thermal_ids[] = {
+ { .compatible = "nxp,imx8qxp-sc-tsens", .data =
+ (ulong)&imx8qxp_sensor_rsrc, },
+ { }
+};
+
+U_BOOT_DRIVER(imx_sc_thermal) = {
+ .name = "imx_sc_thermal",
+ .id = UCLASS_THERMAL,
+ .ops = &imx_sc_thermal_ops,
+ .of_match = imx_sc_thermal_ids,
+ .bind = imx_sc_thermal_bind,
+ .probe = imx_sc_thermal_probe,
+ .ofdata_to_platdata = imx_sc_thermal_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct imx_sc_thermal_plat),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index ee0ddffe73..ccda432f49 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -28,7 +28,7 @@ config BCM2835_WDT
config IMX_WATCHDOG
bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP"
- select HW_WATCHDOG
+ select HW_WATCHDOG if !WDT
help
Select this to enable the IMX and LSCH2 of Layerscape watchdog
driver.
@@ -169,4 +169,11 @@ config WDT_TANGIER
Intel Tangier SoC. If you're using a board with Intel Tangier
SoC, say Y here.
+config SPL_WDT
+ bool "Enable driver model for watchdog timer drivers in SPL"
+ depends on SPL_DM
+ help
+ Enable driver model for watchdog timer in SPL.
+ This is similar to CONFIG_WDT in U-Boot.
+
endmenu
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 68c989aa0b..97aa6a836c 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
-obj-$(CONFIG_WDT) += wdt-uclass.o
+obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
diff --git a/drivers/watchdog/imx_watchdog.c b/drivers/watchdog/imx_watchdog.c
index 14cc618074..53a3e9f5c7 100644
--- a/drivers/watchdog/imx_watchdog.c
+++ b/drivers/watchdog/imx_watchdog.c
@@ -5,7 +5,9 @@
*/
#include <common.h>
+#include <dm.h>
#include <asm/io.h>
+#include <wdt.h>
#include <watchdog.h>
#include <asm/arch/imx-regs.h>
#ifdef CONFIG_FSL_LSCH2
@@ -13,20 +15,40 @@
#endif
#include <fsl_wdog.h>
-#ifdef CONFIG_IMX_WATCHDOG
-void hw_watchdog_reset(void)
+static void imx_watchdog_expire_now(struct watchdog_regs *wdog)
+{
+ clrsetbits_le16(&wdog->wcr, WCR_WT_MSK, WCR_WDE);
+
+ writew(0x5555, &wdog->wsr);
+ writew(0xaaaa, &wdog->wsr); /* load minimum 1/2 second timeout */
+ while (1) {
+ /*
+ * spin for .5 seconds before reset
+ */
+ }
+}
+
+#if !defined(CONFIG_IMX_WATCHDOG) || \
+ (defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
+void __attribute__((weak)) reset_cpu(ulong addr)
{
-#ifndef CONFIG_WATCHDOG_RESET_DISABLE
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
+ imx_watchdog_expire_now(wdog);
+}
+#endif
+
+#if defined(CONFIG_IMX_WATCHDOG)
+static void imx_watchdog_reset(struct watchdog_regs *wdog)
+{
+#ifndef CONFIG_WATCHDOG_RESET_DISABLE
writew(0x5555, &wdog->wsr);
writew(0xaaaa, &wdog->wsr);
#endif /* CONFIG_WATCHDOG_RESET_DISABLE*/
}
-void hw_watchdog_init(void)
+static void imx_watchdog_init(struct watchdog_regs *wdog)
{
- struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
u16 timeout;
/*
@@ -44,21 +66,86 @@ void hw_watchdog_init(void)
writew(WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_WDT | WCR_SRS |
WCR_WDA | SET_WCR_WT(timeout), &wdog->wcr);
#endif /* CONFIG_FSL_LSCH2*/
- hw_watchdog_reset();
+ imx_watchdog_reset(wdog);
}
-#endif
-void __attribute__((weak)) reset_cpu(ulong addr)
+#if !CONFIG_IS_ENABLED(WDT)
+void hw_watchdog_reset(void)
{
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
- clrsetbits_le16(&wdog->wcr, WCR_WT_MSK, WCR_WDE);
+ imx_watchdog_reset(wdog);
+}
- writew(0x5555, &wdog->wsr);
- writew(0xaaaa, &wdog->wsr); /* load minimum 1/2 second timeout */
- while (1) {
- /*
- * spin for .5 seconds before reset
- */
- }
+void hw_watchdog_init(void)
+{
+ struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
+
+ imx_watchdog_init(wdog);
+}
+#else
+struct imx_wdt_priv {
+ void __iomem *base;
+};
+
+static int imx_wdt_reset(struct udevice *dev)
+{
+ struct imx_wdt_priv *priv = dev_get_priv(dev);
+
+ imx_watchdog_reset(priv->base);
+
+ return 0;
+}
+
+static int imx_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+ struct imx_wdt_priv *priv = dev_get_priv(dev);
+
+ imx_watchdog_expire_now(priv->base);
+ hang();
+
+ return 0;
+}
+
+static int imx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+ struct imx_wdt_priv *priv = dev_get_priv(dev);
+
+ imx_watchdog_init(priv->base);
+
+ return 0;
+}
+
+static int imx_wdt_probe(struct udevice *dev)
+{
+ struct imx_wdt_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -ENOENT;
+
+ return 0;
}
+
+static const struct wdt_ops imx_wdt_ops = {
+ .start = imx_wdt_start,
+ .reset = imx_wdt_reset,
+ .expire_now = imx_wdt_expire_now,
+};
+
+static const struct udevice_id imx_wdt_ids[] = {
+ { .compatible = "fsl,imx21-wdt" },
+ {}
+};
+
+U_BOOT_DRIVER(imx_wdt) = {
+ .name = "imx_wdt",
+ .id = UCLASS_WDT,
+ .of_match = imx_wdt_ids,
+ .probe = imx_wdt_probe,
+ .ops = &imx_wdt_ops,
+ .priv_auto_alloc_size = sizeof(struct imx_wdt_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
+#endif
+#endif