From a9092710c5821c4b0d95559c788f13d6804cbd63 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:36 +0200 Subject: dm: Fix documentation entry as there is no UCLASS_CLOCK uclass There is no UCLASS_CLOCK uclass defined. Instead we do use the UCLASS_CLK. Signed-off-by: Lukasz Majewski Reviewed-by: Simon Glass Reviewed-by: Peng Fan --- include/clk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/clk.h b/include/clk.h index d24e99713a3..a909b71f1ae 100644 --- a/include/clk.h +++ b/include/clk.h @@ -20,7 +20,7 @@ * clock provider. This API provides a standard means for drivers to enable and * disable clocks, and to set the rate at which they oscillate. * - * A driver that implements UCLASS_CLOCK is a clock provider. A provider will + * A driver that implements UCLASS_CLK is a clock provider. A provider will * often implement multiple separate clocks, since the hardware it manages * often has this capability. clk-uclass.h describes the interface which * clock providers must implement. -- cgit v1.3.1 From 105db9593eb53ab85a48bcc2187ff8770b7958ec Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:38 +0200 Subject: clk: Extend struct clk to provide information regarding clock rate This commit extends the struct clk to provide information regarding the clock rate. As a result the clock tree traversal is performed at most once, and further reads are using the cached value. Signed-off-by: Lukasz Majewski --- include/clk.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/clk.h b/include/clk.h index a909b71f1ae..d7b937ca7bb 100644 --- a/include/clk.h +++ b/include/clk.h @@ -40,6 +40,7 @@ struct udevice; * other clock APIs to identify which clock signal to operate upon. * * @dev: The device which implements the clock signal. + * @rate: The clock rate (in HZ). * @id: The clock signal ID within the provider. * @data: An optional data field for scenarios where a single integer ID is not * sufficient. If used, it can be populated through an .of_xlate op and @@ -55,6 +56,7 @@ struct udevice; */ struct clk { struct udevice *dev; + long long rate; /* in HZ */ /* * Written by of_xlate. In the future, we might add more fields here. */ -- cgit v1.3.1 From a8592cdd5150ad3766db9e2d738db53a4422bc4e Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:39 +0200 Subject: clk: Extend struct clk to provide clock type agnostic flags This commit extends the struct clk to provide information regarding the flags related to this devices. Those flags are clk device agnostic and indicate generic features (like e.g. CLK_GET_RATE_NOCACHE - the need to always recalculate the rate). Signed-off-by: Lukasz Majewski Reviewed-by: Peng Fan --- include/clk.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/clk.h b/include/clk.h index d7b937ca7bb..b10c0013b14 100644 --- a/include/clk.h +++ b/include/clk.h @@ -41,6 +41,9 @@ struct udevice; * * @dev: The device which implements the clock signal. * @rate: The clock rate (in HZ). + * @flags: Flags used across common clock structure (e.g. CLK_) + * Clock IP blocks specific flags (i.e. mux, div, gate, etc) are defined + * in struct's for those devices (e.g. struct clk_mux). * @id: The clock signal ID within the provider. * @data: An optional data field for scenarios where a single integer ID is not * sufficient. If used, it can be populated through an .of_xlate op and @@ -57,6 +60,7 @@ struct udevice; struct clk { struct udevice *dev; long long rate; /* in HZ */ + u32 flags; /* * Written by of_xlate. In the future, we might add more fields here. */ -- cgit v1.3.1 From 004c1229414ff4a13dd20f48f9c8c1da98fee878 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:41 +0200 Subject: clk: Introduce clk-provider.h to store Common Clock Framework's internals This file now stores the dev_get_clk_ptr() wrapper on the dev_get_uclass_priv() function. Signed-off-by: Lukasz Majewski --- include/linux/clk-provider.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 include/linux/clk-provider.h (limited to 'include') diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h new file mode 100644 index 00000000000..6adf0a57913 --- /dev/null +++ b/include/linux/clk-provider.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + * + * Copyright (c) 2010-2011 Jeremy Kerr + * Copyright (C) 2011-2012 Linaro Ltd + */ +#ifndef __LINUX_CLK_PROVIDER_H +#define __LINUX_CLK_PROVIDER_H + +static inline struct clk *dev_get_clk_ptr(struct udevice *dev) +{ + return (struct clk *)dev_get_uclass_priv(dev); +} +#endif /* __LINUX_CLK_PROVIDER_H */ -- cgit v1.3.1 From 0c660c2b3263ba1d6e3c0dd43d813ef17b051207 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:42 +0200 Subject: dm: clk: Define clk_get_parent() for clk operations This commit adds the clk_get_parent() function, which is responsible for getting the parent's struct clock pointer. U-Boot's DM support for getting parent is different (the parent relationship is in udevice) than the one in Common Clock Framework [CCF] in Linux. To obtain the pointer to struct clk of parent the pdev->uclass_priv field is read via dev_get_clk_ptr() wrapper. Signed-off-by: Lukasz Majewski Reviewed-by: Peng Fan --- drivers/clk/clk-uclass.c | 16 ++++++++++++++++ include/clk.h | 9 +++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 79b3b0494c6..4346c61eea1 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -13,6 +13,7 @@ #include #include #include +#include static inline const struct clk_ops *clk_dev_ops(struct udevice *dev) { @@ -379,6 +380,21 @@ 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; +} + ulong clk_set_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops = clk_dev_ops(clk->dev); diff --git a/include/clk.h b/include/clk.h index b10c0013b14..e20641ee982 100644 --- a/include/clk.h +++ b/include/clk.h @@ -258,6 +258,15 @@ int clk_free(struct clk *clk); */ ulong clk_get_rate(struct clk *clk); +/** + * clk_get_parent() - Get current clock's parent. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @return pointer to parent's struct clk, or error code passed as pointer + */ +struct clk *clk_get_parent(struct clk *clk); + /** * clk_set_rate() - Set current clock rate. * -- cgit v1.3.1 From 4aa78300a025b7e09aa8e902b2178b1870ef1ec5 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:43 +0200 Subject: dm: clk: Define clk_get_parent_rate() for clk operations This commit adds the clk_get_parent_rate() function, which is responsible for getting the rate of parent clock. Unfortunately, u-boot's DM support for getting parent is different (the parent relationship is in udevice) than the one in Common Clock Framework [CCF] in Linux. To alleviate this problem - the clk_get_parent_rate() function has been introduced to clk-uclass.c. Signed-off-by: Lukasz Majewski Reviewed-by: Peng Fan --- drivers/clk/clk-uclass.c | 22 ++++++++++++++++++++++ include/clk.h | 9 +++++++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 4346c61eea1..899b2dda6f3 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -395,6 +395,28 @@ struct clk *clk_get_parent(struct clk *clk) 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 */ + if (!pclk->rate) + 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); diff --git a/include/clk.h b/include/clk.h index e20641ee982..7b2ff8ebe60 100644 --- a/include/clk.h +++ b/include/clk.h @@ -267,6 +267,15 @@ ulong clk_get_rate(struct clk *clk); */ struct clk *clk_get_parent(struct clk *clk); +/** + * clk_get_parent_rate() - Get parent of current clock rate. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @return clock rate in Hz, or -ve error code. + */ +long long clk_get_parent_rate(struct clk *clk); + /** * clk_set_rate() - Set current clock rate. * -- cgit v1.3.1 From 2796af7368acab27a6f9141cecba78106891f1bf Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:44 +0200 Subject: dm: clk: Define clk_get_by_id() for clk operations This commit adds the clk_get_by_id() function, which is responsible for getting the udevice with matching clk->id. Such approach allows re-usage of inherit DM list relationship for the same class (UCLASS_CLK). As a result - we don't need any other external list - it is just enough to look for UCLASS_CLK related udevices. Signed-off-by: Lukasz Majewski Reviewed-by: Peng Fan --- drivers/clk/clk-uclass.c | 22 ++++++++++++++++++++++ include/clk.h | 11 +++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 899b2dda6f3..506ba6014c9 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -491,6 +491,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/include/clk.h b/include/clk.h index 7b2ff8ebe60..f8f56d9cf01 100644 --- a/include/clk.h +++ b/include/clk.h @@ -345,4 +345,15 @@ static inline bool clk_valid(struct clk *clk) { return !!clk->dev; } + +/** + * clk_get_by_id() - Get the clock by its ID + * + * @id: The clock ID to search for + * + * @clkp: A pointer to clock struct that has been found among added clocks + * to UCLASS_CLK + * @return zero on success, or -ENOENT on error + */ +int clk_get_by_id(ulong id, struct clk **clkp); #endif -- cgit v1.3.1 From 1d7993d1d0efb7727de29ebbe164059b7bf71983 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:45 +0200 Subject: clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12) This patch brings the files from Linux kernel (linux-stable/linux-5.1.y SHA1: 5752b50477da)to provide clocks support as it is used on the Linux kernel with Common Clock Framework [CCF] setup. The directory structure has been preserved. The ported code only supports reading information from PLL, MUX, Divider, etc and enabling/disabling the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic to the alias numbering as the information about the clock is read from the device tree. One needs to pay attention to the comments indicating necessary for U-Boot's driver model changes. If needed, the code can be extended to support the "set" part of the clock management. Signed-off-by: Lukasz Majewski --- drivers/clk/Kconfig | 14 ++++ drivers/clk/Makefile | 2 + drivers/clk/clk-divider.c | 147 +++++++++++++++++++++++++++++++++ drivers/clk/clk-fixed-factor.c | 80 ++++++++++++++++++ drivers/clk/clk-mux.c | 164 +++++++++++++++++++++++++++++++++++++ drivers/clk/clk.c | 57 +++++++++++++ drivers/clk/imx/Kconfig | 16 ++++ drivers/clk/imx/Makefile | 2 + drivers/clk/imx/clk-gate2.c | 103 ++++++++++++++++++++++++ drivers/clk/imx/clk-imx6q.c | 179 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk-pfd.c | 90 +++++++++++++++++++++ drivers/clk/imx/clk-pllv3.c | 82 +++++++++++++++++++ drivers/clk/imx/clk.h | 69 ++++++++++++++++ include/linux/clk-provider.h | 109 +++++++++++++++++++++++++ 14 files changed, 1114 insertions(+) create mode 100644 drivers/clk/clk-divider.c create mode 100644 drivers/clk/clk-fixed-factor.c create mode 100644 drivers/clk/clk-mux.c create mode 100644 drivers/clk/clk.c create mode 100644 drivers/clk/imx/clk-gate2.c create mode 100644 drivers/clk/imx/clk-imx6q.c create mode 100644 drivers/clk/imx/clk-pfd.c create mode 100644 drivers/clk/imx/clk-pllv3.c create mode 100644 drivers/clk/imx/clk.h (limited to 'include') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 96969b9e30e..9c2b284d023 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 + 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) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 719b9b8e024..1a45bf35059 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 += imx/ obj-y += tegra/ diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c new file mode 100644 index 00000000000..3348d97829b --- /dev/null +++ b/drivers/clk/clk-divider.c @@ -0,0 +1,147 @@ +// 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 + * Copyright (C) 2011 Richard Zhao, Linaro + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; + + val = readl(divider->reg) >> 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; + + /* 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 00000000000..711b0588bc3 --- /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 + */ +#include +#include +#include +#include +#include +#include +#include +#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 00000000000..14b9f2b1d9e --- /dev/null +++ b/drivers/clk/clk-mux.c @@ -0,0 +1,164 @@ +// 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 + * Copyright (C) 2011 Richard Zhao, Linaro + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#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; + + val = readl(mux->reg) >> 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; + + 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.c b/drivers/clk/clk.c new file mode 100644 index 00000000000..7d748c9fc73 --- /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 +#include +#include +#include +#include +#include +#include + +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/imx/Kconfig b/drivers/clk/imx/Kconfig index a6fb58d6cf6..3e6a980c8c3 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 eb379c188ae..105a58ca907 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 00000000000..571be320883 --- /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 + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#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 00000000000..92e9337d446 --- /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 +#include +#include +#include +#include +#include + +#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 00000000000..188b2b3b90a --- /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 +#include +#include +#include +#include +#include +#include +#include +#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 00000000000..fbb7b24d5e2 --- /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 +#include +#include +#include +#include +#include +#include +#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 00000000000..e6d51830e88 --- /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 + +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/include/linux/clk-provider.h b/include/linux/clk-provider.h index 6adf0a57913..e06487f07b4 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -9,6 +9,115 @@ #ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H +static inline void clk_dm(ulong id, struct clk *clk) +{ + if (!IS_ERR(clk)) + clk->id = id; +} + +/* + * flags used across common struct clk. these flags should only affect the + * top-level framework. custom flags for dealing with hardware specifics + * belong in struct clk_foo + * + * Please update clk_flags[] in drivers/clk/clk.c when making changes here! + */ +#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */ +#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */ +#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ +#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ + /* unused */ +#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ +#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ +#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ +#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ +#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ +#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ +#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ +/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_OPS_PARENT_ENABLE BIT(12) +/* duty cycle call may be forwarded to the parent clock */ +#define CLK_DUTY_CYCLE_PARENT BIT(13) + +#define CLK_MUX_INDEX_ONE BIT(0) +#define CLK_MUX_INDEX_BIT BIT(1) +#define CLK_MUX_HIWORD_MASK BIT(2) +#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */ +#define CLK_MUX_ROUND_CLOSEST BIT(4) + +struct clk_mux { + struct clk clk; + void __iomem *reg; + u32 *table; + u32 mask; + u8 shift; + u8 flags; + + /* + * Fields from struct clk_init_data - this struct has been + * omitted to avoid too deep level of CCF for bootloader + */ + const char * const *parent_names; + u8 num_parents; +}; + +#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk) + +struct clk_div_table { + unsigned int val; + unsigned int div; +}; + +struct clk_divider { + struct clk clk; + void __iomem *reg; + u8 shift; + u8 width; + u8 flags; + const struct clk_div_table *table; +}; + +#define clk_div_mask(width) ((1 << (width)) - 1) +#define to_clk_divider(_clk) container_of(_clk, struct clk_divider, clk) + +#define CLK_DIVIDER_ONE_BASED BIT(0) +#define CLK_DIVIDER_POWER_OF_TWO BIT(1) +#define CLK_DIVIDER_ALLOW_ZERO BIT(2) +#define CLK_DIVIDER_HIWORD_MASK BIT(3) +#define CLK_DIVIDER_ROUND_CLOSEST BIT(4) +#define CLK_DIVIDER_READ_ONLY BIT(5) +#define CLK_DIVIDER_MAX_AT_ZERO BIT(6) + +struct clk_fixed_factor { + struct clk clk; + unsigned int mult; + unsigned int div; +}; + +#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\ + clk) + +int clk_register(struct clk *clk, const char *drv_name, const char *name, + const char *parent_name); + +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_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_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); + +const char *clk_hw_get_name(const struct clk *hw); +ulong clk_generic_get_rate(struct clk *clk); + static inline struct clk *dev_get_clk_ptr(struct udevice *dev) { return (struct clk *)dev_get_uclass_priv(dev); -- cgit v1.3.1 From 6bb15d6f07a8348cca07f2f245f3025cb79e7680 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:48 +0200 Subject: clk: sandbox: Adjust clk-divider to emulate reading its value from HW The generic divider clock code for CCF requires reading the divider value from HW registers. As sandbox by design has readl() as no-op it was necessary to provide this value in the other way. The new field in the divider structure (accessible only when sandbox is run) has been introduced for this purpose. Signed-off-by: Lukasz Majewski --- drivers/clk/clk-divider.c | 10 +++++++++- include/linux/clk-provider.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 3348d97829b..6921c76a48f 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -74,7 +74,12 @@ static ulong clk_divider_recalc_rate(struct clk *clk) unsigned long parent_rate = clk_get_parent_rate(clk); unsigned int val; - val = readl(divider->reg) >> divider->shift; +#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, @@ -112,6 +117,9 @@ static struct clk *_register_divider(struct device *dev, const char *name, 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; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index e06487f07b4..53c9c41b90d 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -75,6 +75,9 @@ struct clk_divider { u8 width; u8 flags; const struct clk_div_table *table; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + u32 io_divider_val; +#endif }; #define clk_div_mask(width) ((1 << (width)) - 1) -- cgit v1.3.1 From 5da0095e3a670fe61a3421a2a826514a61a687e0 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:49 +0200 Subject: clk: sandbox: Adjust clk-mux.c to emulate reading divider value from HW The generic mux clock code for CCF requires reading the clock multiplexer value from HW registers. As sandbox by design has readl() as no-op it was necessary to provide this value in the other way. The new field in the mux structure (accessible only when sandbox is run) has been introduced for this purpose. Signed-off-by: Lukasz Majewski --- drivers/clk/clk-mux.c | 10 +++++++++- include/linux/clk-provider.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 14b9f2b1d9e..3c075aa09ec 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -64,7 +64,12 @@ static u8 clk_mux_get_parent(struct clk *clk) struct clk_mux *mux = to_clk_mux(clk); u32 val; - val = readl(mux->reg) >> mux->shift; +#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); @@ -108,6 +113,9 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, 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; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 53c9c41b90d..43a25e9c6a8 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -59,6 +59,10 @@ struct clk_mux { */ const char * const *parent_names; u8 num_parents; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + u32 io_mux_val; +#endif + }; #define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk) -- cgit v1.3.1 From 87e460c304ad6030bc2aab89edd44e433290d5bc Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 24 Jun 2019 15:50:50 +0200 Subject: clk: sandbox: Add sandbox test code for Common Clock Framework [CCF] This patch provides code to implement the CCF clock tree in sandbox. It uses all the introduced primitives; some generic ones are reused, some sandbox specific were developed. In that way (after introducing the real CCF tree in sandbox) the recently added to clk-uclass.c: clk_get_by_id() and clk_get_parent_rate() are tested in their natural work environment. Usage (sandbox_defconfig and sandbox_flattree_defconfig): ./u-boot --fdt arch/sandbox/dts/test.dtb --command "ut dm clk_ccf" Signed-off-by: Lukasz Majewski --- arch/sandbox/dts/test.dts | 4 + drivers/clk/Kconfig | 10 ++- drivers/clk/Makefile | 1 + drivers/clk/clk_sandbox_ccf.c | 185 ++++++++++++++++++++++++++++++++++++++++++ include/sandbox-clk.h | 76 +++++++++++++++++ test/dm/Makefile | 2 +- test/dm/clk_ccf.c | 62 ++++++++++++++ 7 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/clk_sandbox_ccf.c create mode 100644 include/sandbox-clk.h create mode 100644 test/dm/clk_ccf.c (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 0dd5e8da5ad..9b65adc3f19 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -232,6 +232,10 @@ clock-names = "fixed", "i2c", "spi"; }; + ccf: clk-ccf { + compatible = "sandbox,clk-ccf"; + }; + eth@10002000 { compatible = "sandbox,eth"; reg = <0x10002000 0x1000>; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9c2b284d023..9399bb79e9a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -55,7 +55,7 @@ config SPL_CLK_CCF config CLK_CCF bool "Common Clock Framework [CCF] support " - depends on CLK_IMX6Q + 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. @@ -138,4 +138,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 1a45bf35059..37dfd281e94 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -38,5 +38,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_sandbox_ccf.c b/drivers/clk/clk_sandbox_ccf.c new file mode 100644 index 00000000000..edeb0f2cf3e --- /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 +#include +#include +#include +#include +#include +#include + +/* + * 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", ®, 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", ®, 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", ®, 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/include/sandbox-clk.h b/include/sandbox-clk.h new file mode 100644 index 00000000000..37c9838f765 --- /dev/null +++ b/include/sandbox-clk.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#ifndef __SANDBOX_CLK_H__ +#define __SANDBOX_CLK_H__ + +#include + +enum { + SANDBOX_CLK_PLL2 = 1, + SANDBOX_CLK_PLL3, + SANDBOX_CLK_PLL3_60M, + SANDBOX_CLK_PLL3_80M, + SANDBOX_CLK_ECSPI_ROOT, + SANDBOX_CLK_ECSPI0, + SANDBOX_CLK_ECSPI1, + SANDBOX_CLK_USDHC1_SEL, + SANDBOX_CLK_USDHC2_SEL, +}; + +enum sandbox_pllv3_type { + SANDBOX_PLLV3_GENERIC, + SANDBOX_PLLV3_USB, +}; + +struct clk *sandbox_clk_pllv3(enum sandbox_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask); + +static inline struct clk *sandbox_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 *sandbox_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 *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_gate_flags); + +static inline struct clk *sandbox_clk_gate2(const char *name, + const char *parent, + void __iomem *reg, u8 shift) +{ + return sandbox_clk_register_gate2(NULL, name, parent, + CLK_SET_RATE_PARENT, reg, shift, + 0x3, 0); +} + +static inline struct clk *sandbox_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 /* __SANDBOX_CLK_H__ */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 6a36cc0a328..9c5e8601086 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_SOUND) += audio.o obj-$(CONFIG_BLK) += blk.o obj-$(CONFIG_BOARD) += board.o obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o -obj-$(CONFIG_CLK) += clk.o +obj-$(CONFIG_CLK) += clk.o clk_ccf.o obj-$(CONFIG_DM_ETH) += eth.o obj-$(CONFIG_FIRMWARE) += firmware.o obj-$(CONFIG_DM_GPIO) += gpio.o diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c new file mode 100644 index 00000000000..8d397593a32 --- /dev/null +++ b/test/dm/clk_ccf.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Tests for Common Clock Framework driver */ +static int dm_test_clk_ccf(struct unit_test_state *uts) +{ + struct clk *clk, *pclk; + struct udevice *dev; + long long rate; + int ret; + + /* Get the device using the clk device */ + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-ccf", &dev)); + + /* Test for clk_get_by_id() */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI_ROOT, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi_root", clk->dev->name); + + /* Test for clk_get_parent_rate() */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI1, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi1", clk->dev->name); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 20000000); + + /* Test the mux of CCF */ + ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc1_sel", clk->dev->name); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 60000000); + + ret = clk_get_by_id(SANDBOX_CLK_USDHC2_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc2_sel", clk->dev->name); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 80000000); + + pclk = clk_get_parent(clk); + ut_asserteq_str("pll3_80m", pclk->dev->name); + + return 1; +} + +DM_TEST(dm_test_clk_ccf, DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 1d43e24b946e99cb99839a7a9607db62224fb3fc Mon Sep 17 00:00:00 2001 From: Shyam Saini Date: Fri, 14 Jun 2019 13:05:33 +0530 Subject: i.MX6: nand: add nandbcb command for imx Writing/updating boot image in nand device is not straight forward in i.MX6 platform and it requires boot control block(BCB) to be configured. It becomes difficult to use uboot 'nand' command to write BCB since it requires platform specific attributes need to be taken care of. It is even difficult to use existing msx-nand.c driver by incorporating BCB attributes like mxs_dma_desc does because it requires change in mtd and nand command. So, cmd_nandbcb implemented in arch/arm/mach-imx BCB contains two data structures, Firmware Configuration Block(FCB) and Discovered Bad Block Table(DBBT). FCB has nand timings, DBBT search area, page address of firmware. On summary, nandbcb update will - erase the entire partition - create BCB by creating 2 FCB/DBBT block followed by 1 FW block based on partition size and erasesize. - fill FCB/DBBT structures - write FW/SPL on FW1 - write FCB/DBBT in first 2 blocks for nand boot, up on reset bootrom look for FCB structure in first block's if FCB found the nand timings are loaded for further reads. once FCB read done, DTTB will load and finally firmware will be loaded which is boot image. Refer section "NAND Boot" from doc/imx/common/imx6.txt for more usage information. Reviewed-by: Stefano Babic Signed-off-by: Jagan Teki Signed-off-by: Sergey Kubushyn Signed-off-by: Shyam Saini --- arch/arm/include/asm/mach-imx/imx-nandbcb.h | 111 +++++++++ arch/arm/mach-imx/Kconfig | 11 + arch/arm/mach-imx/Makefile | 1 + arch/arm/mach-imx/cmd_nandbcb.c | 369 ++++++++++++++++++++++++++++ drivers/mtd/nand/raw/mxs_nand.c | 2 +- drivers/mtd/nand/raw/mxs_nand.h | 73 ------ drivers/mtd/nand/raw/mxs_nand_dt.c | 2 +- drivers/mtd/nand/raw/mxs_nand_spl.c | 2 +- include/mxs_nand.h | 73 ++++++ 9 files changed, 568 insertions(+), 76 deletions(-) create mode 100644 arch/arm/include/asm/mach-imx/imx-nandbcb.h create mode 100644 arch/arm/mach-imx/cmd_nandbcb.c delete mode 100644 drivers/mtd/nand/raw/mxs_nand.h create mode 100644 include/mxs_nand.h (limited to 'include') diff --git a/arch/arm/include/asm/mach-imx/imx-nandbcb.h b/arch/arm/include/asm/mach-imx/imx-nandbcb.h new file mode 100644 index 00000000000..033659a0389 --- /dev/null +++ b/arch/arm/include/asm/mach-imx/imx-nandbcb.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 Jagan Teki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _IMX_NAND_BCB_H_ +#define _IMX_NAND_BCB_H_ + +#define FCB_FINGERPRINT 0x20424346 /* 'FCB' */ +#define FCB_VERSION_1 0x01000000 + +#define DBBT_FINGERPRINT2 0x54424244 /* 'DBBT' */ +#define DBBT_VERSION_1 0x01000000 + +struct dbbt_block { + u32 checksum; /* reserved on i.MX6 */ + u32 fingerprint; + u32 version; + u32 numberbb; /* reserved on i.MX6 */ + u32 dbbtpages; +}; + +struct fcb_block { + u32 checksum; /* First fingerprint in first byte */ + u32 fingerprint; /* 2nd fingerprint at byte 4 */ + u32 version; /* 3rd fingerprint at byte 8 */ + u8 datasetup; + u8 datahold; + u8 addr_setup; + u8 dsample_time; + + /* These are for application use only and not for ROM. */ + u8 nandtiming; + u8 rea; + u8 rloh; + u8 rhoh; + u32 pagesize; /* 2048 for 2K pages, 4096 for 4K pages */ + u32 oob_pagesize; /* 2112 for 2K pages, 4314 for 4K pages */ + u32 sectors; /* Number of 2K sections per block */ + u32 nr_nand; /* Total Number of NANDs - not used by ROM */ + u32 nr_die; /* Number of separate chips in this NAND */ + u32 celltype; /* MLC or SLC */ + u32 ecc_type; /* Type of ECC, can be one of BCH-0-20 */ + u32 ecc_nr; /* Number of bytes for Block0 - BCH */ + + /* Block size in bytes for all blocks other than Block0 - BCH */ + u32 ecc_size; + u32 ecc_level; /* Ecc level for Block 0 - BCH */ + u32 meta_size; /* Metadata size - BCH */ + /* Number of blocks per page for ROM use - BCH */ + u32 nr_blocks; + u32 ecc_type_sdk; /* Type of ECC, can be one of BCH-0-20 */ + u32 ecc_nr_sdk; /* Number of bytes for Block0 - BCH */ + /* Block size in bytes for all blocks other than Block0 - BCH */ + u32 ecc_size_sdk; + u32 ecc_level_sdk; /* Ecc level for Block 0 - BCH */ + /* Number of blocks per page for SDK use - BCH */ + u32 nr_blocks_sdk; + u32 meta_size_sdk; /* Metadata size - BCH */ + u32 erase_th; /* To set into BCH_MODE register */ + + /* + * 0: normal boot + * 1: to load patch starting next to FCB + */ + u32 bootpatch; + u32 patch_size; /* Size of patch in sectors */ + u32 fw1_start; /* Firmware image starts on this sector */ + u32 fw2_start; /* Secondary FW Image starting Sector */ + u32 fw1_pages; /* Number of sectors in firmware image */ + u32 fw2_pages; /* Number of sector in secondary FW image */ + u32 dbbt_start; /* Page address where dbbt search area begins */ + + /* + * Byte in page data that have manufacturer marked bad block marker, + * this will be swapped with metadata[0] to complete page data. + */ + u32 bb_byte; + + /* + * For BCH ECC sizes other than 8 and 16 the bad block marker does not + * start at 0th bit of bb_byte. This field is used to get to + * the start bit of bad block marker byte with in bb_byte + */ + u32 bb_start_bit; + + /* + * FCB value that gives byte offset for + * bad block marker on physical NAND page + */ + u32 phy_offset; + u32 bchtype; + + u32 readlatency; + u32 predelay; + u32 cedelay; + u32 postdelay; + u32 cmdaddpause; + u32 datapause; + u32 tmspeed; + u32 busytimeout; + + /* the flag to enable (1)/disable(0) bi swap */ + u32 disbbm; + + /* The swap position of main area in spare area */ + u32 spare_offset; +}; + +#endif /* _IMX_NAND_BCB_H_ */ diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index b6fd1595f04..aeb54934888 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -71,6 +71,17 @@ config CMD_HDMIDETECT This enables the 'hdmidet' command which detects if an HDMI monitor is connected. +config CMD_NANDBCB + bool "i.MX6 NAND Boot Control Block(BCB) command" + depends on NAND && CMD_MTDPARTS + default y if ARCH_MX6 && NAND_MXS + help + Unlike normal 'nand write/erase' commands, this command update + Boot Control Block(BCB) for i.MX6 platform NAND IP's. + + This is similar to kobs-ng, which is used in Linux as separate + rootfs package. + config NXP_BOARD_REVISION bool "Read NXP board revision from fuses" depends on ARCH_MX6 || ARCH_MX7 diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 898478fc4a4..08ee52edbff 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -59,6 +59,7 @@ ifneq ($(CONFIG_SPL_BUILD),y) obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o +obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o endif PLUGIN = board/$(BOARDDIR)/plugin diff --git a/arch/arm/mach-imx/cmd_nandbcb.c b/arch/arm/mach-imx/cmd_nandbcb.c new file mode 100644 index 00000000000..065b814b2e3 --- /dev/null +++ b/arch/arm/mach-imx/cmd_nandbcb.c @@ -0,0 +1,369 @@ +/* + * i.MX6 nand boot control block(bcb). + * + * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng + * + * Copyright (C) 2017 Jagan Teki + * Copyright (C) 2016 Sergey Kubushyn + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET) +#define GETBIT(v, n) (((v) >> (n)) & 0x1) + +static u8 calculate_parity_13_8(u8 d) +{ + u8 p = 0; + + p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0; + p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^ + GETBIT(d, 1)) << 1; + p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^ + GETBIT(d, 0)) << 2; + p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3; + p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^ + GETBIT(d, 1) ^ GETBIT(d, 0)) << 4; + + return p; +} + +static void encode_hamming_13_8(void *_src, void *_ecc, size_t size) +{ + int i; + u8 *src = _src; + u8 *ecc = _ecc; + + for (i = 0; i < size; i++) + ecc[i] = calculate_parity_13_8(src[i]); +} + +static u32 calc_chksum(void *buf, size_t size) +{ + u32 chksum = 0; + u8 *bp = buf; + size_t i; + + for (i = 0; i < size; i++) + chksum += bp[i]; + + return ~chksum; +} + +static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + + fcb->fingerprint = FCB_FINGERPRINT; + fcb->version = FCB_VERSION_1; + fcb->pagesize = mtd->writesize; + fcb->oob_pagesize = mtd->writesize + mtd->oobsize; + fcb->sectors = mtd->erasesize / mtd->writesize; + + /* Divide ECC strength by two and save the value into FCB structure. */ + fcb->ecc_level = nand_info->bch_geometry.ecc_strength >> 1; + + fcb->ecc_type = fcb->ecc_level; + + /* Also hardcoded in kobs-ng */ + fcb->ecc_nr = 0x00000200; + fcb->ecc_size = 0x00000200; + fcb->datasetup = 80; + fcb->datahold = 60; + fcb->addr_setup = 25; + fcb->dsample_time = 6; + fcb->meta_size = 10; + + /* DBBT search area starts at second page on first block */ + fcb->dbbt_start = 1; + + fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; + fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; + + fcb->phy_offset = mtd->writesize; + + fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1; + + fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4); +} + +static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks) +{ + int n, n_bad_blocks = 0; + u32 *bb = buf + 0x8; + u32 *n_bad_blocksp = buf + 0x4; + + for (n = 0; n < num_blocks; n++) { + loff_t offset = n * mtd->erasesize; + if (mtd_block_isbad(mtd, offset)) { + n_bad_blocks++; + *bb = n; + bb++; + } + } + + *n_bad_blocksp = n_bad_blocks; + + return n_bad_blocks; +} + +static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size, + size_t maxsize, const u_char *buf) +{ + nand_erase_options_t opts; + struct fcb_block *fcb; + struct dbbt_block *dbbt; + loff_t fw1_off; + void *fwbuf, *fcb_raw_page, *dbbt_page, *dbbt_data_page; + int nr_blks, nr_blks_fcb, fw1_blk; + size_t fwsize, dummy; + int i, ret; + + /* erase */ + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = maxsize - 1; + ret = nand_erase_opts(mtd, &opts); + if (ret) { + printf("%s: erase failed (ret = %d)\n", __func__, ret); + return ret; + } + + /* + * Reference documentation from i.MX6DQRM section 8.5.2.2 + * + * Nand Boot Control Block(BCB) contains two data structures, + * - Firmware Configuration Block(FCB) + * - Discovered Bad Block Table(DBBT) + * + * FCB contains, + * - nand timings + * - DBBT search page address, + * - start page address of primary firmware + * - start page address of secondary firmware + * + * setup fcb: + * - number of blocks = mtd partition size / mtd erasesize + * - two firmware blocks, primary and secondary + * - first 4 block for FCB/DBBT + * - rest split in half for primary and secondary firmware + * - same firmware will write two times + */ + nr_blks_fcb = 2; + nr_blks = maxsize / mtd->erasesize; + fw1_blk = nr_blks_fcb; + + /* write fw */ + fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize, + mtd->writesize); + fwbuf = kzalloc(fwsize, GFP_KERNEL); + if (!fwbuf) { + debug("failed to allocate fwbuf\n"); + ret = -ENOMEM; + goto err; + } + + memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size); + fw1_off = fw1_blk * mtd->erasesize; + ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, + (u_char *)fwbuf, WITH_WR_VERIFY); + printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n", + fw1_off, fwsize, ret ? "ERROR" : "OK"); + if (ret) + goto fwbuf_err; + + /* fill fcb */ + fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); + if (!fcb) { + debug("failed to allocate fcb\n"); + ret = -ENOMEM; + goto fwbuf_err; + } + + fcb->fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize; + fcb->fw1_pages = size / mtd->writesize + 1; + fill_fcb(fcb, mtd); + + /* fill dbbt */ + dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_page) { + debug("failed to allocate dbbt_page\n"); + ret = -ENOMEM; + goto fcb_err; + } + + dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page) { + debug("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + goto dbbt_page_err; + } + + dbbt = dbbt_page; + dbbt->checksum = 0; + dbbt->fingerprint = DBBT_FINGERPRINT2; + dbbt->version = DBBT_VERSION_1; + ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks); + if (ret < 0) + goto dbbt_data_page_err; + else if (ret > 0) + dbbt->dbbtpages = 1; + + /* write fcb/dbbt */ + fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!fcb_raw_page) { + debug("failed to allocate fcb_raw_page\n"); + ret = -ENOMEM; + goto dbbt_data_page_err; + } + + memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); + encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512); + /* + * Set the first and second byte of OOB data to 0xFF, not 0x00. These + * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since + * the FCB is mostly written to the first page in a block, a scan for + * factory bad blocks will detect these blocks as bad, e.g. when + * function nand_scan_bbt() is executed to build a new bad block table. + */ + memset(fcb_raw_page + mtd->writesize, 0xFF, 2); + + for (i = 0; i < nr_blks_fcb; i++) { + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", i); + continue; + } + + /* raw write */ + mtd_oob_ops_t ops = { + .datbuf = (u8 *)fcb_raw_page, + .oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize, + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_RAW + }; + + ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops); + if (ret) + goto fcb_raw_page_err; + debug("NAND fcb write: 0x%x offset, 0x%x bytes written: %s\n", + mtd->erasesize * i, ops.len, ret ? "ERROR" : "OK"); + + ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize, + mtd->writesize, &dummy, dbbt_page); + if (ret) + goto fcb_raw_page_err; + debug("NAND dbbt write: 0x%x offset, 0x%x bytes written: %s\n", + mtd->erasesize * i + mtd->writesize, dummy, + ret ? "ERROR" : "OK"); + + /* dbbtpages == 0 if no bad blocks */ + if (dbbt->dbbtpages > 0) { + loff_t to = (mtd->erasesize * i + mtd->writesize * 5); + + ret = mtd_write(mtd, to, mtd->writesize, &dummy, + dbbt_data_page); + if (ret) + goto fcb_raw_page_err; + } + } + +fcb_raw_page_err: + kfree(fcb_raw_page); +dbbt_data_page_err: + kfree(dbbt_data_page); +dbbt_page_err: + kfree(dbbt_page); +fcb_err: + kfree(fcb); +fwbuf_err: + kfree(fwbuf); +err: + return ret; +} + +static int do_nandbcb_update(int argc, char * const argv[]) +{ + struct mtd_info *mtd; + loff_t addr, offset, size, maxsize; + char *endp; + u_char *buf; + int dev; + int ret; + + if (argc != 4) + return CMD_RET_USAGE; + + dev = nand_curr_device; + if (dev < 0) { + printf("failed to get nand_curr_device, run nand device"); + return CMD_RET_FAILURE; + } + + addr = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return CMD_RET_FAILURE; + + mtd = get_nand_dev_by_index(dev); + if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size, + &maxsize, MTD_DEV_TYPE_NAND, mtd->size)) + return CMD_RET_FAILURE; + + buf = map_physmem(addr, size, MAP_WRBACK); + if (!buf) { + puts("failed to map physical memory\n"); + return CMD_RET_FAILURE; + } + + ret = nandbcb_update(mtd, offset, size, maxsize, buf); + + return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + const char *cmd; + int ret = 0; + + if (argc < 5) + goto usage; + + cmd = argv[1]; + --argc; + ++argv; + + if (strcmp(cmd, "update") == 0) { + ret = do_nandbcb_update(argc, argv); + goto done; + } + +done: + if (ret != -1) + return ret; +usage: + return CMD_RET_USAGE; +} + +static char nandbcb_help_text[] = + "update addr off|partition len - update 'len' bytes starting at\n" + " 'off|part' to memory address 'addr', skipping bad blocks"; + +U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb, + "i.MX6 Nand BCB", + nandbcb_help_text +); diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index b93d77a3951..a41b9620d0a 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -25,7 +25,7 @@ #include #include #include -#include "mxs_nand.h" +#include #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 4bd65cded94..00000000000 --- 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 - */ - -#include -#include -#include -#include - -/** - * @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 44dec5dedf9..8ad7d618c64 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -15,7 +15,7 @@ #include #include -#include "mxs_nand.h" +#include 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 ee7d9cb9571..975a91a37d2 100644 --- a/drivers/mtd/nand/raw/mxs_nand_spl.c +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c @@ -6,7 +6,7 @@ #include #include #include -#include "mxs_nand.h" +#include static struct mtd_info *mtd; static struct nand_chip nand_chip; diff --git a/include/mxs_nand.h b/include/mxs_nand.h new file mode 100644 index 00000000000..4bd65cded94 --- /dev/null +++ b/include/mxs_nand.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * NXP GPMI NAND flash driver + * + * Copyright (C) 2018 Toradex + * Authors: + * Stefan Agner + */ + +#include +#include +#include +#include + +/** + * @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); -- cgit v1.3.1 From 21b02414f109e06f20c0718747187383b982ed88 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 7 May 2019 10:45:55 +0300 Subject: regulator: bd718x7: support ROHM BD71837 and BD71847 PMICs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BD71837 and BD71847 is PMIC intended for powering single-core, dual-core, and quad-core SoC’s such as NXP-i.MX 8M. BD71847 is used for example on NXP imx8mm EVK. Add regulator driver for ROHM BD71837 and BD71847 PMICs. BD71837 contains 8 bucks and 7 LDOS. BD71847 is reduced version containing 6 bucks and 6 LDOs. Voltages for DVS bucks (1-4 on BD71837, 1 and 2 on BD71847) can be adjusted when regulators are enabled. For other bucks and LDOs we may have over- or undershooting if voltage is adjusted when regulator is enabled. Thus this is prevented by default. BD718x7 has a quirk which may leave power output disabled after reset if enable/disable state was controlled by SW. Thus the SW control is only allowed for BD71837 bucks 3 and 4 by default. The impact of this limitation must be evaluated board-by board and restrictions may need to be modified. (Linux driver get's these limitations from DT and we may want to implement same on u-Boot driver). Signed-off-by: Matti Vaittinen Reviewed-by: Simon Glass --- drivers/power/pmic/bd71837.c | 32 ++- drivers/power/regulator/Kconfig | 17 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/bd71837.c | 468 ++++++++++++++++++++++++++++++++++++++ include/power/bd71837.h | 147 +++++++----- 5 files changed, 607 insertions(+), 58 deletions(-) create mode 100644 drivers/power/regulator/bd71837.c (limited to 'include') diff --git a/drivers/power/pmic/bd71837.c b/drivers/power/pmic/bd71837.c index 24d9f7fab73..e292d42a8c6 100644 --- a/drivers/power/pmic/bd71837.c +++ b/drivers/power/pmic/bd71837.c @@ -3,6 +3,8 @@ * Copyright 2018 NXP */ +#define DEBUG + #include #include #include @@ -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 337e9e7471c..9aa00fad42f 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 e728b73aee3..6a3d4bbee4c 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 00000000000..575429aa2d3 --- /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 +#include +#include +#include +#include + +#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/include/power/bd71837.h b/include/power/bd71837.h index 38c69b2b909..75e07e1de31 100644 --- a/include/power/bd71837.h +++ b/include/power/bd71837.h @@ -1,62 +1,103 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (C) 2018 ROHM Semiconductors */ -#ifndef BD71837_H_ -#define BD71837_H_ +#ifndef BD718XX_H_ +#define BD718XX_H_ -#define BD71837_REGULATOR_DRIVER "bd71837_regulator" +#define BD718XX_REGULATOR_DRIVER "bd718x7_regulator" enum { - BD71837_REV = 0x00, - BD71837_SWRESET = 0x01, - BD71837_I2C_DEV = 0x02, - BD71837_PWRCTRL0 = 0x03, - BD71837_PWRCTRL1 = 0x04, - BD71837_BUCK1_CTRL = 0x05, - BD71837_BUCK2_CTRL = 0x06, - BD71837_BUCK3_CTRL = 0x07, - BD71837_BUCK4_CTRL = 0x08, - BD71837_BUCK5_CTRL = 0x09, - BD71837_BUCK6_CTRL = 0x0a, - BD71837_BUCK7_CTRL = 0x0b, - BD71837_BUCK8_CTRL = 0x0c, - BD71837_BUCK1_VOLT_RUN = 0x0d, - BD71837_BUCK1_VOLT_IDLE = 0x0e, - BD71837_BUCK1_VOLT_SUSP = 0x0f, - BD71837_BUCK2_VOLT_RUN = 0x10, - BD71837_BUCK2_VOLT_IDLE = 0x11, - BD71837_BUCK3_VOLT_RUN = 0x12, - BD71837_BUCK4_VOLT_RUN = 0x13, - BD71837_BUCK5_VOLT = 0x14, - BD71837_BUCK6_VOLT = 0x15, - BD71837_BUCK7_VOLT = 0x16, - BD71837_BUCK8_VOLT = 0x17, - BD71837_LDO1_VOLT = 0x18, - BD71837_LDO2_VOLT = 0x19, - BD71837_LDO3_VOLT = 0x1a, - BD71837_LDO4_VOLT = 0x1b, - BD71837_LDO5_VOLT = 0x1c, - BD71837_LDO6_VOLT = 0x1d, - BD71837_LDO7_VOLT = 0x1e, - BD71837_TRANS_COND0 = 0x1f, - BD71837_TRANS_COND1 = 0x20, - BD71837_VRFAULTEN = 0x21, - BD71837_MVRFLTMASK0 = 0x22, - BD71837_MVRFLTMASK1 = 0x23, - BD71837_MVRFLTMASK2 = 0x24, - BD71837_RCVCFG = 0x25, - BD71837_RCVNUM = 0x26, - BD71837_PWRONCONFIG0 = 0x27, - BD71837_PWRONCONFIG1 = 0x28, - BD71837_RESETSRC = 0x29, - BD71837_MIRQ = 0x2a, - BD71837_IRQ = 0x2b, - BD71837_IN_MON = 0x2c, - BD71837_POW_STATE = 0x2d, - BD71837_OUT32K = 0x2e, - BD71837_REGLOCK = 0x2f, - BD71837_MUXSW_EN = 0x30, - BD71837_REG_NUM, + ROHM_CHIP_TYPE_BD71837 = 0, + ROHM_CHIP_TYPE_BD71847, + ROHM_CHIP_TYPE_BD70528, + ROHM_CHIP_TYPE_AMOUNT }; +enum { + BD718XX_REV = 0x00, + BD718XX_SWRESET = 0x01, + BD718XX_I2C_DEV = 0x02, + BD718XX_PWRCTRL0 = 0x03, + BD718XX_PWRCTRL1 = 0x04, + BD718XX_BUCK1_CTRL = 0x05, + BD718XX_BUCK2_CTRL = 0x06, + BD71837_BUCK3_CTRL = 0x07, + BD71837_BUCK4_CTRL = 0x08, + BD718XX_1ST_NODVS_BUCK_CTRL = 0x09, + BD718XX_2ND_NODVS_BUCK_CTRL = 0x0a, + BD718XX_3RD_NODVS_BUCK_CTRL = 0x0b, + BD718XX_4TH_NODVS_BUCK_CTRL = 0x0c, + BD718XX_BUCK1_VOLT_RUN = 0x0d, + BD718XX_BUCK1_VOLT_IDLE = 0x0e, + BD718XX_BUCK1_VOLT_SUSP = 0x0f, + BD718XX_BUCK2_VOLT_RUN = 0x10, + BD718XX_BUCK2_VOLT_IDLE = 0x11, + BD71837_BUCK3_VOLT_RUN = 0x12, + BD71837_BUCK4_VOLT_RUN = 0x13, + BD718XX_1ST_NODVS_BUCK_VOLT = 0x14, + BD718XX_2ND_NODVS_BUCK_VOLT = 0x15, + BD718XX_3RD_NODVS_BUCK_VOLT = 0x16, + BD718XX_4TH_NODVS_BUCK_VOLT = 0x17, + BD718XX_LDO1_VOLT = 0x18, + BD718XX_LDO2_VOLT = 0x19, + BD718XX_LDO3_VOLT = 0x1a, + BD718XX_LDO4_VOLT = 0x1b, + BD718XX_LDO5_VOLT = 0x1c, + BD718XX_LDO6_VOLT = 0x1d, + BD71837_LDO7_VOLT = 0x1e, + BD718XX_TRANS_COND0 = 0x1f, + BD718XX_TRANS_COND1 = 0x20, + BD718XX_VRFAULTEN = 0x21, + BD718XX_MVRFLTMASK0 = 0x22, + BD718XX_MVRFLTMASK1 = 0x23, + BD718XX_MVRFLTMASK2 = 0x24, + BD718XX_RCVCFG = 0x25, + BD718XX_RCVNUM = 0x26, + BD718XX_PWRONCONFIG0 = 0x27, + BD718XX_PWRONCONFIG1 = 0x28, + BD718XX_RESETSRC = 0x29, + BD718XX_MIRQ = 0x2a, + BD718XX_IRQ = 0x2b, + BD718XX_IN_MON = 0x2c, + BD718XX_POW_STATE = 0x2d, + BD718XX_OUT32K = 0x2e, + BD718XX_REGLOCK = 0x2f, + BD718XX_MUXSW_EN = 0x30, + BD718XX_REG_OTPVER = 0xff, + BD718XX_MAX_REGISTER = 0x100, +}; + +#define BD718XX_REGLOCK_PWRSEQ 0x1 +#define BD718XX_REGLOCK_VREG 0x10 + +#define BD718XX_BUCK_EN 0x01 +#define BD718XX_LDO_EN 0x40 +#define BD718XX_BUCK_SEL 0x02 +#define BD718XX_LDO_SEL 0x80 + +#define DVS_BUCK_RUN_MASK 0x3f +#define BD718XX_1ST_NODVS_BUCK_MASK 0x07 +#define BD718XX_3RD_NODVS_BUCK_MASK 0x07 +#define BD718XX_4TH_NODVS_BUCK_MASK 0x3f + +#define BD71847_BUCK3_MASK 0x07 +#define BD71847_BUCK3_RANGE_MASK 0xc0 +#define BD71847_BUCK4_MASK 0x03 +#define BD71847_BUCK4_RANGE_MASK 0x40 + +#define BD71837_BUCK5_RANGE_MASK 0x80 +#define BD71837_BUCK6_MASK 0x03 + +#define BD718XX_LDO1_MASK 0x03 +#define BD718XX_LDO1_RANGE_MASK 0x20 +#define BD718XX_LDO2_MASK 0x20 +#define BD718XX_LDO3_MASK 0x0f +#define BD718XX_LDO4_MASK 0x0f +#define BD718XX_LDO6_MASK 0x0f + +#define BD71837_LDO5_MASK 0x0f +#define BD71847_LDO5_MASK 0x0f +#define BD71847_LDO5_RANGE_MASK 0x20 +#define BD71837_LDO7_MASK 0x0f + #endif -- cgit v1.3.1 From 299ef26ec867e995f780136a23068745dc14bd1a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 8 May 2019 19:14:43 +0100 Subject: warp7: include: configs: Specify image name of bootscript in FIT When obtaining the bootscript from a FIT image we need to specify the name of the bootscript as defined inside of the FIT. This patch makes a define that appends a "bootscr" parameter to the source command when compiling up in FIT mode on warp7. An environment variable is supplied to enable others to use a different name than "bootscr" as the image name of the boot script in their FIT. Signed-off-by: Bryan O'Donoghue --- include/configs/warp7.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/configs/warp7.h b/include/configs/warp7.h index 8ceaa0c6c60..f5449b0c792 100644 --- a/include/configs/warp7.h +++ b/include/configs/warp7.h @@ -27,9 +27,17 @@ #define CONFIG_DFU_ENV_SETTINGS \ "dfu_alt_info=boot raw 0x2 0x1000 mmcpart 1\0" \ +/* When booting with FIT specify the node entry containing boot.scr */ +#if defined(CONFIG_FIT) +#define BOOT_SCR_STRING "source ${bootscriptaddr}:${bootscr_fitimage_name}\0" +#else +#define BOOT_SCR_STRING "source ${bootscriptaddr}\0" +#endif + #define CONFIG_EXTRA_ENV_SETTINGS \ CONFIG_DFU_ENV_SETTINGS \ "script=boot.scr\0" \ + "bootscr_fitimage_name=bootscr\0" \ "script_signed=boot.scr.imx-signed\0" \ "image=zImage\0" \ "console=ttymxc0\0" \ -- cgit v1.3.1 From 1d4cdc717e2cbd6d2c9746805c2f6dedcfd93008 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 8 May 2019 19:14:44 +0100 Subject: warp7: include: configs: Differentiate bootscript address from loadaddr Reusing the loadaddr to load the boot script breaks some of the logic we want to have around the bootscript/FIT load addresses. Making a dedicated bootscript address allows us to differentiate the bootscript load address from the Linux Kernel or OPTEE load address, thus ensuring that no matter what the load sequence the bootscript and Kernel/OPTEE binary load addresses do not conflict. Signed-off-by: Bryan O'Donoghue --- include/configs/warp7.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/configs/warp7.h b/include/configs/warp7.h index f5449b0c792..7b1416848f9 100644 --- a/include/configs/warp7.h +++ b/include/configs/warp7.h @@ -39,6 +39,7 @@ "script=boot.scr\0" \ "bootscr_fitimage_name=bootscr\0" \ "script_signed=boot.scr.imx-signed\0" \ + "bootscriptaddr=0x83200000\0" \ "image=zImage\0" \ "console=ttymxc0\0" \ "ethact=usb_ether\0" \ @@ -59,16 +60,16 @@ "warp7_auth_or_fail=hab_auth_img_or_fail ${hab_ivt_addr} ${filesize} 0;\0" \ "do_bootscript_hab=" \ "if test ${hab_enabled} -eq 1; then " \ - "setexpr hab_ivt_addr ${loadaddr} - ${ivt_offset}; " \ + "setexpr hab_ivt_addr ${bootscriptaddr} - ${ivt_offset}; " \ "setenv script ${script_signed}; " \ "load mmc ${mmcdev}:${mmcpart} ${hab_ivt_addr} ${script}; " \ "run warp7_auth_or_fail; " \ "run bootscript; "\ "fi;\0" \ "loadbootscript=" \ - "load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \ + "load mmc ${mmcdev}:${mmcpart} ${bootscriptaddr} ${script};\0" \ "bootscript=echo Running bootscript from mmc ...; " \ - "source\0" \ + BOOT_SCR_STRING \ "loadimage=load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \ "loadfdt=load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}\0" \ "mmcboot=echo Booting from mmc ...; " \ -- cgit v1.3.1 From 66a15495b36fec920c8ee556df6819700d8ed31a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 8 May 2019 19:14:47 +0100 Subject: warp7: include: configs: Specify an fdtovaddr In the Mbed Linux OS bootflow OP-TEE runs before u-boot and provides a DTB overlay at 0x83100000. This overlay should subsequently be merged into the main DTB before handing over to the kernel. This patch defines fdtovaddr at 0x83100000. Signed-off-by: Bryan O'Donoghue --- include/configs/warp7.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/configs/warp7.h b/include/configs/warp7.h index 7b1416848f9..358941e449a 100644 --- a/include/configs/warp7.h +++ b/include/configs/warp7.h @@ -47,6 +47,7 @@ "initrd_high=0xffffffff\0" \ "fdt_file=imx7s-warp.dtb\0" \ "fdt_addr=" __stringify(CONFIG_SYS_FDT_ADDR)"\0" \ + "fdtovaddr=0x83100000\0" \ "optee_addr=" __stringify(CONFIG_OPTEE_LOAD_ADDR)"\0" \ "boot_fdt=try\0" \ "ip_dyn=yes\0" \ -- cgit v1.3.1 From 2528a666aea244b20abf7b387039b10b1d8c2d76 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 8 May 2019 19:14:48 +0100 Subject: warp7: Specify a default CONFIG_OPTEE_LOAD_ADDR if non provided If no CONFIG_OPTEE_LOAD_ADDR is provided i.e. you are not loading OPTEE into memory in u-boot, then just set the non-existent CONFIG option to zero, elsewise stringify(CONFIG_OPTEE_LOAD_ADDR) will return "CONFIG_OPTEE_LOAD_ADDR" - which looks weird in the u-boot environment. Signed-off-by: Bryan O'Donoghue --- include/configs/warp7.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/configs/warp7.h b/include/configs/warp7.h index 358941e449a..73541fe1760 100644 --- a/include/configs/warp7.h +++ b/include/configs/warp7.h @@ -34,6 +34,10 @@ #define BOOT_SCR_STRING "source ${bootscriptaddr}\0" #endif +#ifndef CONFIG_OPTEE_LOAD_ADDR +#define CONFIG_OPTEE_LOAD_ADDR 0 +#endif + #define CONFIG_EXTRA_ENV_SETTINGS \ CONFIG_DFU_ENV_SETTINGS \ "script=boot.scr\0" \ -- cgit v1.3.1 From 8bf9364c4cc9d57eee34dd41a238e2dba9524378 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 16 May 2019 09:41:42 +0200 Subject: ARM: imx: cosmetic: Remove not needed comment from the mccmon6.h file This comment is a leftover from the Kconfig CONFIG_*MTD* move. Signed-off-by: Lukasz Majewski --- include/configs/mccmon6.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/configs/mccmon6.h b/include/configs/mccmon6.h index a1774c027a9..b97373512f5 100644 --- a/include/configs/mccmon6.h +++ b/include/configs/mccmon6.h @@ -65,8 +65,6 @@ #define CONFIG_SYS_FLASH_BANKS_LIST { (CONFIG_SYS_FLASH_BASE) } #define CONFIG_SYS_FLASH_BANKS_SIZES { (32 * SZ_1M) } -/* MTD support */ - /* USB Configs */ #define CONFIG_USB_MAX_CONTROLLER_COUNT 2 #define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW) -- cgit v1.3.1 From 2e8f9138d8e3600de38c0776f962bd087260349d Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 16 May 2019 09:41:43 +0200 Subject: ARM: imx: config: Disable support for USB on MCCMON6 The IMX6Q based MCCMON6 is not using USB for any purpose. Signed-off-by: Lukasz Majewski --- configs/mccmon6_nor_defconfig | 2 -- configs/mccmon6_sd_defconfig | 2 -- include/configs/mccmon6.h | 5 ----- 3 files changed, 9 deletions(-) (limited to 'include') diff --git a/configs/mccmon6_nor_defconfig b/configs/mccmon6_nor_defconfig index 781620048d2..cd78c5cb33e 100644 --- a/configs/mccmon6_nor_defconfig +++ b/configs/mccmon6_nor_defconfig @@ -20,7 +20,6 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_SF=y -CONFIG_CMD_USB=y CONFIG_CMD_CACHE=y CONFIG_CMD_EXT4_WRITE=y CONFIG_CMD_MTDPARTS=y @@ -50,5 +49,4 @@ CONFIG_MII=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_DM_THERMAL=y -CONFIG_USB=y CONFIG_OF_LIBFDT=y diff --git a/configs/mccmon6_sd_defconfig b/configs/mccmon6_sd_defconfig index 522207afcad..17947860eec 100644 --- a/configs/mccmon6_sd_defconfig +++ b/configs/mccmon6_sd_defconfig @@ -21,7 +21,6 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_SF=y -CONFIG_CMD_USB=y CONFIG_CMD_CACHE=y CONFIG_CMD_EXT4_WRITE=y CONFIG_CMD_MTDPARTS=y @@ -51,5 +50,4 @@ CONFIG_MII=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_DM_THERMAL=y -CONFIG_USB=y CONFIG_OF_LIBFDT=y diff --git a/include/configs/mccmon6.h b/include/configs/mccmon6.h index b97373512f5..231b2af87a5 100644 --- a/include/configs/mccmon6.h +++ b/include/configs/mccmon6.h @@ -65,11 +65,6 @@ #define CONFIG_SYS_FLASH_BANKS_LIST { (CONFIG_SYS_FLASH_BASE) } #define CONFIG_SYS_FLASH_BANKS_SIZES { (32 * SZ_1M) } -/* USB Configs */ -#define CONFIG_USB_MAX_CONTROLLER_COUNT 2 -#define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW) -#define CONFIG_MXC_USB_FLAGS 0 - /* Ethernet Configuration */ #define CONFIG_FEC_MXC #define IMX_FEC_BASE ENET_BASE_ADDR -- cgit v1.3.1 From 917964cbbede7cf1568ef74ab71a541b07dae183 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 16 May 2019 09:41:44 +0200 Subject: ARM: imx: Disable 1Gbps support on MCCMON6's KSZ9031 PHY mccmon6 works in 10/100 MiB Ethernet environment, so disabling 1GiB support improves robustness of the network after power up (as one don't need to wait for autoneg). Signed-off-by: Lukasz Majewski --- include/configs/mccmon6.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/configs/mccmon6.h b/include/configs/mccmon6.h index 231b2af87a5..667dac7340a 100644 --- a/include/configs/mccmon6.h +++ b/include/configs/mccmon6.h @@ -78,6 +78,7 @@ "fdt_high=0xffffffff\0" \ "initrd_high=0xffffffff\0" \ "boot_os=yes\0" \ + "disable_giga=yes\0" \ "download_kernel=" \ "tftpboot ${kernel_addr} ${kernel_file};" \ "tftpboot ${fdt_addr} ${fdtfile};\0" \ -- cgit v1.3.1 From e0f0657ddf7322999da40fff95dce727e81848c7 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Tue, 28 May 2019 06:51:51 +0200 Subject: pwm: imx: add Kconfig support add Kconfig support for this driver. Signed-off-by: Heiko Schocher Acked-by: Martyn Welch --- README | 4 ---- configs/aristainetos2_defconfig | 1 + configs/aristainetos2b_defconfig | 1 + configs/aristainetos_defconfig | 1 + configs/dms-ba16-1g_defconfig | 1 + configs/dms-ba16_defconfig | 1 + configs/ge_bx50v3_defconfig | 1 + configs/mx53ppd_defconfig | 1 + configs/vining_2000_defconfig | 1 + drivers/pwm/Kconfig | 5 +++++ include/configs/advantech_dms-ba16.h | 1 - include/configs/aristainetos-common.h | 1 - include/configs/aristainetos2.h | 3 --- include/configs/aristainetos2b.h | 3 --- include/configs/ge_bx50v3.h | 1 - include/configs/mx53ppd.h | 1 - include/configs/vining_2000.h | 1 - scripts/config_whitelist.txt | 1 - 18 files changed, 13 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/README b/README index 8f816ad2aff..7e610a8ebda 100644 --- a/README +++ b/README @@ -960,10 +960,6 @@ The following options need to be configured: CONFIG_SH_ETHER_CACHE_WRITEBACK If this option is set, the driver enables cache flush. -- PWM Support: - CONFIG_PWM_IMX - Support for PWM module on the imx6. - - TPM Support: CONFIG_TPM Support TPM devices. diff --git a/configs/aristainetos2_defconfig b/configs/aristainetos2_defconfig index 49dd9bb7553..33e40cd7d3a 100644 --- a/configs/aristainetos2_defconfig +++ b/configs/aristainetos2_defconfig @@ -50,6 +50,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_MII=y +CONFIG_PWM_IMX=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_USB=y diff --git a/configs/aristainetos2b_defconfig b/configs/aristainetos2b_defconfig index 06c05f7e222..34a6cde1ee8 100644 --- a/configs/aristainetos2b_defconfig +++ b/configs/aristainetos2b_defconfig @@ -48,6 +48,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_MII=y +CONFIG_PWM_IMX=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_USB=y diff --git a/configs/aristainetos_defconfig b/configs/aristainetos_defconfig index e645055afa1..cb361daafb0 100644 --- a/configs/aristainetos_defconfig +++ b/configs/aristainetos_defconfig @@ -49,6 +49,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ8XXX=y CONFIG_MII=y +CONFIG_PWM_IMX=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_USB=y diff --git a/configs/dms-ba16-1g_defconfig b/configs/dms-ba16-1g_defconfig index 4231adb6a72..91b6a7424f9 100644 --- a/configs/dms-ba16-1g_defconfig +++ b/configs/dms-ba16-1g_defconfig @@ -41,6 +41,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_PHYLIB=y CONFIG_MII=y +CONFIG_PWM_IMX=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_USB=y diff --git a/configs/dms-ba16_defconfig b/configs/dms-ba16_defconfig index f98088deb74..fbf1f89ece2 100644 --- a/configs/dms-ba16_defconfig +++ b/configs/dms-ba16_defconfig @@ -40,6 +40,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_PHYLIB=y CONFIG_MII=y +CONFIG_PWM_IMX=y CONFIG_SPI=y CONFIG_MXC_SPI=y CONFIG_USB=y diff --git a/configs/ge_bx50v3_defconfig b/configs/ge_bx50v3_defconfig index 60bdcd67a00..54197f7541c 100644 --- a/configs/ge_bx50v3_defconfig +++ b/configs/ge_bx50v3_defconfig @@ -55,6 +55,7 @@ CONFIG_CMD_E1000=y CONFIG_MII=y CONFIG_PINCTRL=y CONFIG_PINCTRL_IMX6=y +CONFIG_PWM_IMX=y CONFIG_SPI=y CONFIG_DM_SPI=y CONFIG_MXC_SPI=y diff --git a/configs/mx53ppd_defconfig b/configs/mx53ppd_defconfig index a88af15760e..cad798a4166 100644 --- a/configs/mx53ppd_defconfig +++ b/configs/mx53ppd_defconfig @@ -43,6 +43,7 @@ CONFIG_FSL_ESDHC_IMX=y CONFIG_MII=y CONFIG_PINCTRL=y CONFIG_PINCTRL_IMX5=y +CONFIG_PWM_IMX=y CONFIG_RTC_S35392A=y CONFIG_USB=y CONFIG_USB_EHCI_MX5=y diff --git a/configs/vining_2000_defconfig b/configs/vining_2000_defconfig index 7364c67cd7d..6b9691ad3e3 100644 --- a/configs/vining_2000_defconfig +++ b/configs/vining_2000_defconfig @@ -45,6 +45,7 @@ CONFIG_SUPPORT_EMMC_BOOT=y CONFIG_FSL_ESDHC_IMX=y CONFIG_PHYLIB=y CONFIG_MII=y +CONFIG_PWM_IMX=y CONFIG_PCI=y CONFIG_DM_PCI=y CONFIG_PINCTRL=y diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 2984b797663..1f36fc78fa7 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/include/configs/advantech_dms-ba16.h b/include/configs/advantech_dms-ba16.h index 22d1e41bc8d..a0373491627 100644 --- a/include/configs/advantech_dms-ba16.h +++ b/include/configs/advantech_dms-ba16.h @@ -223,7 +223,6 @@ #define CONFIG_IMX_VIDEO_SKIP #endif -#define CONFIG_PWM_IMX #define CONFIG_IMX6_PWM_PER_CLK 66000000 #ifdef CONFIG_CMD_PCI diff --git a/include/configs/aristainetos-common.h b/include/configs/aristainetos-common.h index a24814673c5..e998d9b1b27 100644 --- a/include/configs/aristainetos-common.h +++ b/include/configs/aristainetos-common.h @@ -196,7 +196,6 @@ #define CONFIG_VIDEO_BMP_LOGO #define CONFIG_IMX_VIDEO_SKIP -#define CONFIG_PWM_IMX #define CONFIG_IMX6_PWM_PER_CLK 66000000 #endif /* __ARISTAINETOS_COMMON_CONFIG_H */ diff --git a/include/configs/aristainetos2.h b/include/configs/aristainetos2.h index 00e5667499e..361e6ac6542 100644 --- a/include/configs/aristainetos2.h +++ b/include/configs/aristainetos2.h @@ -45,9 +45,6 @@ #define CONFIG_LG4573_BUS 0 #define CONFIG_LG4573_CS 0 -#define CONFIG_PWM_IMX -#define CONFIG_IMX6_PWM_PER_CLK 66000000 - #include "aristainetos-common.h" #endif /* __ARISTAINETOS2_CONFIG_H */ diff --git a/include/configs/aristainetos2b.h b/include/configs/aristainetos2b.h index cfe0e053b66..cdeb7a3b032 100644 --- a/include/configs/aristainetos2b.h +++ b/include/configs/aristainetos2b.h @@ -45,9 +45,6 @@ #define CONFIG_LG4573_BUS 0 #define CONFIG_LG4573_CS 1 -#define CONFIG_PWM_IMX -#define CONFIG_IMX6_PWM_PER_CLK 66000000 - #include "aristainetos-common.h" #endif /* __ARISTAINETOS2B_CONFIG_H */ diff --git a/include/configs/ge_bx50v3.h b/include/configs/ge_bx50v3.h index 0481ed06a91..31214a6aa78 100644 --- a/include/configs/ge_bx50v3.h +++ b/include/configs/ge_bx50v3.h @@ -198,7 +198,6 @@ #define CONFIG_IMX_VIDEO_SKIP #define CONFIG_CMD_BMP -#define CONFIG_PWM_IMX #define CONFIG_IMX6_PWM_PER_CLK 66000000 #define CONFIG_PCI diff --git a/include/configs/mx53ppd.h b/include/configs/mx53ppd.h index 2d6715cba26..d5b54dfa15e 100644 --- a/include/configs/mx53ppd.h +++ b/include/configs/mx53ppd.h @@ -211,7 +211,6 @@ #define CONFIG_BCH /* Backlight Control */ -#define CONFIG_PWM_IMX #define CONFIG_IMX6_PWM_PER_CLK 66666000 #endif /* __CONFIG_H */ diff --git a/include/configs/vining_2000.h b/include/configs/vining_2000.h index d4db9b4a567..33f06c00b1d 100644 --- a/include/configs/vining_2000.h +++ b/include/configs/vining_2000.h @@ -83,7 +83,6 @@ #define CONFIG_IMX_THERMAL -#define CONFIG_PWM_IMX #define CONFIG_IMX6_PWM_PER_CLK 66000000 #define CONFIG_ENV_OFFSET (8 * SZ_64K) diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt index bd167959b3f..be94a600d63 100644 --- a/scripts/config_whitelist.txt +++ b/scripts/config_whitelist.txt @@ -1459,7 +1459,6 @@ CONFIG_PROOF_POINTS CONFIG_PRPMC_PCI_ALIAS CONFIG_PSRAM_SCFG CONFIG_PWM -CONFIG_PWM_IMX CONFIG_PXA_LCD CONFIG_PXA_MMC_GENERIC CONFIG_PXA_PWR_I2C -- cgit v1.3.1 From 0047a4d1a0e1413ec0ed6de33862d10e09947012 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Sun, 9 Jun 2019 22:54:37 +0200 Subject: cosmetic: display5: Remove not needed comments Some comments are not needed anymore after Kconfig automated conversion. Signed-off-by: Lukasz Majewski Reviewed-by: Peng Fan --- include/configs/display5.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/configs/display5.h b/include/configs/display5.h index 8829cbad913..1d3334ff12c 100644 --- a/include/configs/display5.h +++ b/include/configs/display5.h @@ -55,11 +55,8 @@ /* Size of malloc() pool */ #define CONFIG_SYS_MALLOC_LEN (16 * 1024 * 1024) -/*#define CONFIG_MXC_UART*/ #define CONFIG_MXC_UART_BASE UART5_BASE -/* SPI NOR Flash */ - /* I2C Configs */ #define CONFIG_SYS_I2C #define CONFIG_SYS_I2C_MXC @@ -358,8 +355,6 @@ #define CONFIG_SYS_INIT_SP_ADDR \ (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) -/* Commands */ - /* Watchdog */ #define CONFIG_WATCHDOG_TIMEOUT_MSECS 15000 -- cgit v1.3.1 From 6874cb72204ffbdbe4290baae11d0b9b8ed47ed1 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 9 Jun 2019 03:46:21 +0200 Subject: watchdog: Split WDT from SPL_WDT Use CONFIG_IS_ENABLED(WDT) to permit use of WDT in SPL without DM, while the full U-Boot can use rich DM/DT WDT driver. Signed-off-by: Marek Vasut Cc: Peng Fan Cc: Stefano Babic Tested-by: Heiko Schocher Tested-by: Suniel Mahesh --- common/board_r.c | 2 +- common/spl/Kconfig | 1 + common/spl/spl.c | 2 +- drivers/watchdog/Kconfig | 7 +++++++ drivers/watchdog/Makefile | 2 +- include/asm-generic/global_data.h | 2 +- include/wdt.h | 2 +- 7 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/common/board_r.c b/common/board_r.c index abc31b17b81..ee4dcedd5fa 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -700,7 +700,7 @@ static init_fnc_t init_sequence_r[] = { stdio_init_tables, initr_serial, initr_announce, -#if defined(CONFIG_WDT) +#if CONFIG_IS_ENABLED(WDT) initr_watchdog, #endif INIT_FUNC_WATCHDOG_RESET diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 5978fb29343..81bd908f9b1 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -1061,6 +1061,7 @@ endif config SPL_WATCHDOG_SUPPORT bool "Support watchdog drivers" + imply SPL_WDT if !HW_WATCHDOG help Enable support for watchdog drivers in SPL. A watchdog is typically a hardware peripheral which can reset the system when it diff --git a/common/spl/spl.c b/common/spl/spl.c index d5e3f680f4c..c182705b3fc 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -603,7 +603,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2) spl_board_init(); #endif -#if defined(CONFIG_SPL_WATCHDOG_SUPPORT) && defined(CONFIG_WDT) +#if defined(CONFIG_SPL_WATCHDOG_SUPPORT) && CONFIG_IS_ENABLED(WDT) initr_watchdog(); #endif diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ee0ddffe735..de9bd87236b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -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 68c989aa0b9..97aa6a836ce 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/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 02a3ed68382..7c2220643b5 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -137,7 +137,7 @@ typedef struct global_data { #if defined(CONFIG_TRANSLATION_OFFSET) fdt_addr_t translation_offset; /* optional translation offset */ #endif -#if defined(CONFIG_WDT) +#if CONFIG_IS_ENABLED(WDT) struct udevice *watchdog_dev; #endif } gd_t; diff --git a/include/wdt.h b/include/wdt.h index aa77d3e9b40..5bcff24ab31 100644 --- a/include/wdt.h +++ b/include/wdt.h @@ -106,7 +106,7 @@ struct wdt_ops { int (*expire_now)(struct udevice *dev, ulong flags); }; -#if defined(CONFIG_WDT) +#if CONFIG_IS_ENABLED(WDT) #ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS #define CONFIG_WATCHDOG_TIMEOUT_MSECS (60 * 1000) #endif -- cgit v1.3.1 From 9ac5dda519b297af683465bf169c7e5869c72b47 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 9 Jun 2019 18:46:45 +0200 Subject: ARM: imx: m53menlo: Convert WDT support to DM Enable DM Watchdog support on iMX53 M53Menlo. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Stefano Babic --- arch/arm/dts/imx53-m53menlo-u-boot.dtsi | 5 +++++ configs/m53menlo_defconfig | 2 ++ include/configs/m53menlo.h | 5 +++++ 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/arch/arm/dts/imx53-m53menlo-u-boot.dtsi b/arch/arm/dts/imx53-m53menlo-u-boot.dtsi index 329fa3b5e27..bc4b3483a6b 100644 --- a/arch/arm/dts/imx53-m53menlo-u-boot.dtsi +++ b/arch/arm/dts/imx53-m53menlo-u-boot.dtsi @@ -11,6 +11,11 @@ u-boot,dm-pre-reloc; }; }; + + wdt-reboot { + compatible = "wdt-reboot"; + wdt = <&wdog1>; + }; }; &gpio1 { diff --git a/configs/m53menlo_defconfig b/configs/m53menlo_defconfig index 3a5fa26bcd3..c551ea2daa2 100644 --- a/configs/m53menlo_defconfig +++ b/configs/m53menlo_defconfig @@ -72,6 +72,8 @@ CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y CONFIG_RTC_M41T62=y +CONFIG_SYSRESET=y +CONFIG_SYSRESET_WATCHDOG=y CONFIG_USB=y CONFIG_DM_USB=y CONFIG_USB_EHCI_MX5=y diff --git a/include/configs/m53menlo.h b/include/configs/m53menlo.h index 7b68c1c0a1e..e98dbfbb7ee 100644 --- a/include/configs/m53menlo.h +++ b/include/configs/m53menlo.h @@ -242,4 +242,9 @@ "fi ; " \ "fi\0" +#if defined(CONFIG_SPL_BUILD) +#undef CONFIG_WATCHDOG +#define CONFIG_HW_WATCHDOG +#endif + #endif /* __M53MENLO_CONFIG_H__ */ -- cgit v1.3.1 From 7c5e4b2d53faeac0c9cd9cf1482c9ab07962933b Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Wed, 10 Jul 2019 09:42:38 +0200 Subject: colibri-imx6ull: fix vidargs Unfortunately, that missing M makes the current downstream NXP BSP 4.14.98_2.0.0_ga crash early during Linux kernel boot. Fix this. Signed-off-by: Marcel Ziswiler Reviewed-by: Oleksandr Suvorov Reviewed-by: Igor Opaniuk Tested-by: Igor Opaniuk --- include/configs/colibri-imx6ull.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/configs/colibri-imx6ull.h b/include/configs/colibri-imx6ull.h index 21d9a3da01f..2c43862800a 100644 --- a/include/configs/colibri-imx6ull.h +++ b/include/configs/colibri-imx6ull.h @@ -123,7 +123,7 @@ "${board}/flash_blk.img && source ${loadaddr}\0" \ "splashpos=m,m\0" \ "videomode=video=ctfb:x:640,y:480,depth:18,pclk:39722,le:48,ri:16,up:33,lo:10,hs:96,vs:2,sync:0,vmode:0\0" \ - "vidargs=video=mxsfb:640x480-16@60" + "vidargs=video=mxsfb:640x480M-16@60" #define CONFIG_SYS_MEMTEST_START 0x80000000 #define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START + 0x08000000) -- cgit v1.3.1 From 9358e5b3b4e04343d3e3c996b74886be2ffca172 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 18 Jun 2019 14:57:32 +0300 Subject: colibri_imx7: introduce androidboot wrapper 1. Introduce androidboot wrapper for booting AOSP. 2. Add partitions_android env var for simplifying the process of writing new gpt table from U-boot shell/fastboot. Signed-off-by: Igor Opaniuk Reviewed-by: Oleksandr Suvorov --- configs/colibri_imx7_emmc_defconfig | 2 +- include/configs/colibri_imx7.h | 44 +++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/configs/colibri_imx7_emmc_defconfig b/configs/colibri_imx7_emmc_defconfig index 2e6cfcd063c..0c3a898aabb 100644 --- a/configs/colibri_imx7_emmc_defconfig +++ b/configs/colibri_imx7_emmc_defconfig @@ -26,7 +26,7 @@ CONFIG_CRC32_VERIFY=y CONFIG_CMD_MEMTEST=y CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y -# CONFIG_RANDOM_UUID is not set +CONFIG_RANDOM_UUID=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_USB=y diff --git a/include/configs/colibri_imx7.h b/include/configs/colibri_imx7.h index 40173b18fa7..49cdd610387 100644 --- a/include/configs/colibri_imx7.h +++ b/include/configs/colibri_imx7.h @@ -46,6 +46,46 @@ #define CONFIG_NETMASK 255.255.255.0 #define CONFIG_SERVERIP 192.168.10.1 +#ifndef PARTS_DEFAULT +/* Define the default GPT table for eMMC */ +#define PARTS_DEFAULT \ + /* Android partitions */ \ + "partitions_android=" \ + "uuid_disk=${uuid_gpt_disk};" \ + "name=boot,start=1M,size=32M,uuid=${uuid_gpt_boot};" \ + "name=environment,size=4M,uuid=${uuid_gpt_environment};" \ + "name=recovery,size=16M,uuid=${uuid_gpt_recovery};" \ + "name=system,size=1536M,uuid=${uuid_gpt_system};" \ + "name=cache,size=512M,uuid=${uuid_gpt_cache};" \ + "name=device,size=8M,uuid=${uuid_gpt_device};" \ + "name=misc,size=4M,uuid=${uuid_gpt_misc};" \ + "name=datafooter,size=2M,uuid=${uuid_gpt_datafooter};" \ + "name=metadata,size=2M,uuid=${uuid_gpt_metadata};" \ + "name=persistdata,size=2M,uuid=${uuid_gpt_persistdata};" \ + "name=userdata,size=128M,uuid=${uuid_gpt_userdata};" \ + "name=fbmisc,size=-,uuid=${uuid_gpt_fbmisc}\0" +#endif /* PARTS_DEFAULT */ + +#define EMMC_ANDROID_BOOTCMD \ + "android_args=androidboot.storage_type=emmc\0" \ + PARTS_DEFAULT \ + "android_fdt_addr=0x83700000\0" \ + "android_mmc_dev=0\0" \ + "m4binary=rpmsg_imu_freertos.elf\0" \ + "androidboot=ext4load mmc 0:a ${loadaddr} media/0/${m4binary}; "\ + "bootaux ${loadaddr}; " \ + "setenv loadaddr 0x88000000; " \ + "setenv bootm_boot_mode sec;" \ + "setenv bootargs androidboot.serialno=${serial#} " \ + "$android_args; " \ + "part start mmc ${android_mmc_dev} boot boot_start; " \ + "part size mmc ${android_mmc_dev} boot boot_size; " \ + "mmc read ${loadaddr} ${boot_start} ${boot_size}; " \ + "part start mmc ${android_mmc_dev} environment env_start; " \ + "part size mmc ${android_mmc_dev} environment env_size; " \ + "mmc read ${android_fdt_addr} ${env_start} ${env_size}; " \ + "bootm ${loadaddr} ${loadaddr} ${android_fdt_addr}\0 " + #define EMMC_BOOTCMD \ "set_emmcargs=setenv emmcargs ip=off root=PARTUUID=${uuid} ro " \ "rootfstype=ext4 rootwait\0" \ @@ -63,7 +103,6 @@ "emmcfinduuid=part uuid mmc ${emmcdev}:${emmcrootpart} uuid\0" \ "emmcrootpart=2\0" - #define MEM_LAYOUT_ENV_SETTINGS \ "bootm_size=0x10000000\0" \ "fdt_addr_r=0x82000000\0" \ @@ -125,7 +164,8 @@ "setenv fdtfile ${soc}-colibri-emmc-${fdt_board}.dtb && run distro_bootcmd;" #define MODULE_EXTRA_ENV_SETTINGS \ "variant=-emmc\0" \ - EMMC_BOOTCMD + EMMC_BOOTCMD \ + EMMC_ANDROID_BOOTCMD #endif #if defined(CONFIG_TARGET_COLIBRI_IMX7_NAND) -- cgit v1.3.1 From 0dff26aeee7fee18105b091ec7649019579fc756 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 25 Jun 2019 17:06:42 +0300 Subject: apalis_imx6: configs: remove legacy usbboot command Remove obsolete legacy usbboot wrapper, as distroboot can handle booting from USB drivers. Signed-off-by: Igor Opaniuk Reviewed-by: Oleksandr Suvorov --- include/configs/apalis_imx6.h | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'include') diff --git a/include/configs/apalis_imx6.h b/include/configs/apalis_imx6.h index db37fa7b731..974571df435 100644 --- a/include/configs/apalis_imx6.h +++ b/include/configs/apalis_imx6.h @@ -176,27 +176,6 @@ "sdfinduuid=part uuid mmc ${sddev}:${sdrootpart} uuid\0" \ "sdrootpart=2\0" - -#define USB_BOOTCMD \ - "set_usbargs=setenv usbargs ip=off root=PARTUUID=${uuid} ro,noatime " \ - "rootfstype=ext4 rootwait\0" \ - "usbboot=run setup; usb start; run usbfinduuid; run set_usbargs; " \ - "setenv bootargs ${defargs} ${setupargs} " \ - "${usbargs} ${vidargs}; echo Booting from USB stick...; " \ - "run usbdtbload; load usb " \ - "${usbdev}:${usbbootpart} ${kernel_addr_r} " \ - "${boot_file} && run fdt_fixup && " \ - "bootz ${kernel_addr_r} ${dtbparam}\0" \ - "usbbootpart=1\0" \ - "usbdev=0\0" \ - "usbdtbload=setenv dtbparam; load usb ${usbdev}:${usbbootpart} "\ - "${fdt_addr_r} " \ - "${fdt_file} && setenv dtbparam \" - " \ - "${fdt_addr_r}\" && true\0" \ - "usbfinduuid=part uuid usb ${usbdev}:${usbrootpart} uuid\0" \ - "usbrootpart=2\0" - - #ifndef CONFIG_TDX_APALIS_IMX6_V1_0 #define FDT_FILE "imx6q-apalis-eval.dtb" #define FDT_FILE_V1_0 "imx6q-apalis_v1_0-eval.dtb" @@ -219,7 +198,6 @@ MEM_LAYOUT_ENV_SETTINGS \ NFS_BOOTCMD \ SD_BOOTCMD \ - USB_BOOTCMD \ "setethupdate=if env exists ethaddr; then; else setenv ethaddr " \ "00:14:2d:00:00:00; fi; tftpboot ${loadaddr} " \ "flash_eth.img && source ${loadaddr}\0" \ -- cgit v1.3.1 From 48b09848bfa342a526eb8034f067a6d163b8a6d0 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 25 Jun 2019 17:06:43 +0300 Subject: colibri_imx6: configs: remove legacy usbboot command Remove obsolete legacy usbboot wrapper, as distroboot can handle booting from USB drivers. Signed-off-by: Igor Opaniuk Reviewed-by: Oleksandr Suvorov --- include/configs/colibri_imx6.h | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'include') diff --git a/include/configs/colibri_imx6.h b/include/configs/colibri_imx6.h index 86f3f0d4fa2..147f8013534 100644 --- a/include/configs/colibri_imx6.h +++ b/include/configs/colibri_imx6.h @@ -165,25 +165,6 @@ "sdfinduuid=part uuid mmc ${sddev}:${sdrootpart} uuid\0" \ "sdrootpart=2\0" -#define USB_BOOTCMD \ - "set_usbargs=setenv usbargs ip=off root=PARTUUID=${uuid} rw,noatime " \ - "rootfstype=ext4 rootwait\0" \ - "usbboot=run setup; usb start; run usbfinduuid; run set_usbargs; " \ - "setenv bootargs ${defargs} ${setupargs} " \ - "${usbargs} ${vidargs}; echo Booting from USB stick...; " \ - "run usbdtbload; " \ - "load usb ${usbdev}:${usbbootpart} ${kernel_addr_r} " \ - "${boot_file} && run fdt_fixup && " \ - "bootz ${kernel_addr_r} ${dtbparam}\0" \ - "usbbootpart=1\0" \ - "usbdev=0\0" \ - "usbdtbload=setenv dtbparam; load usb ${usbdev}:${usbbootpart} " \ - "${fdt_addr_r} " \ - "${fdt_file} && setenv dtbparam \" - ${fdt_addr_r}\" && " \ - "true\0" \ - "usbfinduuid=part uuid usb ${usbdev}:${usbrootpart} uuid\0" \ - "usbrootpart=2\0" - #define FDT_FILE "imx6dl-colibri-eval-v3.dtb" #define CONFIG_EXTRA_ENV_SETTINGS \ BOOTENV \ @@ -201,7 +182,6 @@ MEM_LAYOUT_ENV_SETTINGS \ NFS_BOOTCMD \ SD_BOOTCMD \ - USB_BOOTCMD \ "setethupdate=if env exists ethaddr; then; else setenv ethaddr " \ "00:14:2d:00:00:00; fi; tftpboot ${loadaddr} " \ "flash_eth.img && source ${loadaddr}\0" \ -- cgit v1.3.1 From 4a6f5b4f56b8bc6f36736fc0a07c5c4f9069e69b Mon Sep 17 00:00:00 2001 From: Ludwig Zenz Date: Mon, 8 Jul 2019 15:04:58 +0200 Subject: ARM: imx6: DHCOM i.MX6 PDK: Switch to DM for I2C This patch enables DM I2C for DHCOM i.MX6 PDK2 boards and removes non DM I2C code. The I2C EEPROM with ethaddr (MAC) is defined in the device tree. Use UCLASS_I2C_EEPROM to find the device by fixed hardware path and read the ethaddr. Tested with DHCOM i.MX6dl and DHCOM i.MX6q. Signed-off-by: Ludwig Zenz Reviewed-by: Marek Vasut --- board/dhelectronics/dh_imx6/dh_imx6.c | 115 ++++------------------------------ configs/dh_imx6_defconfig | 5 +- include/configs/dh_imx6.h | 8 --- 3 files changed, 16 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/board/dhelectronics/dh_imx6/dh_imx6.c b/board/dhelectronics/dh_imx6/dh_imx6.c index 7b1a27ac370..40cc2a854ee 100644 --- a/board/dhelectronics/dh_imx6/dh_imx6.c +++ b/board/dhelectronics/dh_imx6/dh_imx6.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -26,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,92 +35,6 @@ DECLARE_GLOBAL_DATA_PTR; -#define I2C_PAD_CTRL \ - (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \ - PAD_CTL_HYS | PAD_CTL_ODE | PAD_CTL_SRE_FAST) - -#define EEPROM_I2C_ADDRESS 0x50 - -#define PC MUX_PAD_CTRL(I2C_PAD_CTRL) - -static struct i2c_pads_info dh6sdl_i2c_pad_info0 = { - .scl = { - .i2c_mode = MX6DL_PAD_EIM_D21__I2C1_SCL | PC, - .gpio_mode = MX6DL_PAD_EIM_D21__GPIO3_IO21 | PC, - .gp = IMX_GPIO_NR(3, 21) - }, - .sda = { - .i2c_mode = MX6DL_PAD_EIM_D28__I2C1_SDA | PC, - .gpio_mode = MX6DL_PAD_EIM_D28__GPIO3_IO28 | PC, - .gp = IMX_GPIO_NR(3, 28) - } -}; - -static struct i2c_pads_info dh6sdl_i2c_pad_info1 = { - .scl = { - .i2c_mode = MX6DL_PAD_KEY_COL3__I2C2_SCL | PC, - .gpio_mode = MX6DL_PAD_KEY_COL3__GPIO4_IO12 | PC, - .gp = IMX_GPIO_NR(4, 12) - }, - .sda = { - .i2c_mode = MX6DL_PAD_KEY_ROW3__I2C2_SDA | PC, - .gpio_mode = MX6DL_PAD_KEY_ROW3__GPIO4_IO13 | PC, - .gp = IMX_GPIO_NR(4, 13) - } -}; - -static struct i2c_pads_info dh6sdl_i2c_pad_info2 = { - .scl = { - .i2c_mode = MX6DL_PAD_GPIO_3__I2C3_SCL | PC, - .gpio_mode = MX6DL_PAD_GPIO_3__GPIO1_IO03 | PC, - .gp = IMX_GPIO_NR(1, 3) - }, - .sda = { - .i2c_mode = MX6DL_PAD_GPIO_6__I2C3_SDA | PC, - .gpio_mode = MX6DL_PAD_GPIO_6__GPIO1_IO06 | PC, - .gp = IMX_GPIO_NR(1, 6) - } -}; - -static struct i2c_pads_info dh6dq_i2c_pad_info0 = { - .scl = { - .i2c_mode = MX6Q_PAD_EIM_D21__I2C1_SCL | PC, - .gpio_mode = MX6Q_PAD_EIM_D21__GPIO3_IO21 | PC, - .gp = IMX_GPIO_NR(3, 21) - }, - .sda = { - .i2c_mode = MX6Q_PAD_EIM_D28__I2C1_SDA | PC, - .gpio_mode = MX6Q_PAD_EIM_D28__GPIO3_IO28 | PC, - .gp = IMX_GPIO_NR(3, 28) - } -}; - -static struct i2c_pads_info dh6dq_i2c_pad_info1 = { - .scl = { - .i2c_mode = MX6Q_PAD_KEY_COL3__I2C2_SCL | PC, - .gpio_mode = MX6Q_PAD_KEY_COL3__GPIO4_IO12 | PC, - .gp = IMX_GPIO_NR(4, 12) - }, - .sda = { - .i2c_mode = MX6Q_PAD_KEY_ROW3__I2C2_SDA | PC, - .gpio_mode = MX6Q_PAD_KEY_ROW3__GPIO4_IO13 | PC, - .gp = IMX_GPIO_NR(4, 13) - } -}; - -static struct i2c_pads_info dh6dq_i2c_pad_info2 = { - .scl = { - .i2c_mode = MX6Q_PAD_GPIO_3__I2C3_SCL | PC, - .gpio_mode = MX6Q_PAD_GPIO_3__GPIO1_IO03 | PC, - .gp = IMX_GPIO_NR(1, 3) - }, - .sda = { - .i2c_mode = MX6Q_PAD_GPIO_6__I2C3_SDA | PC, - .gpio_mode = MX6Q_PAD_GPIO_6__GPIO1_IO06 | PC, - .gp = IMX_GPIO_NR(1, 6) - } -}; - int dram_init(void) { gd->ram_size = imx_ddr_size(); @@ -214,6 +127,8 @@ int board_usb_phy_mode(int port) static int setup_dhcom_mac_from_fuse(void) { + struct udevice *dev; + ofnode eeprom; unsigned char enetaddr[6]; int ret; @@ -228,13 +143,19 @@ static int setup_dhcom_mac_from_fuse(void) return 0; } - ret = i2c_set_bus_num(2); + eeprom = ofnode_path("/soc/aips-bus@2100000/i2c@21a8000/eeprom@50"); + if (!ofnode_valid(eeprom)) { + printf("Invalid hardware path to EEPROM!\n"); + return -ENODEV; + } + + ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev); if (ret) { - printf("Error switching I2C bus!\n"); + printf("Cannot find EEPROM!\n"); return ret; } - ret = i2c_read(EEPROM_I2C_ADDRESS, 0xfa, 0x1, enetaddr, 0x6); + ret = i2c_eeprom_read(dev, 0xfa, enetaddr, 0x6); if (ret) { printf("Error reading configuration EEPROM!\n"); return ret; @@ -265,18 +186,6 @@ int board_init(void) /* Enable eim_slow clocks */ setbits_le32(&mxc_ccm->CCGR6, 0x1 << MXC_CCM_CCGR6_EMI_SLOW_OFFSET); -#ifdef CONFIG_SYS_I2C_MXC - if (is_mx6dq()) { - setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &dh6dq_i2c_pad_info0); - setup_i2c(1, CONFIG_SYS_I2C_SPEED, 0x7f, &dh6dq_i2c_pad_info1); - setup_i2c(2, CONFIG_SYS_I2C_SPEED, 0x7f, &dh6dq_i2c_pad_info2); - } else { - setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &dh6sdl_i2c_pad_info0); - setup_i2c(1, CONFIG_SYS_I2C_SPEED, 0x7f, &dh6sdl_i2c_pad_info1); - setup_i2c(2, CONFIG_SYS_I2C_SPEED, 0x7f, &dh6sdl_i2c_pad_info2); - } -#endif - setup_dhcom_mac_from_fuse(); return 0; diff --git a/configs/dh_imx6_defconfig b/configs/dh_imx6_defconfig index 387ab843b31..3022a187cc2 100644 --- a/configs/dh_imx6_defconfig +++ b/configs/dh_imx6_defconfig @@ -23,7 +23,6 @@ CONFIG_BOOTDELAY=3 CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE=y CONFIG_BOUNCE_BUFFER=y CONFIG_SPL_TEXT_BASE=0x00908000 -CONFIG_SPL_I2C_SUPPORT=y CONFIG_SPL_SPI_LOAD=y CONFIG_SPL_WATCHDOG_SUPPORT=y CONFIG_CMD_MEMTEST=y @@ -48,6 +47,10 @@ CONFIG_ENV_IS_IN_SPI_FLASH=y CONFIG_DWC_AHSATA=y CONFIG_BOOTCOUNT_LIMIT=y CONFIG_DM_GPIO=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_MXC=y +CONFIG_MISC=y +CONFIG_I2C_EEPROM=y CONFIG_DM_MMC=y CONFIG_FSL_ESDHC_IMX=y CONFIG_DM_SPI_FLASH=y diff --git a/include/configs/dh_imx6.h b/include/configs/dh_imx6.h index 3b1d0a99a19..7d2e5738467 100644 --- a/include/configs/dh_imx6.h +++ b/include/configs/dh_imx6.h @@ -48,14 +48,6 @@ #define CONFIG_FEC_MXC_PHYADDR 0 #define CONFIG_ARP_TIMEOUT 200UL -/* I2C Configs */ -#define CONFIG_SYS_I2C -#define CONFIG_SYS_I2C_MXC -#define CONFIG_SYS_I2C_MXC_I2C1 /* enable I2C bus 1 */ -#define CONFIG_SYS_I2C_MXC_I2C2 /* enable I2C bus 2 */ -#define CONFIG_SYS_I2C_MXC_I2C3 /* enable I2C bus 3 */ -#define CONFIG_SYS_I2C_SPEED 100000 - /* MMC Configs */ #define CONFIG_FSL_USDHC #define CONFIG_SYS_FSL_ESDHC_ADDR 0 -- cgit v1.3.1