diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/imx_rgpio2p.c | 224 | ||||
-rw-r--r-- | drivers/i2c/Kconfig | 6 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/imx_lpi2c.c | 462 | ||||
-rw-r--r-- | drivers/misc/mxc_ocotp.c | 52 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 8 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/Kconfig | 14 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx.c | 10 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx.h | 3 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx7ulp.c | 41 | ||||
-rw-r--r-- | drivers/serial/serial_lpuart.c | 336 | ||||
-rw-r--r-- | drivers/serial/serial_mxc.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 8 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/ulp_wdog.c | 98 |
18 files changed, 1157 insertions, 117 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8d9ab5237e..dc4108f378 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -48,6 +48,13 @@ config INTEL_BROADWELL_GPIO driver from the common Intel ICH6 driver. It supports a total of 95 GPIOs which can be configured from the device tree. +config IMX_RGPIO2P + bool "i.MX7ULP RGPIO2P driver" + depends on DM + default n + help + This driver supports i.MX7ULP Rapid GPIO2P controller. + config LPC32XX_GPIO bool "LPC32XX GPIO driver" depends on DM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 89392264a4..27f8068e3e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o obj-$(CONFIG_ZYNQ_GPIO) += zynq_gpio.o obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o +obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o obj-$(CONFIG_MSM_GPIO) += msm_gpio.o diff --git a/drivers/gpio/imx_rgpio2p.c b/drivers/gpio/imx_rgpio2p.c new file mode 100644 index 0000000000..886b16188e --- /dev/null +++ b/drivers/gpio/imx_rgpio2p.c @@ -0,0 +1,224 @@ +/* + * Copyright 2016 Freescale Semiconductor, Inc. + * + * RGPIO2P driver for the Freescale i.MX7ULP. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <malloc.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum imx_rgpio2p_direction { + IMX_RGPIO2P_DIRECTION_IN, + IMX_RGPIO2P_DIRECTION_OUT, +}; + +#define GPIO_PER_BANK 32 + +struct imx_rgpio2p_data { + struct gpio_regs *regs; +}; + +struct imx_rgpio2p_plat { + int bank_index; + struct gpio_regs *regs; +}; + +static int imx_rgpio2p_is_output(struct gpio_regs *regs, int offset) +{ + u32 val; + + val = readl(®s->gpio_pddr); + + return val & (1 << offset) ? 1 : 0; +} + +static void imx_rgpio2p_bank_direction(struct gpio_regs *regs, int offset, + enum imx_rgpio2p_direction direction) +{ + u32 l; + + l = readl(®s->gpio_pddr); + + switch (direction) { + case IMX_RGPIO2P_DIRECTION_OUT: + l |= 1 << offset; + break; + case IMX_RGPIO2P_DIRECTION_IN: + l &= ~(1 << offset); + } + writel(l, ®s->gpio_pddr); +} + +static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset, + int value) +{ + if (value) + writel((1 << offset), ®s->gpio_psor); + else + writel((1 << offset), ®s->gpio_pcor); +} + +static int imx_rgpio2p_bank_get_value(struct gpio_regs *regs, int offset) +{ + return (readl(®s->gpio_pdir) >> offset) & 0x01; +} + +static int imx_rgpio2p_direction_input(struct udevice *dev, unsigned offset) +{ + struct imx_rgpio2p_data *bank = dev_get_priv(dev); + + /* Configure GPIO direction as input. */ + imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_IN); + + return 0; +} + +static int imx_rgpio2p_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct imx_rgpio2p_data *bank = dev_get_priv(dev); + + /* Configure GPIO output value. */ + imx_rgpio2p_bank_set_value(bank->regs, offset, value); + + /* Configure GPIO direction as output. */ + imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_OUT); + + return 0; +} + +static int imx_rgpio2p_get_value(struct udevice *dev, unsigned offset) +{ + struct imx_rgpio2p_data *bank = dev_get_priv(dev); + + return imx_rgpio2p_bank_get_value(bank->regs, offset); +} + +static int imx_rgpio2p_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct imx_rgpio2p_data *bank = dev_get_priv(dev); + + imx_rgpio2p_bank_set_value(bank->regs, offset, value); + + return 0; +} + +static int imx_rgpio2p_get_function(struct udevice *dev, unsigned offset) +{ + struct imx_rgpio2p_data *bank = dev_get_priv(dev); + + /* GPIOF_FUNC is not implemented yet */ + if (imx_rgpio2p_is_output(bank->regs, offset)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static const struct dm_gpio_ops imx_rgpio2p_ops = { + .direction_input = imx_rgpio2p_direction_input, + .direction_output = imx_rgpio2p_direction_output, + .get_value = imx_rgpio2p_get_value, + .set_value = imx_rgpio2p_set_value, + .get_function = imx_rgpio2p_get_function, +}; + +static int imx_rgpio2p_probe(struct udevice *dev) +{ + struct imx_rgpio2p_data *bank = dev_get_priv(dev); + struct imx_rgpio2p_plat *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + int banknum; + char name[18], *str; + + banknum = plat->bank_index; + sprintf(name, "GPIO%d_", banknum + 1); + str = strdup(name); + if (!str) + return -ENOMEM; + uc_priv->bank_name = str; + uc_priv->gpio_count = GPIO_PER_BANK; + bank->regs = plat->regs; + + return 0; +} + +static int imx_rgpio2p_bind(struct udevice *dev) +{ + struct imx_rgpio2p_plat *plat = dev->platdata; + fdt_addr_t addr; + + /* + * If platdata already exsits, directly return. + * Actually only when DT is not supported, platdata + * is statically initialized in U_BOOT_DEVICES.Here + * will return. + */ + if (plat) + return 0; + + addr = dev_get_addr_index(dev, 1); + if (addr == FDT_ADDR_T_NONE) + return -ENODEV; + + /* + * TODO: + * When every board is converted to driver model and DT is supported, + * this can be done by auto-alloc feature, but not using calloc + * to alloc memory for platdata. + */ + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + + plat->regs = (struct gpio_regs *)addr; + plat->bank_index = dev->req_seq; + dev->platdata = plat; + + return 0; +} + + +static const struct udevice_id imx_rgpio2p_ids[] = { + { .compatible = "fsl,imx7ulp-gpio" }, + { } +}; + +U_BOOT_DRIVER(imx_rgpio2p) = { + .name = "imx_rgpio2p", + .id = UCLASS_GPIO, + .ops = &imx_rgpio2p_ops, + .probe = imx_rgpio2p_probe, + .priv_auto_alloc_size = sizeof(struct imx_rgpio2p_plat), + .of_match = imx_rgpio2p_ids, + .bind = imx_rgpio2p_bind, +}; + +#if !CONFIG_IS_ENABLED(OF_CONTROL) +static const struct imx_rgpio2p_plat imx_plat[] = { + { 0, (struct gpio_regs *)RGPIO2P_GPIO1_BASE_ADDR }, + { 1, (struct gpio_regs *)RGPIO2P_GPIO2_BASE_ADDR }, + { 2, (struct gpio_regs *)RGPIO2P_GPIO3_BASE_ADDR }, + { 3, (struct gpio_regs *)RGPIO2P_GPIO4_BASE_ADDR }, + { 4, (struct gpio_regs *)RGPIO2P_GPIO5_BASE_ADDR }, + { 5, (struct gpio_regs *)RGPIO2P_GPIO6_BASE_ADDR }, +}; + +U_BOOT_DEVICES(imx_rgpio2ps) = { + { "imx_rgpio2p", &imx_plat[0] }, + { "imx_rgpio2p", &imx_plat[1] }, + { "imx_rgpio2p", &imx_plat[2] }, + { "imx_rgpio2p", &imx_plat[3] }, + { "imx_rgpio2p", &imx_plat[4] }, + { "imx_rgpio2p", &imx_plat[5] }, +}; +#endif diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 39f62daf5d..8ac7aaf2d2 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -109,6 +109,12 @@ config SYS_I2C_INTEL the I2C API meaning that any I2C operations will immediately fail for now. +config SYS_I2C_IMX_LPI2C + bool "NXP i.MX LPI2C driver" + depends on ARCH_MX7ULP + help + Add support for the NXP i.MX LPI2C driver. + config SYS_I2C_MXC bool "NXP i.MX I2C driver" depends on MX6 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 7c86198863..446af225bc 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o obj-$(CONFIG_SYS_I2C_IHS) += ihs_i2c.o obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o +obj-$(CONFIG_SYS_I2C_IMX_LPI2C) += imx_lpi2c.o obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c new file mode 100644 index 0000000000..f792d4432d --- /dev/null +++ b/drivers/i2c/imx_lpi2c.c @@ -0,0 +1,462 @@ +/* + * Copyright 2016 Freescale Semiconductors, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/imx_lpi2c.h> +#include <asm/arch/sys_proto.h> +#include <dm.h> +#include <fdtdec.h> +#include <i2c.h> + +DECLARE_GLOBAL_DATA_PTR; +#define LPI2C_FIFO_SIZE 4 +#define LPI2C_TIMEOUT_MS 100 + +/* Weak linked function for overridden by some SoC power function */ +int __weak init_i2c_power(unsigned i2c_num) +{ + return 0; +} + +static int imx_lpci2c_check_busy_bus(struct udevice *bus) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + u32 status; + + status = readl(®s->msr); + + if ((status & LPI2C_MSR_BBF_MASK) && !(status & LPI2C_MSR_MBF_MASK)) + result = LPI2C_BUSY; + + return result; +} + +static int imx_lpci2c_check_clear_error(struct udevice *bus) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + u32 val, status; + + status = readl(®s->msr); + /* errors to check for */ + status &= LPI2C_MSR_NDF_MASK | LPI2C_MSR_ALF_MASK | + LPI2C_MSR_FEF_MASK | LPI2C_MSR_PLTF_MASK; + + if (status) { + if (status & LPI2C_MSR_PLTF_MASK) + result = LPI2C_PIN_LOW_TIMEOUT_ERR; + else if (status & LPI2C_MSR_ALF_MASK) + result = LPI2C_ARB_LOST_ERR; + else if (status & LPI2C_MSR_NDF_MASK) + result = LPI2C_NAK_ERR; + else if (status & LPI2C_MSR_FEF_MASK) + result = LPI2C_FIFO_ERR; + + /* clear status flags */ + writel(0x7f00, ®s->msr); + /* reset fifos */ + val = readl(®s->mcr); + val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; + writel(val, ®s->mcr); + } + + return result; +} + +static int bus_i2c_wait_for_tx_ready(struct udevice *bus) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + u32 txcount = 0; + ulong start_time = get_timer(0); + + do { + txcount = LPI2C_MFSR_TXCOUNT(readl(®s->mfsr)); + txcount = LPI2C_FIFO_SIZE - txcount; + result = imx_lpci2c_check_clear_error(bus); + if (result) { + debug("i2c: wait for tx ready: result 0x%x\n", result); + return result; + } + if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { + debug("i2c: wait for tx ready: timeout\n"); + return -1; + } + } while (!txcount); + + return result; +} + +static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + + /* empty tx */ + if (!len) + return result; + + while (len--) { + result = bus_i2c_wait_for_tx_ready(bus); + if (result) { + debug("i2c: send wait fot tx ready: %d\n", result); + return result; + } + writel(*txbuf++, ®s->mtdr); + } + + return result; +} + +static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + u32 val; + ulong start_time = get_timer(0); + + /* empty read */ + if (!len) + return result; + + result = bus_i2c_wait_for_tx_ready(bus); + if (result) { + debug("i2c: receive wait fot tx ready: %d\n", result); + return result; + } + + /* clear all status flags */ + writel(0x7f00, ®s->msr); + /* send receive command */ + val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1); + writel(val, ®s->mtdr); + + while (len--) { + do { + result = imx_lpci2c_check_clear_error(bus); + if (result) { + debug("i2c: receive check clear error: %d\n", result); + return result; + } + if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { + debug("i2c: receive mrdr: timeout\n"); + return -1; + } + val = readl(®s->mrdr); + } while (val & LPI2C_MRDR_RXEMPTY_MASK); + *rxbuf++ = LPI2C_MRDR_DATA(val); + } + + return result; +} + +static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + u32 val; + + result = imx_lpci2c_check_busy_bus(bus); + if (result) { + debug("i2c: start check busy bus: 0x%x\n", result); + return result; + } + /* clear all status flags */ + writel(0x7f00, ®s->msr); + /* turn off auto-stop condition */ + val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK; + writel(val, ®s->mcfgr1); + /* wait tx fifo ready */ + result = bus_i2c_wait_for_tx_ready(bus); + if (result) { + debug("i2c: start wait for tx ready: 0x%x\n", result); + return result; + } + /* issue start command */ + val = LPI2C_MTDR_CMD(0x4) | (addr << 0x1) | dir; + writel(val, ®s->mtdr); + + return result; +} +static int bus_i2c_stop(struct udevice *bus) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + lpi2c_status_t result = LPI2C_SUCESS; + u32 status; + + result = bus_i2c_wait_for_tx_ready(bus); + if (result) { + debug("i2c: stop wait for tx ready: 0x%x\n", result); + return result; + } + + /* send stop command */ + writel(LPI2C_MTDR_CMD(0x2), ®s->mtdr); + + while (result == LPI2C_SUCESS) { + status = readl(®s->msr); + result = imx_lpci2c_check_clear_error(bus); + /* stop detect flag */ + if (status & LPI2C_MSR_SDF_MASK) { + /* clear stop flag */ + status &= LPI2C_MSR_SDF_MASK; + writel(status, ®s->msr); + break; + } + } + + return result; +} + +static int bus_i2c_read(struct udevice *bus, u32 chip, u8 *buf, int len) +{ + lpi2c_status_t result = LPI2C_SUCESS; + + result = bus_i2c_start(bus, chip, 1); + if (result) + return result; + result = bus_i2c_receive(bus, buf, len); + if (result) + return result; + result = bus_i2c_stop(bus); + if (result) + return result; + + return result; +} + +static int bus_i2c_write(struct udevice *bus, u32 chip, u8 *buf, int len) +{ + lpi2c_status_t result = LPI2C_SUCESS; + + result = bus_i2c_start(bus, chip, 0); + if (result) + return result; + result = bus_i2c_send(bus, buf, len); + if (result) + return result; + result = bus_i2c_stop(bus); + if (result) + return result; + + return result; +} + + +static int bus_i2c_set_bus_speed(struct udevice *bus, int speed) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + u32 val; + u32 preescale = 0, best_pre = 0, clkhi = 0; + u32 best_clkhi = 0, abs_error = 0, rate; + u32 error = 0xffffffff; + u32 clock_rate; + bool mode; + int i; + + clock_rate = imx_get_i2cclk(bus->seq + 4); + if (!clock_rate) + return -EPERM; + + mode = (readl(®s->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT; + /* disable master mode */ + val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; + writel(val | LPI2C_MCR_MEN(0), ®s->mcr); + + for (preescale = 1; (preescale <= 128) && + (error != 0); preescale = 2 * preescale) { + for (clkhi = 1; clkhi < 32; clkhi++) { + if (clkhi == 1) + rate = (clock_rate / preescale) / (1 + 3 + 2 + 2 / preescale); + else + rate = (clock_rate / preescale / (3 * clkhi + 2 + 2 / preescale)); + + abs_error = speed > rate ? speed - rate : rate - speed; + + if (abs_error < error) { + best_pre = preescale; + best_clkhi = clkhi; + error = abs_error; + if (abs_error == 0) + break; + } + } + } + + /* Standard, fast, fast mode plus and ultra-fast transfers. */ + val = LPI2C_MCCR0_CLKHI(best_clkhi); + if (best_clkhi < 2) + val |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | LPI2C_MCCR0_DATAVD(1); + else + val |= LPI2C_MCCR0_CLKLO(2 * best_clkhi) | LPI2C_MCCR0_SETHOLD(best_clkhi) | + LPI2C_MCCR0_DATAVD(best_clkhi / 2); + writel(val, ®s->mccr0); + + for (i = 0; i < 8; i++) { + if (best_pre == (1 << i)) { + best_pre = i; + break; + } + } + + val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK; + writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), ®s->mcfgr1); + + if (mode) { + val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; + writel(val | LPI2C_MCR_MEN(1), ®s->mcr); + } + + return 0; +} + +static int bus_i2c_init(struct udevice *bus, int speed) +{ + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)dev_get_addr(bus); + u32 val; + int ret; + + /* reset peripheral */ + writel(LPI2C_MCR_RST_MASK, ®s->mcr); + writel(0x0, ®s->mcr); + /* Disable Dozen mode */ + writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), ®s->mcr); + /* host request disable, active high, external pin */ + val = readl(®s->mcfgr0); + val &= (~(LPI2C_MCFGR0_HREN_MASK | LPI2C_MCFGR0_HRPOL_MASK | + LPI2C_MCFGR0_HRSEL_MASK)); + val |= LPI2C_MCFGR0_HRPOL(0x1); + writel(val, ®s->mcfgr0); + /* pincfg and ignore ack */ + val = readl(®s->mcfgr1); + val &= ~(LPI2C_MCFGR1_PINCFG_MASK | LPI2C_MCFGR1_IGNACK_MASK); + val |= LPI2C_MCFGR1_PINCFG(0x0); /* 2 pin open drain */ + val |= LPI2C_MCFGR1_IGNACK(0x0); /* ignore nack */ + writel(val, ®s->mcfgr1); + + ret = bus_i2c_set_bus_speed(bus, speed); + + /* enable lpi2c in master mode */ + val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK; + writel(val | LPI2C_MCR_MEN(1), ®s->mcr); + + debug("i2c : controller bus %d, speed %d:\n", bus->seq, speed); + + return ret; +} + +static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, + u32 chip_flags) +{ + lpi2c_status_t result = LPI2C_SUCESS; + + result = bus_i2c_start(bus, chip, 0); + if (result) { + bus_i2c_stop(bus); + bus_i2c_init(bus, 100000); + return result; + } + + result = bus_i2c_stop(bus); + if (result) { + bus_i2c_init(bus, 100000); + return -result; + } + + return result; +} + +static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{ + int ret = 0; + + for (; nmsgs > 0; nmsgs--, msg++) { + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) + ret = bus_i2c_read(bus, msg->addr, msg->buf, + msg->len); + else { + ret = bus_i2c_write(bus, msg->addr, msg->buf, + msg->len); + if (ret) + break; + } + } + + if (ret) + debug("i2c_write: error sending\n"); + + return ret; +} + +static int imx_lpi2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + return bus_i2c_set_bus_speed(bus, speed); +} + +static int imx_lpi2c_probe(struct udevice *bus) +{ + struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus); + fdt_addr_t addr; + int ret; + + i2c_bus->driver_data = dev_get_driver_data(bus); + + addr = dev_get_addr(bus); + if (addr == FDT_ADDR_T_NONE) + return -ENODEV; + + i2c_bus->base = addr; + i2c_bus->index = bus->seq; + i2c_bus->bus = bus; + + /* power up i2c resource */ + ret = init_i2c_power(bus->seq + 4); + if (ret) { + debug("init_i2c_power err = %d\n", ret); + return ret; + } + + /* Enable clk, only i2c4-7 can be handled by A7 core */ + ret = enable_i2c_clk(1, bus->seq + 4); + if (ret < 0) + return ret; + + ret = bus_i2c_init(bus, 100000); + if (ret < 0) + return ret; + + debug("i2c : controller bus %d at %lu , speed %d: ", + bus->seq, i2c_bus->base, + i2c_bus->speed); + + return 0; +} + +static const struct dm_i2c_ops imx_lpi2c_ops = { + .xfer = imx_lpi2c_xfer, + .probe_chip = imx_lpi2c_probe_chip, + .set_bus_speed = imx_lpi2c_set_bus_speed, +}; + +static const struct udevice_id imx_lpi2c_ids[] = { + { .compatible = "fsl,imx7ulp-lpi2c", }, + {} +}; + +U_BOOT_DRIVER(imx_lpi2c) = { + .name = "imx_lpi2c", + .id = UCLASS_I2C, + .of_match = imx_lpi2c_ids, + .probe = imx_lpi2c_probe, + .priv_auto_alloc_size = sizeof(struct imx_lpi2c_bus), + .ops = &imx_lpi2c_ops, +}; diff --git a/drivers/misc/mxc_ocotp.c b/drivers/misc/mxc_ocotp.c index 0b1c05078f..88610d6af2 100644 --- a/drivers/misc/mxc_ocotp.c +++ b/drivers/misc/mxc_ocotp.c @@ -29,6 +29,12 @@ #ifdef CONFIG_MX7 #define BM_CTRL_ADDR 0x0000000f #define BM_CTRL_RELOAD 0x00000400 +#elif defined(CONFIG_MX7ULP) +#define BM_CTRL_ADDR 0x000000FF +#define BM_CTRL_RELOAD 0x00000400 +#define BM_OUT_STATUS_DED 0x00000400 +#define BM_OUT_STATUS_LOCKED 0x00000800 +#define BM_OUT_STATUS_PROGFAIL 0x00001000 #else #define BM_CTRL_ADDR 0x0000007f #endif @@ -70,6 +76,9 @@ #elif defined CONFIG_MX7 #define FUSE_BANK_SIZE 0x40 #define FUSE_BANKS 16 +#elif defined(CONFIG_MX7ULP) +#define FUSE_BANK_SIZE 0x80 +#define FUSE_BANKS 31 #else #error "Unsupported architecture\n" #endif @@ -98,7 +107,7 @@ u32 fuse_bank_physical(int index) { u32 phy_index; - if (is_mx6sl()) { + if (is_mx6sl() || is_mx7ulp()) { phy_index = index; } else if (is_mx6ul() || is_mx6ull() || is_mx6sll()) { if ((is_mx6ull() || is_mx6sll()) && index == 8) @@ -187,6 +196,10 @@ static int finish_access(struct ocotp_regs *regs, const char *caller) err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); clear_error(regs); +#ifdef CONFIG_MX7ULP + /* Need to power down the OTP memory */ + writel(1, ®s->pdn); +#endif if (err) { printf("mxc_ocotp %s(): Access protect error\n", caller); return -EIO; @@ -217,6 +230,13 @@ int fuse_read(u32 bank, u32 word, u32 *val) *val = readl(®s->bank[phy_bank].fuse_regs[phy_word << 2]); +#ifdef CONFIG_MX7ULP + if (readl(®s->out_status) & BM_OUT_STATUS_DED) { + writel(BM_OUT_STATUS_DED, ®s->out_status_clr); + printf("mxc_ocotp %s(): fuse read wrong\n", __func__); + return -EIO; + } +#endif return finish_access(regs, __func__); } @@ -238,6 +258,12 @@ static void set_timing(struct ocotp_regs *regs) clrsetbits_le32(®s->timing, BM_TIMING_FSOURCE | BM_TIMING_PROG, timing); } +#elif defined(CONFIG_MX7ULP) +static void set_timing(struct ocotp_regs *regs) +{ + /* No timing set for MX7ULP */ +} + #else static void set_timing(struct ocotp_regs *regs) { @@ -302,6 +328,14 @@ int fuse_sense(u32 bank, u32 word, u32 *val) *val = readl(®s->read_fuse_data); #endif +#ifdef CONFIG_MX7ULP + if (readl(®s->out_status) & BM_OUT_STATUS_DED) { + writel(BM_OUT_STATUS_DED, ®s->out_status_clr); + printf("mxc_ocotp %s(): fuse read wrong\n", __func__); + return -EIO; + } +#endif + return finish_access(regs, __func__); } @@ -355,6 +389,14 @@ int fuse_prog(u32 bank, u32 word, u32 val) #endif udelay(WRITE_POSTAMBLE_US); +#ifdef CONFIG_MX7ULP + if (readl(®s->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) { + writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), ®s->out_status_clr); + printf("mxc_ocotp %s(): fuse write is failed\n", __func__); + return -EIO; + } +#endif + return finish_access(regs, __func__); } @@ -374,5 +416,13 @@ int fuse_override(u32 bank, u32 word, u32 val) writel(val, ®s->bank[phy_bank].fuse_regs[phy_word << 2]); +#ifdef CONFIG_MX7ULP + if (readl(®s->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) { + writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), ®s->out_status_clr); + printf("mxc_ocotp %s(): fuse write is failed\n", __func__); + return -EIO; + } +#endif + return finish_access(regs, __func__); } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index adeb5df6da..f3c63585a8 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -949,6 +949,10 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) #ifdef CONFIG_DM_MMC #include <asm/arch/clock.h> +__weak void init_clk_usdhc(u32 index) +{ +} + static int fsl_esdhc_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); @@ -1011,6 +1015,9 @@ static int fsl_esdhc_probe(struct udevice *dev) * correctly get the seq as 2 and 3, then let mxc_get_clock * work as expected. */ + + 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); @@ -1035,6 +1042,7 @@ static const struct udevice_id fsl_esdhc_ids[] = { { .compatible = "fsl,imx6sl-usdhc", }, { .compatible = "fsl,imx6q-usdhc", }, { .compatible = "fsl,imx7d-usdhc", }, + { .compatible = "fsl,imx7ulp-usdhc", }, { .compatible = "fsl,esdhc", }, { /* sentinel */ } }; diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index 238f77d03e..b668359a0b 100644 --- a/drivers/pinctrl/nxp/Kconfig +++ b/drivers/pinctrl/nxp/Kconfig @@ -42,3 +42,17 @@ config PINCTRL_IMX7 configuration. This driver is different from the linux one, this is a simple implementation, only parses the 'fsl,pins' property and configure related registers. + +config PINCTRL_IMX7ULP + bool "IMX7ULP pinctrl driver" + depends on ARCH_MX7ULP && PINCTRL_FULL + select DEVRES + select PINCTRL_IMX + help + Say Y here to enable the imx7ulp pinctrl driver + + This provides a simple pinctrl driver for i.MX7ULP 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 configure related + registers. diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile index e0f7325102..c763948376 100644 --- a/drivers/pinctrl/nxp/Makefile +++ b/drivers/pinctrl/nxp/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o 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 diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c index e130faf876..f0321c4057 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.c +++ b/drivers/pinctrl/nxp/pinctrl-imx.c @@ -24,6 +24,7 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) u32 *pin_data; int npins, size, pin_size; int mux_reg, conf_reg, input_reg, input_val, mux_mode, config_val; + u32 mux_shift = info->mux_mask ? ffs(info->mux_mask) - 1 : 0; int i, j = 0; dev_dbg(dev, "%s: %s\n", __func__, config->name); @@ -97,8 +98,8 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) /* Set Mux */ if (info->flags & SHARE_MUX_CONF_REG) { - clrsetbits_le32(info->base + mux_reg, 0x7 << 20, - mux_mode << 20); + clrsetbits_le32(info->base + mux_reg, info->mux_mask, + mux_mode << mux_shift); } else { writel(mux_mode, info->base + mux_reg); } @@ -154,8 +155,8 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) /* Set config */ if (!(config_val & IMX_NO_PAD_CTL)) { if (info->flags & SHARE_MUX_CONF_REG) { - clrsetbits_le32(info->base + conf_reg, 0xffff, - config_val); + clrsetbits_le32(info->base + conf_reg, + info->mux_mask, config_val); } else { writel(config_val, info->base + conf_reg); } @@ -200,6 +201,7 @@ int imx_pinctrl_probe(struct udevice *dev, return -ENOMEM; priv->info = info; + info->mux_mask = fdtdec_get_int(gd->fdt_blob, node, "fsl,mux_mask", 0); /* * Refer to linux documentation for details: * Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt diff --git a/drivers/pinctrl/nxp/pinctrl-imx.h b/drivers/pinctrl/nxp/pinctrl-imx.h index 037c491580..a26ba854c1 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.h +++ b/drivers/pinctrl/nxp/pinctrl-imx.h @@ -11,11 +11,13 @@ * @base: the address to the controller in virtual memory * @input_sel_base: the address of the select input in virtual memory. * @flags: flags specific for each soc + * @mux_mask: Used when SHARE_MUX_CONF_REG flag is added */ struct imx_pinctrl_soc_info { void __iomem *base; void __iomem *input_sel_base; unsigned int flags; + unsigned int mux_mask; }; /** @@ -41,6 +43,7 @@ extern const struct pinctrl_ops imx_pinctrl_ops; #define SHARE_MUX_CONF_REG 0x1 #define ZERO_OFFSET_VALID 0x2 +#define CONFIG_IBE_OBE 0x4 #define IOMUXC_CONFIG_SION (0x1 << 4) diff --git a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c new file mode 100644 index 0000000000..5f011757d8 --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * + * Peng Fan <peng.fan@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm/device.h> +#include <dm/pinctrl.h> + +#include "pinctrl-imx.h" + +static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info = { + .flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CONFIG_IBE_OBE, +}; + +static int imx7ulp_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 imx7ulp_pinctrl_match[] = { + { .compatible = "fsl,imx7ulp-iomuxc-0", .data = (ulong)&imx7ulp_pinctrl_soc_info }, + { .compatible = "fsl,imx7ulp-iomuxc-1", .data = (ulong)&imx7ulp_pinctrl_soc_info }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(imx7ulp_pinctrl) = { + .name = "imx7ulp-pinctrl", + .id = UCLASS_PINCTRL, + .of_match = of_match_ptr(imx7ulp_pinctrl_match), + .probe = imx7ulp_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/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index beb4243124..95e002ea4b 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -6,6 +6,7 @@ #include <common.h> #include <dm.h> +#include <fsl_lpuart.h> #include <watchdog.h> #include <asm/io.h> #include <serial.h> @@ -48,14 +49,63 @@ DECLARE_GLOBAL_DATA_PTR; +#define LPUART_FLAG_REGMAP_32BIT_REG BIT(0) +#define LPUART_FLAG_REGMAP_ENDIAN_BIG BIT(1) + +enum lpuart_devtype { + DEV_VF610 = 1, + DEV_LS1021A, + DEV_MX7ULP +}; + struct lpuart_serial_platdata { - struct lpuart_fsl *reg; + void *reg; + enum lpuart_devtype devtype; + ulong flags; }; -#ifndef CONFIG_LPUART_32B_REG -static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate) +static void lpuart_read32(u32 flags, u32 *addr, u32 *val) +{ + if (flags & LPUART_FLAG_REGMAP_32BIT_REG) { + if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG) + *(u32 *)val = in_be32(addr); + else + *(u32 *)val = in_le32(addr); + } +} + +static void lpuart_write32(u32 flags, u32 *addr, u32 val) +{ + if (flags & LPUART_FLAG_REGMAP_32BIT_REG) { + if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG) + out_be32(addr, val); + else + out_le32(addr, val); + } +} + + +#ifndef CONFIG_SYS_CLK_FREQ +#define CONFIG_SYS_CLK_FREQ 0 +#endif + +u32 __weak get_lpuart_clk(void) { - u32 clk = mxc_get_clock(MXC_UART_CLK); + return CONFIG_SYS_CLK_FREQ; +} + +static bool is_lpuart32(struct udevice *dev) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + + return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG; +} + +static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat, + int baudrate) +{ + struct lpuart_fsl *base = plat->reg; + u32 clk = get_lpuart_clk(); u16 sbr; sbr = (u16)(clk / (16 * baudrate)); @@ -65,8 +115,9 @@ static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate) __raw_writeb(sbr & 0xff, &base->ubdl); } -static int _lpuart_serial_getc(struct lpuart_fsl *base) +static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat) { + struct lpuart_fsl *base = plat->reg; while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR))) WATCHDOG_RESET(); @@ -75,8 +126,11 @@ static int _lpuart_serial_getc(struct lpuart_fsl *base) return __raw_readb(&base->ud); } -static void _lpuart_serial_putc(struct lpuart_fsl *base, const char c) +static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat, + const char c) { + struct lpuart_fsl *base = plat->reg; + while (!(__raw_readb(&base->us1) & US1_TDRE)) WATCHDOG_RESET(); @@ -84,8 +138,10 @@ static void _lpuart_serial_putc(struct lpuart_fsl *base, const char c) } /* Test whether a character is in the RX buffer */ -static int _lpuart_serial_tstc(struct lpuart_fsl *base) +static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat) { + struct lpuart_fsl *base = plat->reg; + if (__raw_readb(&base->urcfifo) == 0) return 0; @@ -96,8 +152,9 @@ static int _lpuart_serial_tstc(struct lpuart_fsl *base) * 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_fsl *base) +static int _lpuart_serial_init(struct lpuart_serial_platdata *plat) { + struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg; u8 ctrl; ctrl = __raw_readb(&base->uc2); @@ -115,101 +172,140 @@ static int _lpuart_serial_init(struct lpuart_fsl *base) __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo); /* provide data bits, parity, stop bit, etc */ - _lpuart_serial_setbrg(base, gd->baudrate); + _lpuart_serial_setbrg(plat, gd->baudrate); __raw_writeb(UC2_RE | UC2_TE, &base->uc2); return 0; } -static int lpuart_serial_setbrg(struct udevice *dev, int baudrate) +static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat, + int baudrate) { - struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; + struct lpuart_fsl_reg32 *base = plat->reg; + u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp; + u32 clk = get_lpuart_clk(); - _lpuart_serial_setbrg(reg, baudrate); + baud_diff = baudrate; + osr = 0; + sbr = 0; - return 0; -} + for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) { + tmp_sbr = (clk / (baudrate * tmp_osr)); -static int lpuart_serial_getc(struct udevice *dev) -{ - struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; + if (tmp_sbr == 0) + tmp_sbr = 1; - return _lpuart_serial_getc(reg); -} + /*calculate difference in actual buad w/ current values */ + tmp_diff = (clk / (tmp_osr * tmp_sbr)); + tmp_diff = tmp_diff - baudrate; -static int lpuart_serial_putc(struct udevice *dev, const char c) -{ - struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; + /* select best values between sbr and sbr+1 */ + if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) { + tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1))); + tmp_sbr++; + } - _lpuart_serial_putc(reg, c); + if (tmp_diff <= baud_diff) { + baud_diff = tmp_diff; + osr = tmp_osr; + sbr = tmp_sbr; + } + } - return 0; -} + /* + * TODO: handle buadrate outside acceptable rate + * if (baudDiff > ((config->baudRate_Bps / 100) * 3)) + * { + * Unacceptable baud rate difference of more than 3% + * return kStatus_LPUART_BaudrateNotSupport; + * } + */ + tmp = in_le32(&base->baud); -static int lpuart_serial_pending(struct udevice *dev, bool input) -{ - struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; + if ((osr > 3) && (osr < 8)) + tmp |= LPUART_BAUD_BOTHEDGE_MASK; - if (input) - return _lpuart_serial_tstc(reg); - else - return __raw_readb(®->us1) & US1_TDRE ? 0 : 1; -} + tmp &= ~LPUART_BAUD_OSR_MASK; + tmp |= LPUART_BAUD_OSR(osr-1); -static int lpuart_serial_probe(struct udevice *dev) -{ - struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; + tmp &= ~LPUART_BAUD_SBR_MASK; + tmp |= LPUART_BAUD_SBR(sbr); - return _lpuart_serial_init(reg); -} -#else + /* explicitly disable 10 bit mode & set 1 stop bit */ + tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK); -u32 __weak get_lpuart_clk(void) -{ - return CONFIG_SYS_CLK_FREQ; + out_le32(&base->baud, tmp); } -static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate) +static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat, + int baudrate) { + struct lpuart_fsl_reg32 *base = plat->reg; u32 clk = get_lpuart_clk(); u32 sbr; sbr = (clk / (16 * baudrate)); /* place adjustment later - n/32 BRFA */ - out_be32(&base->baud, sbr); + lpuart_write32(plat->flags, &base->baud, sbr); } -static int _lpuart32_serial_getc(struct lpuart_fsl *base) +static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat) { - u32 stat; + struct lpuart_fsl_reg32 *base = plat->reg; + u32 stat, val; - while (((stat = in_be32(&base->stat)) & STAT_RDRF) == 0) { - out_be32(&base->stat, STAT_FLAGS); + lpuart_read32(plat->flags, &base->stat, &stat); + while ((stat & STAT_RDRF) == 0) { + lpuart_write32(plat->flags, &base->stat, STAT_FLAGS); WATCHDOG_RESET(); + lpuart_read32(plat->flags, &base->stat, &stat); + } + + lpuart_read32(plat->flags, &base->data, &val); + + if (plat->devtype & DEV_MX7ULP) { + lpuart_read32(plat->flags, &base->stat, &stat); + if (stat & STAT_OR) + lpuart_write32(plat->flags, &base->stat, STAT_OR); } - return in_be32(&base->data) & 0x3ff; + return val & 0x3ff; } -static void _lpuart32_serial_putc(struct lpuart_fsl *base, const char c) +static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat, + const char c) { - while (!(in_be32(&base->stat) & STAT_TDRE)) + struct lpuart_fsl_reg32 *base = plat->reg; + u32 stat; + + if (plat->devtype & DEV_MX7ULP) { + if (c == '\n') + serial_putc('\r'); + } + + while (true) { + lpuart_read32(plat->flags, &base->stat, &stat); + + if ((stat & STAT_TDRE)) + break; + WATCHDOG_RESET(); + } - out_be32(&base->data, c); + lpuart_write32(plat->flags, &base->data, c); } /* Test whether a character is in the RX buffer */ -static int _lpuart32_serial_tstc(struct lpuart_fsl *base) +static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat) { - if ((in_be32(&base->water) >> 24) == 0) + struct lpuart_fsl_reg32 *base = plat->reg; + u32 water; + + lpuart_read32(plat->flags, &base->water, &water); + + if ((water >> 24) == 0) return 0; return 1; @@ -219,91 +315,127 @@ static int _lpuart32_serial_tstc(struct lpuart_fsl *base) * 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_fsl *base) +static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat) { - u8 ctrl; + struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg; + u32 ctrl; - ctrl = in_be32(&base->ctrl); + lpuart_read32(plat->flags, &base->ctrl, &ctrl); ctrl &= ~CTRL_RE; ctrl &= ~CTRL_TE; - out_be32(&base->ctrl, ctrl); + lpuart_write32(plat->flags, &base->ctrl, ctrl); - out_be32(&base->modir, 0); - out_be32(&base->fifo, ~(FIFO_TXFE | FIFO_RXFE)); + lpuart_write32(plat->flags, &base->modir, 0); + lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE)); - out_be32(&base->match, 0); + lpuart_write32(plat->flags, &base->match, 0); - /* provide data bits, parity, stop bit, etc */ - _lpuart32_serial_setbrg(base, gd->baudrate); + if (plat->devtype & DEV_MX7ULP) { + _lpuart32_serial_setbrg_7ulp(plat, gd->baudrate); + } else { + /* provide data bits, parity, stop bit, etc */ + _lpuart32_serial_setbrg(plat, gd->baudrate); + } - out_be32(&base->ctrl, CTRL_RE | CTRL_TE); + lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE); return 0; } -static int lpuart32_serial_setbrg(struct udevice *dev, int baudrate) +static int lpuart_serial_setbrg(struct udevice *dev, int baudrate) { struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; - _lpuart32_serial_setbrg(reg, baudrate); + if (is_lpuart32(dev)) { + if (plat->devtype & DEV_MX7ULP) + _lpuart32_serial_setbrg_7ulp(plat, baudrate); + else + _lpuart32_serial_setbrg(plat, baudrate); + } else { + _lpuart_serial_setbrg(plat, baudrate); + } return 0; } -static int lpuart32_serial_getc(struct udevice *dev) +static int lpuart_serial_getc(struct udevice *dev) { struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; - return _lpuart32_serial_getc(reg); + if (is_lpuart32(dev)) + return _lpuart32_serial_getc(plat); + + return _lpuart_serial_getc(plat); } -static int lpuart32_serial_putc(struct udevice *dev, const char c) +static int lpuart_serial_putc(struct udevice *dev, const char c) { struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; - _lpuart32_serial_putc(reg, c); + if (is_lpuart32(dev)) + _lpuart32_serial_putc(plat, c); + else + _lpuart_serial_putc(plat, c); return 0; } -static int lpuart32_serial_pending(struct udevice *dev, bool input) +static int lpuart_serial_pending(struct udevice *dev, bool input) { struct lpuart_serial_platdata *plat = dev->platdata; struct lpuart_fsl *reg = plat->reg; + struct lpuart_fsl_reg32 *reg32 = plat->reg; + u32 stat; + + if (is_lpuart32(dev)) { + if (input) { + return _lpuart32_serial_tstc(plat); + } else { + lpuart_read32(plat->flags, ®32->stat, &stat); + return stat & STAT_TDRE ? 0 : 1; + } + } if (input) - return _lpuart32_serial_tstc(reg); + return _lpuart_serial_tstc(plat); else - return in_be32(®->stat) & STAT_TDRE ? 0 : 1; + return __raw_readb(®->us1) & US1_TDRE ? 0 : 1; } -static int lpuart32_serial_probe(struct udevice *dev) +static int lpuart_serial_probe(struct udevice *dev) { struct lpuart_serial_platdata *plat = dev->platdata; - struct lpuart_fsl *reg = plat->reg; - return _lpuart32_serial_init(reg); + if (is_lpuart32(dev)) + return _lpuart32_serial_init(plat); + else + return _lpuart_serial_init(plat); } -#endif /* CONFIG_LPUART_32B_REG */ static int lpuart_serial_ofdata_to_platdata(struct udevice *dev) { struct lpuart_serial_platdata *plat = dev->platdata; + const void *blob = gd->fdt_blob; + int node = dev->of_offset; fdt_addr_t addr; addr = dev_get_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - plat->reg = (struct lpuart_fsl *)addr; + plat->reg = (void *)addr; + plat->flags = dev_get_driver_data(dev); + + if (!fdt_node_check_compatible(blob, node, "fsl,ls1021a-lpuart")) + plat->devtype = DEV_LS1021A; + else if (!fdt_node_check_compatible(blob, node, "fsl,imx7ulp-lpuart")) + plat->devtype = DEV_MX7ULP; + else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart")) + plat->devtype = DEV_VF610; return 0; } -#ifndef CONFIG_LPUART_32B_REG static const struct dm_serial_ops lpuart_serial_ops = { .putc = lpuart_serial_putc, .pending = lpuart_serial_pending, @@ -312,7 +444,11 @@ static const struct dm_serial_ops lpuart_serial_ops = { }; static const struct udevice_id lpuart_serial_ids[] = { - { .compatible = "fsl,vf610-lpuart" }, + { .compatible = "fsl,ls1021a-lpuart", .data = + LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG }, + { .compatible = "fsl,imx7ulp-lpuart", + .data = LPUART_FLAG_REGMAP_32BIT_REG }, + { .compatible = "fsl,vf610-lpuart"}, { } }; @@ -326,27 +462,3 @@ U_BOOT_DRIVER(serial_lpuart) = { .ops = &lpuart_serial_ops, .flags = DM_FLAG_PRE_RELOC, }; -#else /* CONFIG_LPUART_32B_REG */ -static const struct dm_serial_ops lpuart32_serial_ops = { - .putc = lpuart32_serial_putc, - .pending = lpuart32_serial_pending, - .getc = lpuart32_serial_getc, - .setbrg = lpuart32_serial_setbrg, -}; - -static const struct udevice_id lpuart32_serial_ids[] = { - { .compatible = "fsl,ls1021a-lpuart" }, - { } -}; - -U_BOOT_DRIVER(serial_lpuart32) = { - .name = "serial_lpuart32", - .id = UCLASS_SERIAL, - .of_match = lpuart32_serial_ids, - .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata, - .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata), - .probe = lpuart32_serial_probe, - .ops = &lpuart32_serial_ops, - .flags = DM_FLAG_PRE_RELOC, -}; -#endif /* CONFIG_LPUART_32B_REG */ diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c index 1cfcbf25b4..64126e25bf 100644 --- a/drivers/serial/serial_mxc.c +++ b/drivers/serial/serial_mxc.c @@ -365,6 +365,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" }, { } }; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e69de29bb2..dbdaafc149 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -0,0 +1,8 @@ +menu "WATCHDOG support" + +config ULP_WATCHDOG + bool "i.MX7ULP watchdog" + help + Say Y here to enable i.MX7ULP watchdog driver. + +endmenu diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a007ae8234..dea18363ca 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o obj-$(CONFIG_BFIN_WATCHDOG) += bfin_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o +obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o diff --git a/drivers/watchdog/ulp_wdog.c b/drivers/watchdog/ulp_wdog.c new file mode 100644 index 0000000000..72ec694cdd --- /dev/null +++ b/drivers/watchdog/ulp_wdog.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> + +/* + * MX7ULP WDOG Register Map + */ +struct wdog_regs { + u8 cs1; + u8 cs2; + u16 reserve0; + u32 cnt; + u32 toval; + u32 win; +}; + +#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS +#define CONFIG_WATCHDOG_TIMEOUT_MSECS 0x1500 +#endif + +#define REFRESH_WORD0 0xA602 /* 1st refresh word */ +#define REFRESH_WORD1 0xB480 /* 2nd refresh word */ + +#define UNLOCK_WORD0 0xC520 /* 1st unlock word */ +#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */ + +#define WDGCS1_WDGE (1<<7) +#define WDGCS1_WDGUPDATE (1<<5) + +#define WDGCS2_FLG (1<<6) + +#define WDG_BUS_CLK (0x0) +#define WDG_LPO_CLK (0x1) +#define WDG_32KHZ_CLK (0x2) +#define WDG_EXT_CLK (0x3) + +DECLARE_GLOBAL_DATA_PTR; + +void hw_watchdog_set_timeout(u16 val) +{ + /* setting timeout value */ + struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; + + writel(val, &wdog->toval); +} + +void hw_watchdog_reset(void) +{ + struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; + + writel(REFRESH_WORD0, &wdog->cnt); + writel(REFRESH_WORD1, &wdog->cnt); +} + +void hw_watchdog_init(void) +{ + u8 val; + struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; + + writel(UNLOCK_WORD0, &wdog->cnt); + writel(UNLOCK_WORD1, &wdog->cnt); + + val = readb(&wdog->cs2); + val |= WDGCS2_FLG; + writeb(val, &wdog->cs2); + + hw_watchdog_set_timeout(CONFIG_WATCHDOG_TIMEOUT_MSECS); + writel(0, &wdog->win); + + writeb(WDG_LPO_CLK, &wdog->cs2);/* setting 1-kHz clock source */ + writeb((WDGCS1_WDGE | WDGCS1_WDGUPDATE), &wdog->cs1);/* enable counter running */ + + hw_watchdog_reset(); +} + +void reset_cpu(ulong addr) +{ + struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; + + writel(UNLOCK_WORD0, &wdog->cnt); + writel(UNLOCK_WORD1, &wdog->cnt); + + hw_watchdog_set_timeout(5); /* 5ms timeout */ + writel(0, &wdog->win); + + writeb(WDG_LPO_CLK, &wdog->cs2);/* setting 1-kHz clock source */ + writeb(WDGCS1_WDGE, &wdog->cs1);/* enable counter running */ + + hw_watchdog_reset(); + + while (1); +} |