diff options
author | Tom Rini <trini@konsulko.com> | 2018-10-25 10:16:21 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-10-25 10:16:21 -0400 |
commit | cf033e04da315ba949e804c127abae0134bda30f (patch) | |
tree | 77100d0a1c7ac1ce879509df57b845184f058550 /drivers | |
parent | 1ed3c0954bd160dafcad8847a51c3ddd5f992f51 (diff) | |
parent | 3c28576bb0f0990d699fd330089412e620706941 (diff) |
Merge tag 'u-boot-imx-20181025' of git://git.denx.de/u-boot-imx
Merged imx8 architecture, fix build for imx8 + warnings
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Kconfig | 1 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/imx/Kconfig | 6 | ||||
-rw-r--r-- | drivers/clk/imx/Makefile | 5 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx8.c | 393 | ||||
-rw-r--r-- | drivers/gpio/mxc_gpio.c | 30 | ||||
-rw-r--r-- | drivers/i2c/imx_lpi2c.c | 4 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/imx8/Makefile | 3 | ||||
-rw-r--r-- | drivers/misc/imx8/scu.c | 266 | ||||
-rw-r--r-- | drivers/misc/imx8/scu_api.c | 367 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 34 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/Kconfig | 18 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/Makefile | 2 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx.c | 209 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx.h | 16 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx8.c | 40 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-scu.c | 66 | ||||
-rw-r--r-- | drivers/power/domain/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/domain/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/domain/imx8-power-domain.c | 315 | ||||
-rw-r--r-- | drivers/serial/serial_lpuart.c | 143 | ||||
-rw-r--r-- | drivers/serial/serial_mxc.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 7 |
24 files changed, 1795 insertions, 142 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 7853a7a9d3..eadf7f8250 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -100,6 +100,7 @@ config CLK_STM32MP1 source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" +source "drivers/clk/imx/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/owl/Kconfig" source "drivers/clk/renesas/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 8bc9f520c1..f87c9aec85 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o clk_fixed_rate.o +obj-y += imx/ obj-y += tegra/ obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-$(CONFIG_ARCH_MESON) += clk_meson.o diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig new file mode 100644 index 0000000000..a6fb58d6cf --- /dev/null +++ b/drivers/clk/imx/Kconfig @@ -0,0 +1,6 @@ +config CLK_IMX8 + bool "Clock support for i.MX8" + depends on ARCH_IMX8 + select CLK + help + This enables support clock driver for i.MX8 platforms. diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile new file mode 100644 index 0000000000..5505ae52e2 --- /dev/null +++ b/drivers/clk/imx/Makefile @@ -0,0 +1,5 @@ +# Copyright 2018 NXP +# +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CLK_IMX8) += clk-imx8.o diff --git a/drivers/clk/imx/clk-imx8.c b/drivers/clk/imx/clk-imx8.c new file mode 100644 index 0000000000..fcb8090d35 --- /dev/null +++ b/drivers/clk/imx/clk-imx8.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <asm/arch/sci/sci.h> +#include <asm/arch/clock.h> +#include <dt-bindings/clock/imx8qxp-clock.h> +#include <dt-bindings/soc/imx_rsrc.h> +#include <misc.h> + +struct imx8_clks { + ulong id; + const char *name; +}; + +static struct imx8_clks imx8_clk_names[] = { + { IMX8QXP_A35_DIV, "A35_DIV" }, + { IMX8QXP_I2C0_CLK, "I2C0" }, + { IMX8QXP_I2C1_CLK, "I2C1" }, + { IMX8QXP_I2C2_CLK, "I2C2" }, + { IMX8QXP_I2C3_CLK, "I2C3" }, + { IMX8QXP_UART0_CLK, "UART0" }, + { IMX8QXP_UART1_CLK, "UART1" }, + { IMX8QXP_UART2_CLK, "UART2" }, + { IMX8QXP_UART3_CLK, "UART3" }, + { IMX8QXP_SDHC0_CLK, "SDHC0" }, + { IMX8QXP_SDHC1_CLK, "SDHC1" }, + { IMX8QXP_ENET0_AHB_CLK, "ENET0_AHB" }, + { IMX8QXP_ENET0_IPG_CLK, "ENET0_IPG" }, + { IMX8QXP_ENET0_REF_DIV, "ENET0_REF" }, + { IMX8QXP_ENET0_PTP_CLK, "ENET0_PTP" }, + { IMX8QXP_ENET1_AHB_CLK, "ENET1_AHB" }, + { IMX8QXP_ENET1_IPG_CLK, "ENET1_IPG" }, + { IMX8QXP_ENET1_REF_DIV, "ENET1_REF" }, + { IMX8QXP_ENET1_PTP_CLK, "ENET1_PTP" }, +}; + +static ulong imx8_clk_get_rate(struct clk *clk) +{ + sc_pm_clk_t pm_clk; + ulong rate; + u16 resource; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + switch (clk->id) { + case IMX8QXP_A35_DIV: + resource = SC_R_A35; + pm_clk = SC_PM_CLK_CPU; + break; + case IMX8QXP_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC0_IPG_CLK: + case IMX8QXP_SDHC0_CLK: + case IMX8QXP_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC1_IPG_CLK: + case IMX8QXP_SDHC1_CLK: + case IMX8QXP_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART0_IPG_CLK: + case IMX8QXP_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET0_IPG_CLK: + case IMX8QXP_ENET0_AHB_CLK: + case IMX8QXP_ENET0_REF_DIV: + case IMX8QXP_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET1_IPG_CLK: + case IMX8QXP_ENET1_AHB_CLK: + case IMX8QXP_ENET1_REF_DIV: + case IMX8QXP_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QXP_UART0_IPG_CLK || + clk->id >= IMX8QXP_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + }; + + ret = sc_pm_get_clock_rate(-1, resource, pm_clk, + (sc_pm_clock_rate_t *)&rate); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return rate; +} + +static ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate) +{ + sc_pm_clk_t pm_clk; + u32 new_rate = rate; + u16 resource; + int ret; + + debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate); + + switch (clk->id) { + case IMX8QXP_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC0_IPG_CLK: + case IMX8QXP_SDHC0_CLK: + case IMX8QXP_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC1_SEL: + case IMX8QXP_SDHC0_SEL: + return 0; + case IMX8QXP_SDHC1_IPG_CLK: + case IMX8QXP_SDHC1_CLK: + case IMX8QXP_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET0_IPG_CLK: + case IMX8QXP_ENET0_AHB_CLK: + case IMX8QXP_ENET0_REF_DIV: + case IMX8QXP_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET1_IPG_CLK: + case IMX8QXP_ENET1_AHB_CLK: + case IMX8QXP_ENET1_REF_DIV: + case IMX8QXP_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QXP_UART0_IPG_CLK || + clk->id >= IMX8QXP_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + }; + + ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return new_rate; +} + +static int __imx8_clk_enable(struct clk *clk, bool enable) +{ + sc_pm_clk_t pm_clk; + u16 resource; + int ret; + + debug("%s(#%lu)\n", __func__, clk->id); + + switch (clk->id) { + case IMX8QXP_I2C0_CLK: + resource = SC_R_I2C_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C1_CLK: + resource = SC_R_I2C_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C2_CLK: + resource = SC_R_I2C_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_I2C3_CLK: + resource = SC_R_I2C_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART0_CLK: + resource = SC_R_UART_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART1_CLK: + resource = SC_R_UART_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART2_CLK: + resource = SC_R_UART_2; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_UART3_CLK: + resource = SC_R_UART_3; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC0_IPG_CLK: + case IMX8QXP_SDHC0_CLK: + case IMX8QXP_SDHC0_DIV: + resource = SC_R_SDHC_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_SDHC1_IPG_CLK: + case IMX8QXP_SDHC1_CLK: + case IMX8QXP_SDHC1_DIV: + resource = SC_R_SDHC_1; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET0_IPG_CLK: + case IMX8QXP_ENET0_AHB_CLK: + case IMX8QXP_ENET0_REF_DIV: + case IMX8QXP_ENET0_PTP_CLK: + resource = SC_R_ENET_0; + pm_clk = SC_PM_CLK_PER; + break; + case IMX8QXP_ENET1_IPG_CLK: + case IMX8QXP_ENET1_AHB_CLK: + case IMX8QXP_ENET1_REF_DIV: + case IMX8QXP_ENET1_PTP_CLK: + resource = SC_R_ENET_1; + pm_clk = SC_PM_CLK_PER; + break; + default: + if (clk->id < IMX8QXP_UART0_IPG_CLK || + clk->id >= IMX8QXP_CLK_END) { + printf("%s(Invalid clk ID #%lu)\n", + __func__, clk->id); + return -EINVAL; + } + return -ENOTSUPP; + } + + ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0); + if (ret) { + printf("%s err %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int imx8_clk_disable(struct clk *clk) +{ + return __imx8_clk_enable(clk, 0); +} + +static int imx8_clk_enable(struct clk *clk) +{ + return __imx8_clk_enable(clk, 1); +} + +#if CONFIG_IS_ENABLED(CMD_CLK) +int soc_clk_dump(void) +{ + struct udevice *dev; + struct clk clk; + unsigned long rate; + int i, ret; + + ret = uclass_get_device_by_driver(UCLASS_CLK, + DM_GET_DRIVER(imx8_clk), &dev); + if (ret) + return ret; + + printf("Clk\t\tHz\n"); + + for (i = 0; i < ARRAY_SIZE(imx8_clk_names); i++) { + clk.id = imx8_clk_names[i].id; + ret = clk_request(dev, &clk); + if (ret < 0) { + debug("%s clk_request() failed: %d\n", __func__, ret); + continue; + } + + ret = clk_get_rate(&clk); + rate = ret; + + clk_free(&clk); + + if (ret == -ENOTSUPP) { + printf("clk ID %lu not supported yet\n", + imx8_clk_names[i].id); + continue; + } + if (ret < 0) { + printf("%s %lu: get_rate err: %d\n", + __func__, imx8_clk_names[i].id, ret); + continue; + } + + printf("%s(%3lu):\t%lu\n", + imx8_clk_names[i].name, imx8_clk_names[i].id, rate); + } + + return 0; +} +#endif + +static struct clk_ops imx8_clk_ops = { + .set_rate = imx8_clk_set_rate, + .get_rate = imx8_clk_get_rate, + .enable = imx8_clk_enable, + .disable = imx8_clk_disable, +}; + +static int imx8_clk_probe(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id imx8_clk_ids[] = { + { .compatible = "fsl,imx8qxp-clk" }, + { }, +}; + +U_BOOT_DRIVER(imx8_clk) = { + .name = "clk_imx8", + .id = UCLASS_CLK, + .of_match = imx8_clk_ids, + .ops = &imx8_clk_ops, + .probe = imx8_clk_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c index d8e72ada19..b820160ae7 100644 --- a/drivers/gpio/mxc_gpio.c +++ b/drivers/gpio/mxc_gpio.c @@ -40,21 +40,27 @@ static unsigned long gpio_ports[] = { [2] = GPIO3_BASE_ADDR, #if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \ defined(CONFIG_MX53) || defined(CONFIG_MX6) || \ - defined(CONFIG_MX7) || defined(CONFIG_MX8M) + defined(CONFIG_MX7) || defined(CONFIG_MX8M) || \ + defined(CONFIG_ARCH_IMX8) [3] = GPIO4_BASE_ADDR, #endif #if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \ - defined(CONFIG_MX7) || defined(CONFIG_MX8M) + defined(CONFIG_MX7) || defined(CONFIG_MX8M) || \ + defined(CONFIG_ARCH_IMX8) [4] = GPIO5_BASE_ADDR, #if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) || defined(CONFIG_MX8M)) [5] = GPIO6_BASE_ADDR, #endif #endif -#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) +#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \ + defined(CONFIG_ARCH_IMX8) #if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)) [6] = GPIO7_BASE_ADDR, #endif #endif +#if defined(CONFIG_ARCH_IMX8) + [7] = GPIO8_BASE_ADDR, +#endif }; static int mxc_gpio_direction(unsigned int gpio, @@ -347,19 +353,22 @@ static const struct mxc_gpio_plat mxc_plat[] = { { 2, (struct gpio_regs *)GPIO3_BASE_ADDR }, #if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \ defined(CONFIG_MX53) || defined(CONFIG_MX6) || \ - defined(CONFIG_MX8M) + defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8) { 3, (struct gpio_regs *)GPIO4_BASE_ADDR }, #endif #if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \ - defined(CONFIG_MX8M) + defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8) { 4, (struct gpio_regs *)GPIO5_BASE_ADDR }, #ifndef CONFIG_MX8M { 5, (struct gpio_regs *)GPIO6_BASE_ADDR }, #endif #endif -#if defined(CONFIG_MX53) || defined(CONFIG_MX6) +#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8) { 6, (struct gpio_regs *)GPIO7_BASE_ADDR }, #endif +#if defined(CONFIG_ARCH_IMX8) + { 7, (struct gpio_regs *)GPIO8_BASE_ADDR }, +#endif }; U_BOOT_DEVICES(mxc_gpios) = { @@ -368,19 +377,22 @@ U_BOOT_DEVICES(mxc_gpios) = { { "gpio_mxc", &mxc_plat[2] }, #if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \ defined(CONFIG_MX53) || defined(CONFIG_MX6) || \ - defined(CONFIG_MX8M) + defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8) { "gpio_mxc", &mxc_plat[3] }, #endif #if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \ - defined(CONFIG_MX8M) + defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8) { "gpio_mxc", &mxc_plat[4] }, #ifndef CONFIG_MX8M { "gpio_mxc", &mxc_plat[5] }, #endif #endif -#if defined(CONFIG_MX53) || defined(CONFIG_MX6) +#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8) { "gpio_mxc", &mxc_plat[6] }, #endif +#if defined(CONFIG_ARCH_IMX8) + { "gpio_mxc", &mxc_plat[7] }, +#endif }; #endif #endif diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index 6c343072fb..4586d4331f 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -105,7 +105,7 @@ static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len) while (len--) { result = bus_i2c_wait_for_tx_ready(regs); if (result) { - debug("i2c: send wait fot tx ready: %d\n", result); + debug("i2c: send wait for tx ready: %d\n", result); return result; } writel(*txbuf++, ®s->mtdr); @@ -482,7 +482,7 @@ static int imx_lpi2c_probe(struct udevice *bus) if (ret < 0) return ret; - debug("i2c : controller bus %d at %lu , speed %d: ", + debug("i2c : controller bus %d at 0x%lx , speed %d: ", bus->seq, i2c_bus->base, i2c_bus->speed); diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index cf6587268e..759d2c791b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_FSL_IIM) += fsl_iim.o obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o +obj-$(CONFIG_IMX8) += imx8/ obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o diff --git a/drivers/misc/imx8/Makefile b/drivers/misc/imx8/Makefile new file mode 100644 index 0000000000..ee05893cbb --- /dev/null +++ b/drivers/misc/imx8/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += scu_api.o scu.o diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c new file mode 100644 index 0000000000..0647ddf103 --- /dev/null +++ b/drivers/misc/imx8/scu.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <dm/device-internal.h> +#include <asm/arch/sci/sci.h> +#include <linux/iopoll.h> +#include <misc.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct mu_type { + u32 tr[4]; + u32 rr[4]; + u32 sr; + u32 cr; +}; + +struct imx8_scu { + struct mu_type *base; + struct udevice *clk; + struct udevice *pinclk; +}; + +#define MU_CR_GIE_MASK 0xF0000000u +#define MU_CR_RIE_MASK 0xF000000u +#define MU_CR_GIR_MASK 0xF0000u +#define MU_CR_TIE_MASK 0xF00000u +#define MU_CR_F_MASK 0x7u +#define MU_SR_TE0_MASK BIT(23) +#define MU_SR_RF0_MASK BIT(27) +#define MU_TR_COUNT 4 +#define MU_RR_COUNT 4 + +static inline void mu_hal_init(struct mu_type *base) +{ + /* Clear GIEn, RIEn, TIEn, GIRn and ABFn. */ + clrbits_le32(&base->cr, MU_CR_GIE_MASK | MU_CR_RIE_MASK | + MU_CR_TIE_MASK | MU_CR_GIR_MASK | MU_CR_F_MASK); +} + +static int mu_hal_sendmsg(struct mu_type *base, u32 reg_index, u32 msg) +{ + u32 mask = MU_SR_TE0_MASK >> reg_index; + u32 val; + int ret; + + assert(reg_index < MU_TR_COUNT); + + /* Wait TX register to be empty. */ + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000); + if (ret < 0) { + printf("%s timeout\n", __func__); + return -ETIMEDOUT; + } + + writel(msg, &base->tr[reg_index]); + + return 0; +} + +static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 *msg) +{ + u32 mask = MU_SR_RF0_MASK >> reg_index; + u32 val; + int ret; + + assert(reg_index < MU_TR_COUNT); + + /* Wait RX register to be full. */ + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000); + if (ret < 0) { + printf("%s timeout\n", __func__); + return -ETIMEDOUT; + } + + *msg = readl(&base->rr[reg_index]); + + return 0; +} + +static int sc_ipc_read(struct mu_type *base, void *data) +{ + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; + int ret; + u8 count = 0; + + if (!msg) + return -EINVAL; + + /* Read first word */ + ret = mu_hal_receivemsg(base, 0, (u32 *)msg); + if (ret) + return ret; + count++; + + /* Check size */ + if (msg->size > SC_RPC_MAX_MSG) { + *((u32 *)msg) = 0; + return -EINVAL; + } + + /* Read remaining words */ + while (count < msg->size) { + ret = mu_hal_receivemsg(base, count % MU_RR_COUNT, + &msg->DATA.u32[count - 1]); + if (ret) + return ret; + count++; + } + + return 0; +} + +static int sc_ipc_write(struct mu_type *base, void *data) +{ + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; + int ret; + u8 count = 0; + + if (!msg) + return -EINVAL; + + /* Check size */ + if (msg->size > SC_RPC_MAX_MSG) + return -EINVAL; + + /* Write first word */ + ret = mu_hal_sendmsg(base, 0, *((u32 *)msg)); + if (ret) + return ret; + count++; + + /* Write remaining words */ + while (count < msg->size) { + ret = mu_hal_sendmsg(base, count % MU_TR_COUNT, + msg->DATA.u32[count - 1]); + if (ret) + return ret; + count++; + } + + return 0; +} + +/* + * Note the function prototype use msgid as the 2nd parameter, here + * we take it as no_resp. + */ +static int imx8_scu_call(struct udevice *dev, int no_resp, void *tx_msg, + int tx_size, void *rx_msg, int rx_size) +{ + struct imx8_scu *priv = dev_get_priv(dev); + sc_err_t result; + int ret; + + /* Expect tx_msg, rx_msg are the same value */ + if (rx_msg && tx_msg != rx_msg) + printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg); + + ret = sc_ipc_write(priv->base, tx_msg); + if (ret) + return ret; + if (!no_resp) { + ret = sc_ipc_read(priv->base, rx_msg); + if (ret) + return ret; + } + + result = RPC_R8((struct sc_rpc_msg_s *)tx_msg); + + return sc_err_to_linux(result); +} + +static int imx8_scu_probe(struct udevice *dev) +{ + struct imx8_scu *priv = dev_get_priv(dev); + fdt_addr_t addr; + + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); + + addr = devfdt_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = (struct mu_type *)addr; + + /* U-Boot not enable interrupts, so need to enable RX interrupts */ + mu_hal_init(priv->base); + + gd->arch.scu_dev = dev; + + device_probe(priv->clk); + device_probe(priv->pinclk); + + return 0; +} + +static int imx8_scu_remove(struct udevice *dev) +{ + return 0; +} + +static int imx8_scu_bind(struct udevice *dev) +{ + struct imx8_scu *priv = dev_get_priv(dev); + int ret; + struct udevice *child; + int node; + + debug("%s(dev=%p)\n", __func__, dev); + + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, + "fsl,imx8qxp-clk"); + if (node < 0) + panic("No clk node found\n"); + + ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child); + if (ret) + return ret; + + priv->clk = child; + + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, + "fsl,imx8qxp-iomuxc"); + if (node < 0) + panic("No iomuxc node found\n"); + + ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child); + if (ret) + return ret; + + priv->pinclk = child; + + return 0; +} + +static struct misc_ops imx8_scu_ops = { + .call = imx8_scu_call, +}; + +static const struct udevice_id imx8_scu_ids[] = { + { .compatible = "fsl,imx8qxp-mu" }, + { .compatible = "fsl,imx8-mu" }, + { } +}; + +U_BOOT_DRIVER(imx8_scu) = { + .name = "imx8_scu", + .id = UCLASS_MISC, + .of_match = imx8_scu_ids, + .probe = imx8_scu_probe, + .bind = imx8_scu_bind, + .remove = imx8_scu_remove, + .ops = &imx8_scu_ops, + .priv_auto_alloc_size = sizeof(struct imx8_scu), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/misc/imx8/scu_api.c b/drivers/misc/imx8/scu_api.c new file mode 100644 index 0000000000..65080d7544 --- /dev/null +++ b/drivers/misc/imx8/scu_api.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <asm/arch/sci/sci.h> +#include <misc.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* CLK and PM */ +int sc_pm_set_clock_rate(sc_ipc_t ipc, sc_rsrc_t resource, sc_pm_clk_t clk, + sc_pm_clock_rate_t *rate) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM; + RPC_FUNC(&msg) = (u8)PM_FUNC_SET_CLOCK_RATE; + RPC_U32(&msg, 0U) = *(u32 *)rate; + RPC_U16(&msg, 4U) = (u16)resource; + RPC_U8(&msg, 6U) = (u8)clk; + RPC_SIZE(&msg) = 3U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: rate:%u resource:%u: clk:%u res:%d\n", + __func__, *rate, resource, clk, RPC_R8(&msg)); + + *rate = RPC_U32(&msg, 0U); + + return ret; +} + +int sc_pm_get_clock_rate(sc_ipc_t ipc, sc_rsrc_t resource, sc_pm_clk_t clk, + sc_pm_clock_rate_t *rate) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM; + RPC_FUNC(&msg) = (u8)PM_FUNC_GET_CLOCK_RATE; + RPC_U16(&msg, 0U) = (u16)resource; + RPC_U8(&msg, 2U) = (u8)clk; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) { + printf("%s: resource:%d clk:%d: res:%d\n", + __func__, resource, clk, RPC_R8(&msg)); + return ret; + } + + if (rate) + *rate = RPC_U32(&msg, 0U); + + return 0; +} + +int sc_pm_clock_enable(sc_ipc_t ipc, sc_rsrc_t resource, sc_pm_clk_t clk, + sc_bool_t enable, sc_bool_t autog) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM; + RPC_FUNC(&msg) = (u8)PM_FUNC_CLOCK_ENABLE; + RPC_U16(&msg, 0U) = (u16)resource; + RPC_U8(&msg, 2U) = (u8)clk; + RPC_U8(&msg, 3U) = (u8)enable; + RPC_U8(&msg, 4U) = (u8)autog; + RPC_SIZE(&msg) = 3U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: resource:%d clk:%d: enable:%d autog: %d, res:%d\n", + __func__, resource, clk, enable, autog, RPC_R8(&msg)); + + return ret; +} + +int sc_pm_set_resource_power_mode(sc_ipc_t ipc, sc_rsrc_t resource, + sc_pm_power_mode_t mode) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM; + RPC_FUNC(&msg) = (u8)PM_FUNC_SET_RESOURCE_POWER_MODE; + RPC_U16(&msg, 0U) = (u16)resource; + RPC_U8(&msg, 2U) = (u8)mode; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: resource:%d mode:%d: res:%d\n", + __func__, resource, mode, RPC_R8(&msg)); + + return ret; +} + +/* PAD */ +int sc_pad_set(sc_ipc_t ipc, sc_pad_t pad, u32 val) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_PAD; + RPC_FUNC(&msg) = (u8)PAD_FUNC_SET; + RPC_U32(&msg, 0U) = (u32)val; + RPC_U16(&msg, 4U) = (u16)pad; + RPC_SIZE(&msg) = 3U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: val:%d pad:%d: res:%d\n", + __func__, val, pad, RPC_R8(&msg)); + + return ret; +} + +/* MISC */ +int sc_misc_get_control(sc_ipc_t ipc, sc_rsrc_t resource, sc_ctrl_t ctrl, + u32 *val) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC; + RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_CONTROL; + RPC_U32(&msg, 0U) = (u32)ctrl; + RPC_U16(&msg, 4U) = (u16)resource; + RPC_SIZE(&msg) = 3U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: ctrl:%d resource:%d: res:%d\n", + __func__, ctrl, resource, RPC_R8(&msg)); + + if (!val) + *val = RPC_U32(&msg, 0U); + + return ret; +} + +void sc_misc_get_boot_dev(sc_ipc_t ipc, sc_rsrc_t *boot_dev) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC; + RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_BOOT_DEV; + RPC_SIZE(&msg) = 1U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: res:%d\n", __func__, RPC_R8(&msg)); + + if (!boot_dev) + *boot_dev = RPC_U16(&msg, 0U); +} + +void sc_misc_boot_status(sc_ipc_t ipc, sc_misc_boot_status_t status) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC; + RPC_FUNC(&msg) = (u8)MISC_FUNC_BOOT_STATUS; + RPC_U8(&msg, 0U) = (u8)status; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_TRUE, &msg, size, &msg, size); + if (ret) + printf("%s: status:%d res:%d\n", + __func__, status, RPC_R8(&msg)); +} + +void sc_misc_build_info(sc_ipc_t ipc, u32 *build, u32 *commit) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = SC_RPC_SVC_MISC; + RPC_FUNC(&msg) = MISC_FUNC_BUILD_INFO; + RPC_SIZE(&msg) = 1; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret < 0) { + printf("%s: err: %d\n", __func__, ret); + return; + } + + if (build) + *build = RPC_U32(&msg, 0); + if (commit) + *commit = RPC_U32(&msg, 4); +} + +int sc_misc_otp_fuse_read(sc_ipc_t ipc, u32 word, u32 *val) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = SC_RPC_SVC_MISC; + RPC_FUNC(&msg) = MISC_FUNC_OTP_FUSE_READ; + RPC_U32(&msg, 0) = word; + RPC_SIZE(&msg) = 2; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret < 0) + return ret; + + if (val) + *val = RPC_U32(&msg, 0U); + + return 0; +} + +/* RM */ +sc_bool_t sc_rm_is_memreg_owned(sc_ipc_t ipc, sc_rm_mr_t mr) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + sc_err_t result; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM; + RPC_FUNC(&msg) = (u8)RM_FUNC_IS_MEMREG_OWNED; + RPC_U8(&msg, 0U) = (u8)mr; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + result = RPC_R8(&msg); + + if (result != 0 && result != 1) { + printf("%s: mr:%d res:%d\n", __func__, mr, RPC_R8(&msg)); + if (ret) + printf("%s: mr:%d res:%d\n", __func__, mr, + RPC_R8(&msg)); + } + + return (sc_bool_t)result; +} + +int sc_rm_get_memreg_info(sc_ipc_t ipc, sc_rm_mr_t mr, sc_faddr_t *addr_start, + sc_faddr_t *addr_end) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM; + RPC_FUNC(&msg) = (u8)RM_FUNC_GET_MEMREG_INFO; + RPC_U8(&msg, 0U) = (u8)mr; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + printf("%s: mr:%d res:%d\n", __func__, mr, RPC_R8(&msg)); + + if (addr_start) + *addr_start = ((u64)RPC_U32(&msg, 0U) << 32U) | + RPC_U32(&msg, 4U); + + if (addr_end) + *addr_end = ((u64)RPC_U32(&msg, 8U) << 32U) | + RPC_U32(&msg, 12U); + + return ret; +} + +sc_bool_t sc_rm_is_resource_owned(sc_ipc_t ipc, sc_rsrc_t resource) +{ + struct udevice *dev = gd->arch.scu_dev; + int size = sizeof(struct sc_rpc_msg_s); + struct sc_rpc_msg_s msg; + int ret; + u8 result; + + if (!dev) + hang(); + + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM; + RPC_FUNC(&msg) = (u8)RM_FUNC_IS_RESOURCE_OWNED; + RPC_U16(&msg, 0U) = (u16)resource; + RPC_SIZE(&msg) = 2U; + + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + result = RPC_R8(&msg); + if (result != 0 && result != 1) { + printf("%s: resource:%d res:%d\n", + __func__, resource, RPC_R8(&msg)); + if (ret) + printf("%s: res:%d res:%d\n", __func__, resource, + RPC_R8(&msg)); + } + + return !!result; +} diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 03c6743ae8..3cdfa7f5a6 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -11,6 +11,7 @@ #include <config.h> #include <common.h> #include <command.h> +#include <clk.h> #include <errno.h> #include <hwconfig.h> #include <mmc.h> @@ -121,6 +122,7 @@ struct esdhc_soc_data { struct fsl_esdhc_priv { struct fsl_esdhc *esdhc_regs; unsigned int sdhc_clk; + struct clk per_clk; unsigned int clock; unsigned int mode; unsigned int bus_width; @@ -257,7 +259,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, int timeout; struct fsl_esdhc *regs = priv->esdhc_regs; #if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_MX8M) + defined(CONFIG_IMX8) || defined(CONFIG_MX8M) dma_addr_t addr; #endif uint wml_value; @@ -271,7 +273,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO #if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_MX8M) + defined(CONFIG_IMX8) || defined(CONFIG_MX8M) addr = virt_to_phys((void *)(data->dest)); if (upper_32_bits(addr)) printf("Error found for upper 32 bits\n"); @@ -301,7 +303,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, wml_value << 16); #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO #if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_MX8M) + defined(CONFIG_IMX8) || defined(CONFIG_MX8M) addr = virt_to_phys((void *)(data->src)); if (upper_32_bits(addr)) printf("Error found for upper 32 bits\n"); @@ -367,7 +369,7 @@ static void check_and_invalidate_dcache_range unsigned size = roundup(ARCH_DMA_MINALIGN, data->blocks*data->blocksize); #if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_MX8M) + defined(CONFIG_IMX8) || defined(CONFIG_MX8M) dma_addr_t addr; addr = virt_to_phys((void *)(data->dest)); @@ -1496,10 +1498,26 @@ static int fsl_esdhc_probe(struct udevice *dev) init_clk_usdhc(dev->seq); - priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq); - if (priv->sdhc_clk <= 0) { - dev_err(dev, "Unable to get clk for %s\n", dev->name); - return -EINVAL; + if (IS_ENABLED(CONFIG_CLK)) { + /* Assigned clock already set clock */ + ret = clk_get_by_name(dev, "per", &priv->per_clk); + if (ret) { + printf("Failed to get per_clk\n"); + return ret; + } + ret = clk_enable(&priv->per_clk); + if (ret) { + printf("Failed to enable per_clk\n"); + return ret; + } + + priv->sdhc_clk = clk_get_rate(&priv->per_clk); + } else { + priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq); + if (priv->sdhc_clk <= 0) { + dev_err(dev, "Unable to get clk for %s\n", dev->name); + return -EINVAL; + } } ret = fsl_esdhc_init(priv, plat); diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index b668359a0b..799d1d2465 100644 --- a/drivers/pinctrl/nxp/Kconfig +++ b/drivers/pinctrl/nxp/Kconfig @@ -1,6 +1,9 @@ config PINCTRL_IMX bool +config PINCTRL_IMX_SCU + bool + config PINCTRL_IMX5 bool "IMX5 pinctrl driver" depends on ARCH_MX5 && PINCTRL_FULL @@ -56,3 +59,18 @@ config PINCTRL_IMX7ULP is different from the linux one, this is a simple implementation, only parses the 'fsl,pins' property and configure related registers. + +config PINCTRL_IMX8 + bool "IMX8 pinctrl driver" + depends on ARCH_IMX8 && PINCTRL_FULL + select DEVRES + select PINCTRL_IMX + select PINCTRL_IMX_SCU + help + Say Y here to enable the imx8 pinctrl driver + + This provides a simple pinctrl driver for i.MX8 SoC familiy. + This feature depends on device tree configuration. This driver + is different from the linux one, this is a simple implementation, + only parses the 'fsl,pins' property and configures related + registers. diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile index c763948376..310b3b3a2e 100644 --- a/drivers/pinctrl/nxp/Makefile +++ b/drivers/pinctrl/nxp/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_PINCTRL_IMX5) += pinctrl-imx5.o obj-$(CONFIG_PINCTRL_IMX6) += pinctrl-imx6.o obj-$(CONFIG_PINCTRL_IMX7) += pinctrl-imx7.o obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o +obj-$(CONFIG_PINCTRL_IMX_SCU) += pinctrl-scu.o +obj-$(CONFIG_PINCTRL_IMX8) += pinctrl-imx8.o diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c index 36e1e8983c..04ea82aba5 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.c +++ b/drivers/pinctrl/nxp/pinctrl-imx.c @@ -28,7 +28,9 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) dev_dbg(dev, "%s: %s\n", __func__, config->name); - if (info->flags & SHARE_MUX_CONF_REG) + if (info->flags & IMX8_USE_SCU) + pin_size = SHARE_IMX8_PIN_SIZE; + else if (info->flags & SHARE_MUX_CONF_REG) pin_size = SHARE_FSL_PIN_SIZE; else pin_size = FSL_PIN_SIZE; @@ -58,112 +60,127 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) npins = size / pin_size; - /* - * Refer to linux documentation for details: - * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt - */ - for (i = 0; i < npins; i++) { - mux_reg = pin_data[j++]; - - if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) - mux_reg = -1; - - if (info->flags & SHARE_MUX_CONF_REG) { - conf_reg = mux_reg; - } else { - conf_reg = pin_data[j++]; - if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg) - conf_reg = -1; - } + if (info->flags & IMX8_USE_SCU) { + imx_pinctrl_scu_conf_pins(info, pin_data, npins); + } else { + /* + * Refer to linux documentation for details: + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt + */ + for (i = 0; i < npins; i++) { + mux_reg = pin_data[j++]; - if ((mux_reg == -1) || (conf_reg == -1)) { - dev_err(dev, "Error mux_reg or conf_reg\n"); - devm_kfree(dev, pin_data); - return -EINVAL; - } + if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) + mux_reg = -1; - input_reg = pin_data[j++]; - mux_mode = pin_data[j++]; - input_val = pin_data[j++]; - config_val = pin_data[j++]; + if (info->flags & SHARE_MUX_CONF_REG) { + conf_reg = mux_reg; + } else { + conf_reg = pin_data[j++]; + if (!(info->flags & ZERO_OFFSET_VALID) && + !conf_reg) + conf_reg = -1; + } - dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, input_reg 0x%x, " - "mux_mode 0x%x, input_val 0x%x, config_val 0x%x\n", - mux_reg, conf_reg, input_reg, mux_mode, input_val, - config_val); + if ((mux_reg == -1) || (conf_reg == -1)) { + dev_err(dev, "Error mux_reg or conf_reg\n"); + devm_kfree(dev, pin_data); + return -EINVAL; + } - if (config_val & IMX_PAD_SION) - mux_mode |= IOMUXC_CONFIG_SION; + input_reg = pin_data[j++]; + mux_mode = pin_data[j++]; + input_val = pin_data[j++]; + config_val = pin_data[j++]; - config_val &= ~IMX_PAD_SION; + dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, " + "input_reg 0x%x, mux_mode 0x%x, " + "input_val 0x%x, config_val 0x%x\n", + mux_reg, conf_reg, input_reg, mux_mode, + input_val, config_val); - /* Set Mux */ - if (info->flags & SHARE_MUX_CONF_REG) { - clrsetbits_le32(info->base + mux_reg, info->mux_mask, - mux_mode << mux_shift); - } else { - writel(mux_mode, info->base + mux_reg); - } + if (config_val & IMX_PAD_SION) + mux_mode |= IOMUXC_CONFIG_SION; - dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", mux_reg, - mux_mode); + config_val &= ~IMX_PAD_SION; - /* - * Set select input - * - * If the select input value begins with 0xff, it's a quirky - * select input and the value should be interpreted as below. - * 31 23 15 7 0 - * | 0xff | shift | width | select | - * It's used to work around the problem that the select - * input for some pin is not implemented in the select - * input register but in some general purpose register. - * We encode the select input value, width and shift of - * the bit field into input_val cell of pin function ID - * in device tree, and then decode them here for setting - * up the select input bits in general purpose register. - */ + /* Set Mux */ + if (info->flags & SHARE_MUX_CONF_REG) { + clrsetbits_le32(info->base + mux_reg, + info->mux_mask, + mux_mode << mux_shift); + } else { + writel(mux_mode, info->base + mux_reg); + } + + dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", + mux_reg, mux_mode); - if (input_val >> 24 == 0xff) { - u32 val = input_val; - u8 select = val & 0xff; - u8 width = (val >> 8) & 0xff; - u8 shift = (val >> 16) & 0xff; - u32 mask = ((1 << width) - 1) << shift; - /* - * The input_reg[i] here is actually some IOMUXC general - * purpose register, not regular select input register. - */ - val = readl(info->base + input_reg); - val &= ~mask; - val |= select << shift; - writel(val, info->base + input_reg); - } else if (input_reg) { /* - * Regular select input register can never be at offset - * 0, and we only print register value for regular case. + * Set select input + * + * If the select input value begins with 0xff, + * it's a quirky select input and the value should + * be interpreted as below. + * 31 23 15 7 0 + * | 0xff | shift | width | select | + * It's used to work around the problem that the + * select input for some pin is not implemented in + * the select input register but in some general + * purpose register. We encode the select input + * value, width and shift of the bit field into + * input_val cell of pin function ID in device tree, + * and then decode them here for setting up the select + * input bits in general purpose register. */ - if (info->input_sel_base) - writel(input_val, info->input_sel_base + - input_reg); - else - writel(input_val, info->base + input_reg); - - dev_dbg(dev, "select_input: offset 0x%x val 0x%x\n", - input_reg, input_val); - } - /* Set config */ - if (!(config_val & IMX_NO_PAD_CTL)) { - if (info->flags & SHARE_MUX_CONF_REG) { - clrsetbits_le32(info->base + conf_reg, - ~info->mux_mask, config_val); - } else { - writel(config_val, info->base + conf_reg); + if (input_val >> 24 == 0xff) { + u32 val = input_val; + u8 select = val & 0xff; + u8 width = (val >> 8) & 0xff; + u8 shift = (val >> 16) & 0xff; + u32 mask = ((1 << width) - 1) << shift; + /* + * The input_reg[i] here is actually some + * IOMUXC general purpose register, not + * regular select input register. + */ + val = readl(info->base + input_reg); + val &= ~mask; + val |= select << shift; + writel(val, info->base + input_reg); + } else if (input_reg) { + /* + * Regular select input register can never be + * at offset 0, and we only print register + * value for regular case. + */ + if (info->input_sel_base) + writel(input_val, + info->input_sel_base + + input_reg); + else + writel(input_val, + info->base + input_reg); + + dev_dbg(dev, "select_input: offset 0x%x val " + "0x%x\n", input_reg, input_val); } - dev_dbg(dev, "write config: offset 0x%x val 0x%x\n", - conf_reg, config_val); + /* Set config */ + if (!(config_val & IMX_NO_PAD_CTL)) { + if (info->flags & SHARE_MUX_CONF_REG) { + clrsetbits_le32(info->base + conf_reg, + ~info->mux_mask, + config_val); + } else { + writel(config_val, + info->base + conf_reg); + } + + dev_dbg(dev, "write config: offset 0x%x val " + "0x%x\n", conf_reg, config_val); + } } } @@ -193,6 +210,9 @@ int imx_pinctrl_probe(struct udevice *dev, priv->dev = dev; priv->info = info; + if (info->flags & IMX8_USE_SCU) + return 0; + addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg", &size); @@ -238,6 +258,9 @@ int imx_pinctrl_remove(struct udevice *dev) struct imx_pinctrl_priv *priv = dev_get_priv(dev); struct imx_pinctrl_soc_info *info = priv->info; + if (info->flags & IMX8_USE_SCU) + return 0; + if (info->input_sel_base) unmap_sysmem(info->input_sel_base); if (info->base) diff --git a/drivers/pinctrl/nxp/pinctrl-imx.h b/drivers/pinctrl/nxp/pinctrl-imx.h index b0032455b7..947975ee72 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.h +++ b/drivers/pinctrl/nxp/pinctrl-imx.h @@ -40,13 +40,29 @@ extern const struct pinctrl_ops imx_pinctrl_ops; #define FSL_PIN_SIZE 24 #define SHARE_FSL_PIN_SIZE 20 +/* Each pin on imx8qm/qxp consists of 2 u32 PIN_FUNC_ID and 1 u32 CONFIG */ +#define SHARE_IMX8_PIN_SIZE 12 + #define SHARE_MUX_CONF_REG 0x1 #define ZERO_OFFSET_VALID 0x2 #define CONFIG_IBE_OBE 0x4 +#define IMX8_USE_SCU 0x8 #define IOMUXC_CONFIG_SION (0x1 << 4) int imx_pinctrl_probe(struct udevice *dev, struct imx_pinctrl_soc_info *info); int imx_pinctrl_remove(struct udevice *dev); + +#ifdef CONFIG_PINCTRL_IMX_SCU +int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, + u32 *pin_data, int npins); +#else +static inline int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, + u32 *pin_data, int npins) +{ + return 0; +} +#endif + #endif /* __DRIVERS_PINCTRL_IMX_H */ diff --git a/drivers/pinctrl/nxp/pinctrl-imx8.c b/drivers/pinctrl/nxp/pinctrl-imx8.c new file mode 100644 index 0000000000..0738da0ebe --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-imx8.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <common.h> +#include <dm/device.h> +#include <dm/pinctrl.h> + +#include "pinctrl-imx.h" + +DECLARE_GLOBAL_DATA_PTR; + +static struct imx_pinctrl_soc_info imx8_pinctrl_soc_info = { + .flags = IMX8_USE_SCU, +}; + +static int imx8_pinctrl_probe(struct udevice *dev) +{ + struct imx_pinctrl_soc_info *info = + (struct imx_pinctrl_soc_info *)dev_get_driver_data(dev); + + return imx_pinctrl_probe(dev, info); +} + +static const struct udevice_id imx8_pinctrl_match[] = { + { .compatible = "fsl,imx8qxp-iomuxc", .data = (ulong)&imx8_pinctrl_soc_info }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(imx8_pinctrl) = { + .name = "imx8_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = of_match_ptr(imx8_pinctrl_match), + .probe = imx8_pinctrl_probe, + .remove = imx_pinctrl_remove, + .priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv), + .ops = &imx_pinctrl_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/nxp/pinctrl-scu.c b/drivers/pinctrl/nxp/pinctrl-scu.c new file mode 100644 index 0000000000..aa11075e0a --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-scu.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <common.h> +#include <errno.h> +#include <linux/bitops.h> +#include <asm/io.h> +#include <asm/arch/sci/sci.h> +#include <misc.h> + +#include "pinctrl-imx.h" + +#define PADRING_IFMUX_EN_SHIFT 31 +#define PADRING_IFMUX_EN_MASK BIT(31) +#define PADRING_GP_EN_SHIFT 30 +#define PADRING_GP_EN_MASK BIT(30) +#define PADRING_IFMUX_SHIFT 27 +#define PADRING_IFMUX_MASK GENMASK(29, 27) + +static int imx_pinconf_scu_set(struct imx_pinctrl_soc_info *info, u32 pad, + u32 mux, u32 val) +{ + int ret; + + /* + * Mux should be done in pmx set, but we do not have a good api + * to handle that in scfw, so config it in pad conf func + */ + + val |= PADRING_IFMUX_EN_MASK; + val |= PADRING_GP_EN_MASK; + val |= (mux << PADRING_IFMUX_SHIFT) & PADRING_IFMUX_MASK; + + ret = sc_pad_set(-1, pad, val); + if (ret) + printf("%s %d\n", __func__, ret); + + return 0; +} + +int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, u32 *pin_data, + int npins) +{ + int pin_id, mux, config_val; + int i, j = 0; + int ret; + + /* + * Refer to linux documentation for details: + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt + */ + for (i = 0; i < npins; i++) { + pin_id = pin_data[j++]; + mux = pin_data[j++]; + config_val = pin_data[j++]; + + ret = imx_pinconf_scu_set(info, pin_id, mux, config_val); + if (ret) + printf("Set pin %d, mux %d, val %d, error\n", pin_id, + mux, config_val); + } + + return 0; +} diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index 2c344888ca..a08b4288b4 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -16,6 +16,13 @@ config BCM6328_POWER_DOMAIN Enable support for manipulating BCM6345 power domains via MMIO mapped registers. +config IMX8_POWER_DOMAIN + bool "Enable i.MX8 power domain driver" + depends on ARCH_IMX8 + help + Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC + requests to the SCU. + config MESON_GX_VPU_POWER_DOMAIN bool "Enable Amlogic Meson GX VPU power domain driver" depends on ARCH_MESON @@ -44,5 +51,4 @@ config TI_SCI_POWER_DOMAIN help Generic power domain implementation for TI devices implementing the TI SCI protocol. - endmenu diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 6bdaa175e9..23fb1ecede 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_$(SPL_)POWER_DOMAIN) += power-domain-uclass.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o +obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o diff --git a/drivers/power/domain/imx8-power-domain.c b/drivers/power/domain/imx8-power-domain.c new file mode 100644 index 0000000000..d51dbaa6c0 --- /dev/null +++ b/drivers/power/domain/imx8-power-domain.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017 NXP + */ + +#include <common.h> +#include <dm.h> +#include <power-domain-uclass.h> +#include <asm/io.h> +#include <asm/arch/power-domain.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <asm/arch/sci/sci.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct imx8_power_domain_priv { + bool state_on; +}; + +static int imx8_power_domain_request(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p)\n", __func__, power_domain); + + return 0; +} + +static int imx8_power_domain_free(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p)\n", __func__, power_domain); + + return 0; +} + +static int imx8_power_domain_on(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8_power_domain_platdata *pdata; + struct imx8_power_domain_priv *ppriv; + sc_err_t ret; + int err; + + struct power_domain parent_domain; + struct udevice *parent = dev_get_parent(dev); + + /* Need to power on parent node first */ + if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) { + parent_domain.dev = parent; + err = imx8_power_domain_on(&parent_domain); + if (err) + return err; + } + + pdata = (struct imx8_power_domain_platdata *)dev_get_platdata(dev); + ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev); + + debug("%s(power_domain=%s) resource_id %d\n", __func__, dev->name, + pdata->resource_id); + + /* Already powered on */ + if (ppriv->state_on) + return 0; + + if (pdata->resource_id != SC_R_LAST) { + ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id, + SC_PM_PW_MODE_ON); + if (ret) { + printf("Error: %s Power up failed! (error = %d)\n", + dev->name, ret); + return -EIO; + } + } + + ppriv->state_on = true; + debug("%s is powered on\n", dev->name); + + return 0; +} + +static int imx8_power_domain_off_node(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct udevice *child; + struct imx8_power_domain_priv *ppriv; + struct imx8_power_domain_priv *child_ppriv; + struct imx8_power_domain_platdata *pdata; + sc_err_t ret; + + ppriv = dev_get_priv(dev); + pdata = dev_get_platdata(dev); + + debug("%s, %s, state_on %d\n", __func__, dev->name, ppriv->state_on); + + /* Already powered off */ + if (!ppriv->state_on) + return 0; + + /* Check if all subnodes are off */ + for (device_find_first_child(dev, &child); + child; + device_find_next_child(&child)) { + if (device_active(child)) { + child_ppriv = + (struct imx8_power_domain_priv *)dev_get_priv(child); + if (child_ppriv->state_on) + return -EPERM; + } + } + + if (pdata->resource_id != SC_R_LAST) { + if (!sc_rm_is_resource_owned(-1, pdata->resource_id)) { + printf("%s not owned by curr partition\n", dev->name); + return 0; + } + ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id, + SC_PM_PW_MODE_OFF); + if (ret) { + printf("Error: %s Power off failed! (error = %d)\n", + dev->name, ret); + return -EIO; + } + } + + ppriv->state_on = false; + debug("%s is powered off\n", dev->name); + + return 0; +} + +static int imx8_power_domain_off_parentnodes(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct udevice *parent = dev_get_parent(dev); + struct udevice *child; + struct imx8_power_domain_priv *ppriv; + struct imx8_power_domain_priv *child_ppriv; + struct imx8_power_domain_platdata *pdata; + sc_err_t ret; + struct power_domain parent_pd; + + if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) { + pdata = + (struct imx8_power_domain_platdata *)dev_get_platdata(parent); + ppriv = (struct imx8_power_domain_priv *)dev_get_priv(parent); + + debug("%s, %s, state_on %d\n", __func__, parent->name, + ppriv->state_on); + + /* Already powered off */ + if (!ppriv->state_on) + return 0; + + /* + * Check if all sibling nodes are off. If yes, + * power off parent + */ + for (device_find_first_child(parent, &child); child; + device_find_next_child(&child)) { + if (device_active(child)) { + child_ppriv = (struct imx8_power_domain_priv *) + dev_get_priv(child); + /* Find a power on sibling */ + if (child_ppriv->state_on) { + debug("sibling %s, state_on %d\n", + child->name, + child_ppriv->state_on); + return 0; + } + } + } + + /* power off parent */ + if (pdata->resource_id != SC_R_LAST) { + ret = sc_pm_set_resource_power_mode(-1, + pdata->resource_id, + SC_PM_PW_MODE_OFF); + if (ret) { + printf("%s Power off failed! (error = %d)\n", + parent->name, ret); + return -EIO; + } + } + + ppriv->state_on = false; + debug("%s is powered off\n", parent->name); + + parent_pd.dev = parent; + imx8_power_domain_off_parentnodes(&parent_pd); + } + + return 0; +} + +static int imx8_power_domain_off(struct power_domain *power_domain) +{ + int ret; + + debug("%s(power_domain=%p)\n", __func__, power_domain); + + /* Turn off the node */ + ret = imx8_power_domain_off_node(power_domain); + if (ret) { + debug("Can't power off the node of dev %s, ret = %d\n", + power_domain->dev->name, ret); + return ret; + } + + /* Turn off parent nodes, if sibling nodes are all off */ + ret = imx8_power_domain_off_parentnodes(power_domain); + if (ret) { + printf("Failed to power off parent nodes of dev %s, ret = %d\n", + power_domain->dev->name, ret); + return ret; + } + + return 0; +} + +static int imx8_power_domain_of_xlate(struct power_domain *power_domain, + struct ofnode_phandle_args *args) +{ + debug("%s(power_domain=%p)\n", __func__, power_domain); + + /* Do nothing to the xlate, since we don't have args used */ + + return 0; +} + +static int imx8_power_domain_bind(struct udevice *dev) +{ + int offset; + const char *name; + int ret = 0; + + debug("%s(dev=%p)\n", __func__, dev); + + offset = dev_of_offset(dev); + for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0; + offset = fdt_next_subnode(gd->fdt_blob, offset)) { + /* Bind the subnode to this driver */ + name = fdt_get_name(gd->fdt_blob, offset, NULL); + + ret = device_bind_with_driver_data(dev, dev->driver, name, + dev->driver_data, + offset_to_ofnode(offset), + NULL); + + if (ret == -ENODEV) + printf("Driver '%s' refuses to bind\n", + dev->driver->name); + + if (ret) + printf("Error binding driver '%s': %d\n", + dev->driver->name, ret); + } + + return 0; +} + +static int imx8_power_domain_probe(struct udevice *dev) +{ + struct imx8_power_domain_priv *ppriv; + + debug("%s(dev=%s)\n", __func__, dev->name); + + ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev); + + /* Set default to power off */ + if (ppriv) + ppriv->state_on = false; + + return 0; +} + +static int imx8_power_domain_ofdata_to_platdata(struct udevice *dev) +{ + int reg; + struct imx8_power_domain_platdata *pdata = dev_get_platdata(dev); + + reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1); + if (reg == -1) { + debug("%s: Invalid resource id %d\n", __func__, reg); + return -EINVAL; + } + pdata->resource_id = (sc_rsrc_t)reg; + + debug("%s resource_id %d\n", __func__, pdata->resource_id); + + return 0; +} + +static const struct udevice_id imx8_power_domain_ids[] = { + { .compatible = "nxp,imx8-pd" }, + { } +}; + +struct power_domain_ops imx8_power_domain_ops = { + .request = imx8_power_domain_request, + .free = imx8_power_domain_free, + .on = imx8_power_domain_on, + .off = imx8_power_domain_off, + .of_xlate = imx8_power_domain_of_xlate, +}; + +U_BOOT_DRIVER(imx8_power_domain) = { + .name = "imx8_power_domain", + .id = UCLASS_POWER_DOMAIN, + .of_match = imx8_power_domain_ids, + .bind = imx8_power_domain_bind, + .probe = imx8_power_domain_probe, + .ofdata_to_platdata = imx8_power_domain_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct imx8_power_domain_platdata), + .priv_auto_alloc_size = sizeof(struct imx8_power_domain_priv), + .ops = &imx8_power_domain_ops, +}; diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 1212b72676..6106c1f9ec 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -4,6 +4,7 @@ */ #include <common.h> +#include <clk.h> #include <dm.h> #include <fsl_lpuart.h> #include <watchdog.h> @@ -40,10 +41,20 @@ #define CTRL_TE (1 << 19) #define CTRL_RE (1 << 18) +#define FIFO_RXFLUSH BIT(14) +#define FIFO_TXFLUSH BIT(15) +#define FIFO_TXSIZE_MASK 0x70 +#define FIFO_TXSIZE_OFF 4 +#define FIFO_RXSIZE_MASK 0x7 +#define FIFO_RXSIZE_OFF 0 #define FIFO_TXFE 0x80 +#ifdef CONFIG_ARCH_IMX8 +#define FIFO_RXFE 0x08 +#else #define FIFO_RXFE 0x40 +#endif -#define WATER_TXWATER_OFF 1 +#define WATER_TXWATER_OFF 0 #define WATER_RXWATER_OFF 16 DECLARE_GLOBAL_DATA_PTR; @@ -54,7 +65,8 @@ DECLARE_GLOBAL_DATA_PTR; enum lpuart_devtype { DEV_VF610 = 1, DEV_LS1021A, - DEV_MX7ULP + DEV_MX7ULP, + DEV_IMX8 }; struct lpuart_serial_platdata { @@ -93,6 +105,32 @@ u32 __weak get_lpuart_clk(void) return CONFIG_SYS_CLK_FREQ; } +#if IS_ENABLED(CONFIG_CLK) +static int get_lpuart_clk_rate(struct udevice *dev, u32 *clk) +{ + struct clk per_clk; + ulong rate; + int ret; + + ret = clk_get_by_name(dev, "per", &per_clk); + if (ret) { + dev_err(dev, "Failed to get per clk: %d\n", ret); + return ret; + } + + rate = clk_get_rate(&per_clk); + if ((long)rate <= 0) { + dev_err(dev, "Failed to get per clk rate: %ld\n", (long)rate); + return ret; + } + *clk = rate; + return 0; +} +#else +static inline int get_lpuart_clk_rate(struct udevice *dev, u32 *clk) +{ return -ENOSYS; } +#endif + static bool is_lpuart32(struct udevice *dev) { struct lpuart_serial_platdata *plat = dev->platdata; @@ -100,12 +138,22 @@ static bool is_lpuart32(struct udevice *dev) return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG; } -static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat, +static void _lpuart_serial_setbrg(struct udevice *dev, int baudrate) { + struct lpuart_serial_platdata *plat = dev_get_platdata(dev); struct lpuart_fsl *base = plat->reg; - u32 clk = get_lpuart_clk(); + u32 clk; u16 sbr; + int ret; + + if (IS_ENABLED(CONFIG_CLK)) { + ret = get_lpuart_clk_rate(dev, &clk); + if (ret) + return; + } else { + clk = get_lpuart_clk(); + } sbr = (u16)(clk / (16 * baudrate)); @@ -151,8 +199,9 @@ static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat) * Initialise the serial port with the given baudrate. The settings * are always 8 data bits, no parity, 1 stop bit, no start bits. */ -static int _lpuart_serial_init(struct lpuart_serial_platdata *plat) +static int _lpuart_serial_init(struct udevice *dev) { + struct lpuart_serial_platdata *plat = dev_get_platdata(dev); struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg; u8 ctrl; @@ -171,19 +220,29 @@ static int _lpuart_serial_init(struct lpuart_serial_platdata *plat) __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo); /* provide data bits, parity, stop bit, etc */ - _lpuart_serial_setbrg(plat, gd->baudrate); + _lpuart_serial_setbrg(dev, gd->baudrate); __raw_writeb(UC2_RE | UC2_TE, &base->uc2); return 0; } -static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat, +static void _lpuart32_serial_setbrg_7ulp(struct udevice *dev, int baudrate) { + struct lpuart_serial_platdata *plat = dev_get_platdata(dev); struct lpuart_fsl_reg32 *base = plat->reg; u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp; - u32 clk = get_lpuart_clk(); + u32 clk; + int ret; + + if (IS_ENABLED(CONFIG_CLK)) { + ret = get_lpuart_clk_rate(dev, &clk); + if (ret) + return; + } else { + clk = get_lpuart_clk(); + } baud_diff = baudrate; osr = 0; @@ -237,12 +296,22 @@ static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat, out_le32(&base->baud, tmp); } -static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat, +static void _lpuart32_serial_setbrg(struct udevice *dev, int baudrate) { + struct lpuart_serial_platdata *plat = dev_get_platdata(dev); struct lpuart_fsl_reg32 *base = plat->reg; - u32 clk = get_lpuart_clk(); + u32 clk; u32 sbr; + int ret; + + if (IS_ENABLED(CONFIG_CLK)) { + ret = get_lpuart_clk_rate(dev, &clk); + if (ret) + return; + } else { + clk = get_lpuart_clk(); + } sbr = (clk / (16 * baudrate)); @@ -310,26 +379,40 @@ static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat) * Initialise the serial port with the given baudrate. The settings * are always 8 data bits, no parity, 1 stop bit, no start bits. */ -static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat) +static int _lpuart32_serial_init(struct udevice *dev) { + struct lpuart_serial_platdata *plat = dev_get_platdata(dev); struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg; - u32 ctrl; + u32 val, tx_fifo_size; - lpuart_read32(plat->flags, &base->ctrl, &ctrl); - ctrl &= ~CTRL_RE; - ctrl &= ~CTRL_TE; - lpuart_write32(plat->flags, &base->ctrl, ctrl); + lpuart_read32(plat->flags, &base->ctrl, &val); + val &= ~CTRL_RE; + val &= ~CTRL_TE; + lpuart_write32(plat->flags, &base->ctrl, val); lpuart_write32(plat->flags, &base->modir, 0); - lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE)); + + lpuart_read32(plat->flags, &base->fifo, &val); + tx_fifo_size = (val & FIFO_TXSIZE_MASK) >> FIFO_TXSIZE_OFF; + /* Set the TX water to half of FIFO size */ + if (tx_fifo_size > 1) + tx_fifo_size = tx_fifo_size >> 1; + + /* Set RX water to 0, to be triggered by any receive data */ + lpuart_write32(plat->flags, &base->water, + (tx_fifo_size << WATER_TXWATER_OFF)); + + /* Enable TX and RX FIFO */ + val |= (FIFO_TXFE | FIFO_RXFE | FIFO_TXFLUSH | FIFO_RXFLUSH); + lpuart_write32(plat->flags, &base->fifo, val); lpuart_write32(plat->flags, &base->match, 0); - if (plat->devtype == DEV_MX7ULP) { - _lpuart32_serial_setbrg_7ulp(plat, gd->baudrate); + if (plat->devtype == DEV_MX7ULP || plat->devtype == DEV_IMX8) { + _lpuart32_serial_setbrg_7ulp(dev, gd->baudrate); } else { /* provide data bits, parity, stop bit, etc */ - _lpuart32_serial_setbrg(plat, gd->baudrate); + _lpuart32_serial_setbrg(dev, gd->baudrate); } lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE); @@ -339,15 +422,15 @@ static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat) static int lpuart_serial_setbrg(struct udevice *dev, int baudrate) { - struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_serial_platdata *plat = dev_get_platdata(dev); if (is_lpuart32(dev)) { - if (plat->devtype == DEV_MX7ULP) - _lpuart32_serial_setbrg_7ulp(plat, baudrate); + if (plat->devtype == DEV_MX7ULP || plat->devtype == DEV_IMX8) + _lpuart32_serial_setbrg_7ulp(dev, baudrate); else - _lpuart32_serial_setbrg(plat, baudrate); + _lpuart32_serial_setbrg(dev, baudrate); } else { - _lpuart_serial_setbrg(plat, baudrate); + _lpuart_serial_setbrg(dev, baudrate); } return 0; @@ -399,12 +482,10 @@ static int lpuart_serial_pending(struct udevice *dev, bool input) static int lpuart_serial_probe(struct udevice *dev) { - struct lpuart_serial_platdata *plat = dev->platdata; - if (is_lpuart32(dev)) - return _lpuart32_serial_init(plat); + return _lpuart32_serial_init(dev); else - return _lpuart_serial_init(plat); + return _lpuart_serial_init(dev); } static int lpuart_serial_ofdata_to_platdata(struct udevice *dev) @@ -427,6 +508,8 @@ static int lpuart_serial_ofdata_to_platdata(struct udevice *dev) plat->devtype = DEV_MX7ULP; else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart")) plat->devtype = DEV_VF610; + else if (!fdt_node_check_compatible(blob, node, "fsl,imx8qm-lpuart")) + plat->devtype = DEV_IMX8; return 0; } @@ -444,6 +527,8 @@ static const struct udevice_id lpuart_serial_ids[] = { { .compatible = "fsl,imx7ulp-lpuart", .data = LPUART_FLAG_REGMAP_32BIT_REG }, { .compatible = "fsl,vf610-lpuart"}, + { .compatible = "fsl,imx8qm-lpuart", + .data = LPUART_FLAG_REGMAP_32BIT_REG }, { } }; diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c index 4f1f822c58..e586c18cf0 100644 --- a/drivers/serial/serial_mxc.c +++ b/drivers/serial/serial_mxc.c @@ -339,6 +339,7 @@ static int mxc_serial_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id mxc_serial_ids[] = { { .compatible = "fsl,imx6ul-uart" }, { .compatible = "fsl,imx7d-uart" }, + { .compatible = "fsl,imx6q-uart" }, { } }; #endif diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d545b3e000..02f4e1e32f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -111,4 +111,11 @@ config XILINX_TB_WATCHDOG Select this to enable Xilinx Axi watchdog timer, which can be found on some Xilinx Microblaze Platforms. +config IMX_WATCHDOG + bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP" + select HW_WATCHDOG + help + Select this to enable the IMX and LSCH2 of Layerscape watchdog + driver. + endmenu |