diff options
Diffstat (limited to 'drivers')
127 files changed, 3505 insertions, 838 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 417d6f88c29..b26ca8cf70c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -50,6 +50,8 @@ source "drivers/i2c/Kconfig" source "drivers/input/Kconfig" +source "drivers/iommu/Kconfig" + source "drivers/led/Kconfig" source "drivers/mailbox/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4cbc40787db..4e7cf284405 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -102,6 +102,7 @@ obj-y += mtd/ obj-y += pwm/ obj-y += reset/ obj-y += input/ +obj-y += iommu/ # SOC specific infrastructure drivers. obj-y += smem/ obj-y += thermal/ diff --git a/drivers/ata/sata_ceva.c b/drivers/ata/sata_ceva.c index 87e6a90f74e..b71f10223da 100644 --- a/drivers/ata/sata_ceva.c +++ b/drivers/ata/sata_ceva.c @@ -212,7 +212,7 @@ static int sata_ceva_of_to_plat(struct udevice *dev) if (priv->base == FDT_ADDR_T_NONE) return -EINVAL; - ret = dev_read_resource_byname(dev, "ecc-addr", &res_regs); + ret = dev_read_resource_byname(dev, "sata-ecc", &res_regs); if (ret) priv->ecc_base = 0; else diff --git a/drivers/cache/cache-sifive-ccache.c b/drivers/cache/cache-sifive-ccache.c index 76c0ab26ae3..c8766f62427 100644 --- a/drivers/cache/cache-sifive-ccache.c +++ b/drivers/cache/cache-sifive-ccache.c @@ -38,7 +38,7 @@ static int sifive_ccache_get_info(struct udevice *dev, struct cache_info *info) { struct sifive_ccache *priv = dev_get_priv(dev); - info->base = (phys_addr_t)priv->base; + info->base = (uintptr_t)priv->base; return 0; } diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 114192bb321..83ab6b728ed 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -27,12 +27,10 @@ DECLARE_GLOBAL_DATA_PTR; -#ifndef CONFIG_TFABOOT -#if !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD) +#if defined(CONFIG_SPL_BUILD) /* activate clock tree initialization in the driver */ #define STM32MP1_CLOCK_TREE_INIT #endif -#endif #define MAX_HSI_HZ 64000000 diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c index cd1acb94429..52ae268e0c8 100644 --- a/drivers/clk/sifive/sifive-prci.c +++ b/drivers/clk/sifive/sifive-prci.c @@ -653,9 +653,9 @@ static int sifive_prci_probe(struct udevice *dev) struct prci_clk_desc *data = (struct prci_clk_desc *)dev_get_driver_data(dev); - pd->va = (void *)dev_read_addr(dev); - if (IS_ERR(pd->va)) - return PTR_ERR(pd->va); + pd->va = dev_read_addr_ptr(dev); + if (!pd->va) + return -EINVAL; err = clk_get_by_index(dev, 0, &pd->parent_hfclk); if (err) diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index f89c7ffd42a..f19908113e1 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -2,6 +2,7 @@ config CLK_SUNXI bool "Clock support for Allwinner SoCs" depends on CLK && ARCH_SUNXI select DM_RESET + select SPL_DM_RESET if SPL_CLK default y help This enables support for common clock driver API on Allwinner diff --git a/drivers/core/device.c b/drivers/core/device.c index d7a778a2413..efd07176e37 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -28,6 +28,7 @@ #include <dm/uclass.h> #include <dm/uclass-internal.h> #include <dm/util.h> +#include <iommu.h> #include <linux/err.h> #include <linux/list.h> #include <power-domain.h> @@ -543,6 +544,13 @@ int device_probe(struct udevice *dev) goto fail; } + if (CONFIG_IS_ENABLED(IOMMU) && dev->parent && + (device_get_uclass_id(dev) != UCLASS_IOMMU)) { + ret = dev_iommu_enable(dev); + if (ret) + goto fail; + } + ret = device_get_dma_constraints(dev); if (ret) goto fail; diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c index 6dfda207726..c3a50a2b0c1 100644 --- a/drivers/core/fdtaddr.c +++ b/drivers/core/fdtaddr.c @@ -93,6 +93,13 @@ fdt_addr_t devfdt_get_addr_index(const struct udevice *dev, int index) #endif } +void *devfdt_get_addr_index_ptr(const struct udevice *dev, int index) +{ + fdt_addr_t addr = devfdt_get_addr_index(dev, index); + + return (addr == FDT_ADDR_T_NONE) ? NULL : (void *)(uintptr_t)addr; +} + fdt_addr_t devfdt_get_addr_size_index(const struct udevice *dev, int index, fdt_size_t *size) { @@ -155,9 +162,7 @@ fdt_addr_t devfdt_get_addr(const struct udevice *dev) void *devfdt_get_addr_ptr(const struct udevice *dev) { - fdt_addr_t addr = devfdt_get_addr_index(dev, 0); - - return (addr == FDT_ADDR_T_NONE) ? NULL : (void *)(uintptr_t)addr; + return devfdt_get_addr_index_ptr(dev, 0); } void *devfdt_remap_addr_index(const struct udevice *dev, int index) diff --git a/drivers/crypto/fsl/fsl_hash.c b/drivers/crypto/fsl/fsl_hash.c index 8b5c26db070..80394730121 100644 --- a/drivers/crypto/fsl/fsl_hash.c +++ b/drivers/crypto/fsl/fsl_hash.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2014 Freescale Semiconductor, Inc. - * + * Copyright 2021 NXP */ #include <common.h> @@ -120,8 +120,8 @@ static int caam_hash_update(void *hash_ctx, const void *buf, * Perform progressive hashing on the given buffer and copy hash at * destination buffer * - * The context is freed after completion of hash operation. - * + * The context is freed after successful completion of hash operation. + * In case of failure, context is not freed. * @hash_ctx: Pointer to the context for hashing * @dest_buf: Pointer to the destination buffer where hash is to be copied * @size: Size of the buffer being hashed @@ -136,7 +136,6 @@ static int caam_hash_finish(void *hash_ctx, void *dest_buf, int i = 0, ret = 0; if (size < driver_hash[caam_algo].digestsize) { - free(ctx); return -EINVAL; } @@ -152,11 +151,12 @@ static int caam_hash_finish(void *hash_ctx, void *dest_buf, ret = run_descriptor_jr(ctx->sha_desc); - if (ret) + if (ret) { debug("Error %x\n", ret); - else + return ret; + } else { memcpy(dest_buf, ctx->hash, sizeof(ctx->hash)); - + } free(ctx); return ret; } diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index e43c6801546..89cb4d352ee 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2014-2020 Freescale Semiconductor, Inc. + * Copyright 2021 NXP */ #include <common.h> @@ -57,7 +58,8 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, struct ccsr_ddr __iomem *ddr; u32 temp32; u32 total_gb_size_per_controller; - int timeout; + int timeout = 0; + int ddr_freq_for_timeout = 0; int mod_bnds = 0; #ifdef CONFIG_SYS_FSL_ERRATUM_A008511 @@ -511,8 +513,14 @@ step2: */ bus_width = 3 - ((ddr_in32(&ddr->sdram_cfg) & SDRAM_CFG_DBW_MASK) >> SDRAM_CFG_DBW_SHIFT); - timeout = ((total_gb_size_per_controller << (6 - bus_width)) * 100 / - (get_ddr_freq(ctrl_num) >> 20)) << 2; + ddr_freq_for_timeout = (get_ddr_freq(ctrl_num) >> 20) << 2; + if (ddr_freq_for_timeout) { + timeout = ((total_gb_size_per_controller << + (6 - bus_width)) * 100 / + ddr_freq_for_timeout); + } else { + debug("Error in getting timeout.\n"); + } total_gb_size_per_controller >>= 4; /* shift down to gb size */ debug("total %d GB\n", total_gb_size_per_controller); debug("Need to wait up to %d * 10ms\n", timeout); diff --git a/drivers/ddr/fsl/lc_common_dimm_params.c b/drivers/ddr/fsl/lc_common_dimm_params.c index d299d763db1..d738ae3a7c6 100644 --- a/drivers/ddr/fsl/lc_common_dimm_params.c +++ b/drivers/ddr/fsl/lc_common_dimm_params.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright 2008-2016 Freescale Semiconductor, Inc. - * Copyright 2017-2018 NXP Semiconductor + * Copyright 2017-2021 NXP Semiconductor */ #include <common.h> @@ -23,7 +23,7 @@ compute_cas_latency(const unsigned int ctrl_num, unsigned int caslat_actual; unsigned int retry = 16; unsigned int tmp = ~0; - const unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num); + unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num); #ifdef CONFIG_SYS_FSL_DDR3 const unsigned int taamax = 20000; #else @@ -37,6 +37,12 @@ compute_cas_latency(const unsigned int ctrl_num, } common_caslat = tmp; + if (!mclk_ps) { + printf("DDR clock (MCLK cycle was 0 ps), So setting it to slowest DIMM(s) (tCKmin %u ps).\n", + outpdimm->tckmin_x_ps); + mclk_ps = outpdimm->tckmin_x_ps; + } + /* validate if the memory clk is in the range of dimms */ if (mclk_ps < outpdimm->tckmin_x_ps) { printf("DDR clock (MCLK cycle %u ps) is faster than " diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c index 8e147160b9f..f389e5ef952 100644 --- a/drivers/ddr/fsl/main.c +++ b/drivers/ddr/fsl/main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright 2008-2014 Freescale Semiconductor, Inc. + * Copyright 2021 NXP */ /* @@ -297,9 +298,13 @@ const char * step_to_string(unsigned int step) { unsigned int s = __ilog2(step); - if ((1 << s) != step) - return step_string_tbl[7]; - + if (s <= 31) { + if ((1 << s) != step) + return step_string_tbl[7]; + } else { + if ((1 << (s - 32)) != step) + return step_string_tbl[7]; + } if (s >= ARRAY_SIZE(step_string_tbl)) { printf("Error for the step in %s\n", __func__); s = 0; diff --git a/drivers/ddr/fsl/util.c b/drivers/ddr/fsl/util.c index ac4f8d2732d..43cb01804b7 100644 --- a/drivers/ddr/fsl/util.c +++ b/drivers/ddr/fsl/util.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright 2008-2014 Freescale Semiconductor, Inc. + * Copyright 2021 NXP */ #include <common.h> @@ -75,10 +76,13 @@ unsigned int get_memory_clk_period_ps(const unsigned int ctrl_num) /* Round to nearest 10ps, being careful about 64-bit multiply/divide */ unsigned long long rem, mclk_ps = ULL_2E12; - - /* Now perform the big divide, the result fits in 32-bits */ - rem = do_div(mclk_ps, data_rate); - result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps; + if (data_rate) { + /* Now perform the big divide, the result fits in 32-bits */ + rem = do_div(mclk_ps, data_rate); + result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps; + } else { + result = 0; + } return result; } diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 48e41bc2629..8d7f13dcb0b 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -38,6 +38,13 @@ config DFU_MMC help This option enables using DFU to read and write to MMC based storage. +config DFU_MTD + bool "MTD back end for DFU" + depends on DM_MTD + depends on CMD_MTDPARTS + help + This option enables using DFU to read and write to on any MTD device. + config DFU_NAND bool "NAND back end for DFU" depends on CMD_MTDPARTS @@ -72,13 +79,6 @@ config DFU_SF_PART This option enables the support of "part" and "partubi" target in SPI flash DFU back end. -config DFU_MTD - bool "MTD back end for DFU" - depends on DM_MTD - depends on CMD_MTDPARTS - help - This option enables using DFU to read and write to on any MTD device. - config DFU_VIRT bool "VIRTUAL flash back end for DFU" help diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index ff1859d9463..af3975925a9 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -735,6 +735,7 @@ int dfu_write_from_mem_addr(struct dfu_entity *dfu, void *buf, int size) ret = dfu_flush(dfu, NULL, 0, i); if (ret) pr_err("DFU flush failed!"); + puts("\n"); return ret; } diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index 7e64ab772f0..b72493ced86 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -24,8 +24,18 @@ static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size) static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf, long *len) { - return spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset, - *len, buf); + long seglen = *len; + int ret; + + if (seglen > (16 << 20)) + seglen = (16 << 20); + + ret = spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset, + seglen, buf); + if (!ret) + *len = seglen; + + return ret; } static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset) diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c index e1ff84c1c03..a3c3cd7824c 100644 --- a/drivers/gpio/msm_gpio.c +++ b/drivers/gpio/msm_gpio.c @@ -120,6 +120,7 @@ static const struct udevice_id msm_gpio_ids[] = { { .compatible = "qcom,msm8916-pinctrl" }, { .compatible = "qcom,apq8016-pinctrl" }, { .compatible = "qcom,ipq4019-pinctrl" }, + { .compatible = "qcom,sdm845-pinctrl" }, { } }; diff --git a/drivers/gpio/pm8916_gpio.c b/drivers/gpio/pm8916_gpio.c index 40b0f2578b9..7ad95784a89 100644 --- a/drivers/gpio/pm8916_gpio.c +++ b/drivers/gpio/pm8916_gpio.c @@ -202,6 +202,7 @@ static int pm8916_gpio_of_to_plat(struct udevice *dev) static const struct udevice_id pm8916_gpio_ids[] = { { .compatible = "qcom,pm8916-gpio" }, { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ + { .compatible = "qcom,pm8998-gpio" }, { } }; @@ -266,7 +267,7 @@ static int pm8941_pwrkey_probe(struct udevice *dev) return log_msg_ret("bad type", -ENXIO); reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE); - if (reg != 0x1) + if ((reg & 0x5) == 0) return log_msg_ret("bad subtype", -ENXIO); return 0; @@ -287,11 +288,12 @@ static int pm8941_pwrkey_of_to_plat(struct udevice *dev) static const struct udevice_id pm8941_pwrkey_ids[] = { { .compatible = "qcom,pm8916-pwrkey" }, { .compatible = "qcom,pm8994-pwrkey" }, + { .compatible = "qcom,pm8998-pwrkey" }, { } }; -U_BOOT_DRIVER(pwrkey_pm8941) = { - .name = "pwrkey_pm8916", +U_BOOT_DRIVER(pwrkey_pm89xx) = { + .name = "pwrkey_pm89xx", .id = UCLASS_GPIO, .of_match = pm8941_pwrkey_ids, .of_to_plat = pm8941_pwrkey_of_to_plat, diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 76f35ac5d92..06ed585f3d6 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -357,6 +357,7 @@ static const struct udevice_id exynos_gpio_ids[] = { { .compatible = "samsung,exynos4x12-pinctrl" }, { .compatible = "samsung,exynos5250-pinctrl" }, { .compatible = "samsung,exynos5420-pinctrl" }, + { .compatible = "samsung,exynos78x0-gpio" }, { } }; diff --git a/drivers/gpio/sifive-gpio.c b/drivers/gpio/sifive-gpio.c index abd1f629b9b..151f484e8fd 100644 --- a/drivers/gpio/sifive-gpio.c +++ b/drivers/gpio/sifive-gpio.c @@ -157,13 +157,11 @@ static const struct dm_gpio_ops sifive_gpio_ops = { static int sifive_gpio_of_to_plat(struct udevice *dev) { struct sifive_gpio_plat *plat = dev_get_plat(dev); - fdt_addr_t addr; - addr = dev_read_addr(dev); - if (addr == FDT_ADDR_T_NONE) + plat->base = dev_read_addr_ptr(dev); + if (!plat->base) return -EINVAL; - plat->base = (void *)addr; return 0; } diff --git a/drivers/gpio/stm32_gpio.c b/drivers/gpio/stm32_gpio.c index 125c237551c..8667ed3835c 100644 --- a/drivers/gpio/stm32_gpio.c +++ b/drivers/gpio/stm32_gpio.c @@ -11,7 +11,6 @@ #include <dm.h> #include <fdtdec.h> #include <log.h> -#include <asm/arch/gpio.h> #include <asm/arch/stm32.h> #include <asm/gpio.h> #include <asm/io.h> @@ -20,6 +19,8 @@ #include <linux/errno.h> #include <linux/io.h> +#include "stm32_gpio_priv.h" + #define STM32_GPIOS_PER_BANK 16 #define MODE_BITS(gpio_pin) ((gpio_pin) * 2) diff --git a/drivers/gpio/stm32_gpio_priv.h b/drivers/gpio/stm32_gpio_priv.h new file mode 100644 index 00000000000..d3d8f2ed5de --- /dev/null +++ b/drivers/gpio/stm32_gpio_priv.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics. + */ + +#ifndef _STM32_GPIO_PRIV_H_ +#define _STM32_GPIO_PRIV_H_ + +enum stm32_gpio_mode { + STM32_GPIO_MODE_IN = 0, + STM32_GPIO_MODE_OUT, + STM32_GPIO_MODE_AF, + STM32_GPIO_MODE_AN +}; + +enum stm32_gpio_otype { + STM32_GPIO_OTYPE_PP = 0, + STM32_GPIO_OTYPE_OD +}; + +enum stm32_gpio_speed { + STM32_GPIO_SPEED_2M = 0, + STM32_GPIO_SPEED_25M, + STM32_GPIO_SPEED_50M, + STM32_GPIO_SPEED_100M +}; + +enum stm32_gpio_pupd { + STM32_GPIO_PUPD_NO = 0, + STM32_GPIO_PUPD_UP, + STM32_GPIO_PUPD_DOWN +}; + +enum stm32_gpio_af { + STM32_GPIO_AF0 = 0, + STM32_GPIO_AF1, + STM32_GPIO_AF2, + STM32_GPIO_AF3, + STM32_GPIO_AF4, + STM32_GPIO_AF5, + STM32_GPIO_AF6, + STM32_GPIO_AF7, + STM32_GPIO_AF8, + STM32_GPIO_AF9, + STM32_GPIO_AF10, + STM32_GPIO_AF11, + STM32_GPIO_AF12, + STM32_GPIO_AF13, + STM32_GPIO_AF14, + STM32_GPIO_AF15 +}; + +struct stm32_gpio_dsc { + u8 port; + u8 pin; +}; + +struct stm32_gpio_ctl { + enum stm32_gpio_mode mode; + enum stm32_gpio_otype otype; + enum stm32_gpio_speed speed; + enum stm32_gpio_pupd pupd; + enum stm32_gpio_af af; +}; + +struct stm32_gpio_regs { + u32 moder; /* GPIO port mode */ + u32 otyper; /* GPIO port output type */ + u32 ospeedr; /* GPIO port output speed */ + u32 pupdr; /* GPIO port pull-up/pull-down */ + u32 idr; /* GPIO port input data */ + u32 odr; /* GPIO port output data */ + u32 bsrr; /* GPIO port bit set/reset */ + u32 lckr; /* GPIO port configuration lock */ + u32 afr[2]; /* GPIO alternate function */ +}; + +struct stm32_gpio_priv { + struct stm32_gpio_regs *regs; + unsigned int gpio_range; +}; + +int stm32_offset_to_index(struct udevice *dev, unsigned int offset); + +#endif /* _STM32_GPIO_PRIV_H_ */ diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 66bd6fe2f34..7c447a8aa0a 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -627,6 +627,12 @@ config SYS_I2C_VERSATILE Add support for the Arm Ltd Versatile Express I2C driver. The I2C host controller is present in the development boards manufactured by Arm Ltd. +config SYS_I2C_MV + bool "Marvell PXA (Armada 3720) I2C driver" + help + Support for PXA based I2C controller used on Armada 3720 SoC. + In Linux, this driver is called i2c-pxa. + config SYS_I2C_MVTWSI bool "Marvell I2C driver" help diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 916427452a5..fca6b157f8a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_$(SPL_)DM_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_$(SPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o obj-$(CONFIG_$(SPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o -obj-$(CONFIG_I2C_MV) += mv_i2c.o obj-$(CONFIG_$(SPL_)SYS_I2C_LEGACY) += i2c_core.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o @@ -29,6 +28,7 @@ obj-$(CONFIG_SYS_I2C_IPROC) += iproc_i2c.o obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o +obj-$(CONFIG_SYS_I2C_MV) += mv_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o diff --git a/drivers/i2c/ocores_i2c.c b/drivers/i2c/ocores_i2c.c index f129ec3818c..3b19ba78fa3 100644 --- a/drivers/i2c/ocores_i2c.c +++ b/drivers/i2c/ocores_i2c.c @@ -516,7 +516,7 @@ static int ocores_i2c_probe(struct udevice *dev) u32 clock_frequency_khz; int ret; - bus->base = (void __iomem *)devfdt_get_addr(dev); + bus->base = dev_read_addr_ptr(dev); if (dev_read_u32(dev, "reg-shift", &bus->reg_shift)) { /* no 'reg-shift', check for deprecated 'regstep' */ diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig new file mode 100644 index 00000000000..dabc1f900d5 --- /dev/null +++ b/drivers/iommu/Kconfig @@ -0,0 +1,27 @@ +# +# IOMMU devices +# + +menu "IOMMU device drivers" + +config IOMMU + bool "Enable Driver Model for IOMMU drivers" + depends on DM + help + Enable driver model for IOMMU devices. An IOMMU maps device + virtiual memory addresses to physical addresses. Devices + that sit behind an IOMMU can typically only access physical + memory if the IOMMU has been programmed to allow access to + that memory. + +config APPLE_DART + bool "Apple DART support" + depends on IOMMU && ARCH_APPLE + default y + help + Enable support for the DART on Apple SoCs. The DART is Apple's + IOMMU implementation. The driver performs the necessary + configuration to put the DART into bypass mode such that it can + be used transparently by U-Boot. + +endmenu diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile new file mode 100644 index 00000000000..e3e0900e170 --- /dev/null +++ b/drivers/iommu/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_IOMMU) += iommu-uclass.o + +obj-$(CONFIG_APPLE_DART) += apple_dart.o +obj-$(CONFIG_SANDBOX) += sandbox_iommu.o diff --git a/drivers/iommu/apple_dart.c b/drivers/iommu/apple_dart.c new file mode 100644 index 00000000000..ff8c5fa62c2 --- /dev/null +++ b/drivers/iommu/apple_dart.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> + */ + +#include <common.h> +#include <cpu_func.h> +#include <dm.h> +#include <asm/io.h> + +#define DART_PARAMS2 0x0004 +#define DART_PARAMS2_BYPASS_SUPPORT BIT(0) +#define DART_TLB_OP 0x0020 +#define DART_TLB_OP_OPMASK (0xfff << 20) +#define DART_TLB_OP_FLUSH (0x001 << 20) +#define DART_TLB_OP_BUSY BIT(2) +#define DART_TLB_OP_SIDMASK 0x0034 +#define DART_ERROR_STATUS 0x0040 +#define DART_TCR(sid) (0x0100 + 4 * (sid)) +#define DART_TCR_TRANSLATE_ENABLE BIT(7) +#define DART_TCR_BYPASS_DART BIT(8) +#define DART_TCR_BYPASS_DAPF BIT(12) +#define DART_TTBR(sid, idx) (0x0200 + 16 * (sid) + 4 * (idx)) +#define DART_TTBR_VALID BIT(31) +#define DART_TTBR_SHIFT 12 + +static int apple_dart_probe(struct udevice *dev) +{ + void *base; + int sid, i; + + base = dev_read_addr_ptr(dev); + if (!base) + return -EINVAL; + + u32 params2 = readl(base + DART_PARAMS2); + if (params2 & DART_PARAMS2_BYPASS_SUPPORT) { + for (sid = 0; sid < 16; sid++) { + writel(DART_TCR_BYPASS_DART | DART_TCR_BYPASS_DAPF, + base + DART_TCR(sid)); + for (i = 0; i < 4; i++) + writel(0, base + DART_TTBR(sid, i)); + } + } + + return 0; +} + +static const struct udevice_id apple_dart_ids[] = { + { .compatible = "apple,t8103-dart" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(apple_dart) = { + .name = "apple_dart", + .id = UCLASS_IOMMU, + .of_match = apple_dart_ids, + .probe = apple_dart_probe +}; diff --git a/drivers/iommu/iommu-uclass.c b/drivers/iommu/iommu-uclass.c new file mode 100644 index 00000000000..ed917b3c3e8 --- /dev/null +++ b/drivers/iommu/iommu-uclass.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> + */ + +#define LOG_CATEGORY UCLASS_IOMMU + +#include <common.h> +#include <dm.h> + +#if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) +int dev_iommu_enable(struct udevice *dev) +{ + struct ofnode_phandle_args args; + struct udevice *dev_iommu; + int i, count, ret = 0; + + count = dev_count_phandle_with_args(dev, "iommus", + "#iommu-cells", 0); + for (i = 0; i < count; i++) { + ret = dev_read_phandle_with_args(dev, "iommus", + "#iommu-cells", 0, i, &args); + if (ret) { + debug("%s: dev_read_phandle_with_args failed: %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_IOMMU, args.node, + &dev_iommu); + if (ret) { + debug("%s: uclass_get_device_by_ofnode failed: %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} +#endif + +UCLASS_DRIVER(iommu) = { + .id = UCLASS_IOMMU, + .name = "iommu", +}; diff --git a/drivers/iommu/sandbox_iommu.c b/drivers/iommu/sandbox_iommu.c new file mode 100644 index 00000000000..c8161a40aef --- /dev/null +++ b/drivers/iommu/sandbox_iommu.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> + */ + +#include <common.h> +#include <dm.h> + +static const struct udevice_id sandbox_iommu_ids[] = { + { .compatible = "sandbox,iommu" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sandbox_iommu) = { + .name = "sandbox_iommu", + .id = UCLASS_IOMMU, + .of_match = sandbox_iommu_ids, +}; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 099ff293489..3bae0720058 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -233,6 +233,15 @@ config MXC_OCOTP Programmable memory pages that are stored on the some Freescale i.MX processors. +config SPL_MXC_OCOTP + bool "Enable MXC OCOTP driver in SPL" + depends on SPL && (ARCH_IMX8M || ARCH_MX6 || ARCH_MX7 || ARCH_MX7ULP || ARCH_VF610) + default y + help + If you say Y here, you will get support for the One Time + Programmable memory pages, that are stored on some + Freescale i.MX processors, in SPL. + config NUVOTON_NCT6102D bool "Enable Nuvoton NCT6102D Super I/O driver" help diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c16a77c34c4..f9826d2462d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -50,7 +50,7 @@ obj-$(CONFIG_IMX8ULP) += imx8ulp/ obj-$(CONFIG_LED_STATUS) += status_led.o obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o -obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o +obj-$(CONFIG_$(SPL_)MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index f99b5f997e2..9c5d48e90cd 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -282,6 +282,14 @@ static int host_request(struct mmc *dev, return result; } +static int check_peripheral_id(struct pl180_mmc_host *host, u32 periph_id) +{ + return readl(&host->base->periph_id0) == (periph_id & 0xFF) && + readl(&host->base->periph_id1) == ((periph_id >> 8) & 0xFF) && + readl(&host->base->periph_id2) == ((periph_id >> 16) & 0xFF) && + readl(&host->base->periph_id3) == ((periph_id >> 24) & 0xFF); +} + static int host_set_ios(struct mmc *dev) { struct pl180_mmc_host *host = dev->priv; @@ -337,6 +345,12 @@ static int host_set_ios(struct mmc *dev) sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); sdi_clkcr |= buswidth; } + /* For MMCs' with peripheral id 0x02041180 and 0x03041180, H/W flow control + * needs to be enabled for multi block writes (MMC CMD 18). + */ + if (check_peripheral_id(host, 0x02041180) || + check_peripheral_id(host, 0x03041180)) + sdi_clkcr |= SDI_CLKCR_HWFCEN; writel(sdi_clkcr, &host->base->clock); udelay(CLK_CHANGE_DELAY); diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h index 15c29beadbc..fca15910a8d 100644 --- a/drivers/mmc/arm_pl180_mmci.h +++ b/drivers/mmc/arm_pl180_mmci.h @@ -43,6 +43,7 @@ #define SDI_CLKCR_CLKEN 0x00000100 #define SDI_CLKCR_PWRSAV 0x00000200 #define SDI_CLKCR_BYPASS 0x00000400 +#define SDI_CLKCR_HWFCEN 0x00001000 #define SDI_CLKCR_WIDBUS_MASK 0x00001800 #define SDI_CLKCR_WIDBUS_1 0x00000000 #define SDI_CLKCR_WIDBUS_4 0x00000800 diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index ebb307e9506..05a6d0ce156 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -27,6 +27,7 @@ #include <dm/device_compat.h> #include <linux/bitops.h> #include <linux/delay.h> +#include <linux/iopoll.h> #include <linux/dma-mapping.h> #include <sdhci.h> @@ -1138,6 +1139,20 @@ int fsl_esdhc_hs400_prepare_ddr(struct udevice *dev) return 0; } +static int fsl_esdhc_wait_dat0(struct udevice *dev, int state, + int timeout_us) +{ + int ret; + u32 tmp; + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + + ret = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, + !!(tmp & PRSSTAT_DAT0) == !!state, + timeout_us); + return ret; +} + static const struct dm_mmc_ops fsl_esdhc_ops = { .get_cd = fsl_esdhc_get_cd, .send_cmd = fsl_esdhc_send_cmd, @@ -1147,6 +1162,7 @@ static const struct dm_mmc_ops fsl_esdhc_ops = { #endif .reinit = fsl_esdhc_reinit, .hs400_prepare_ddr = fsl_esdhc_hs400_prepare_ddr, + .wait_dat0 = fsl_esdhc_wait_dat0, }; static const struct udevice_id fsl_esdhc_ids[] = { diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 5dfd484ef9a..4c06361beef 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -727,17 +727,20 @@ static void esdhc_set_strobe_dll(struct mmc *mmc) if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) { esdhc_write32(®s->strobe_dllctrl, ESDHC_STROBE_DLL_CTRL_RESET); + /* clear the reset bit on strobe dll before any setting */ + esdhc_write32(®s->strobe_dllctrl, 0); /* * enable strobe dll ctrl and adjust the delay target * for the uSDHC loopback read clock */ val = ESDHC_STROBE_DLL_CTRL_ENABLE | + ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT | (priv->strobe_dll_delay_target << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); esdhc_write32(®s->strobe_dllctrl, val); - /* wait 1us to make sure strobe dll status register stable */ - mdelay(1); + /* wait 5us to make sure strobe dll status register stable */ + mdelay(5); val = esdhc_read32(®s->strobe_dllstat); if (!(val & ESDHC_STROBE_DLL_STS_REF_LOCK)) pr_warn("HS400 strobe DLL status REF not lock!\n"); @@ -971,7 +974,6 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) if (priv->clock != clock) set_sysctl(priv, mmc, clock); -#ifdef MMC_SUPPORTS_TUNING if (mmc->clk_disable) { #ifdef CONFIG_FSL_USDHC esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN); @@ -987,6 +989,7 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) #endif } +#ifdef MMC_SUPPORTS_TUNING /* * For HS400/HS400ES mode, make sure set the strobe dll in the * target clock rate. So call esdhc_set_strobe_dll() after the @@ -1707,6 +1710,12 @@ static struct esdhc_soc_data usdhc_imx7d_data = { | ESDHC_FLAG_HS400, }; +static struct esdhc_soc_data usdhc_imx7ulp_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400, +}; + static struct esdhc_soc_data usdhc_imx8qm_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | @@ -1721,7 +1730,7 @@ static const struct udevice_id fsl_esdhc_ids[] = { { .compatible = "fsl,imx6sl-usdhc", }, { .compatible = "fsl,imx6q-usdhc", }, { .compatible = "fsl,imx7d-usdhc", .data = (ulong)&usdhc_imx7d_data,}, - { .compatible = "fsl,imx7ulp-usdhc", }, + { .compatible = "fsl,imx7ulp-usdhc", .data = (ulong)&usdhc_imx7ulp_data,}, { .compatible = "fsl,imx8qm-usdhc", .data = (ulong)&usdhc_imx8qm_data,}, { .compatible = "fsl,imx8mm-usdhc", .data = (ulong)&usdhc_imx8qm_data,}, { .compatible = "fsl,imx8mn-usdhc", .data = (ulong)&usdhc_imx8qm_data,}, diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index ba54b19c140..4d9871d69f2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -819,11 +819,11 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, return ret; /* - * In cases when not allowed to poll by using CMD13 or because we aren't + * In cases when neiter allowed to poll by using CMD13 nor we are * capable of polling by using mmc_wait_dat0, then rely on waiting the * stated timeout to be sufficient. */ - if (ret == -ENOSYS || !send_status) { + if (ret == -ENOSYS && !send_status) { mdelay(timeout_ms); return 0; } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 03bfd9d18ae..766e4a6b0c5 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -780,6 +780,25 @@ static int sdhci_get_cd(struct udevice *dev) return value; } +static int sdhci_wait_dat0(struct udevice *dev, int state, + int timeout_us) +{ + int tmp; + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct sdhci_host *host = mmc->priv; + unsigned long timeout = timer_get_us() + timeout_us; + + // readx_poll_timeout is unsuitable because sdhci_readl accepts + // two arguments + do { + tmp = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (!!(tmp & SDHCI_DATA_0_LVL_MASK) == !!state) + return 0; + } while (!timeout_us || !time_after(timer_get_us(), timeout)); + + return -ETIMEDOUT; +} + const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, @@ -788,6 +807,7 @@ const struct dm_mmc_ops sdhci_ops = { #ifdef MMC_SUPPORTS_TUNING .execute_tuning = sdhci_execute_tuning, #endif + .wait_dat0 = sdhci_wait_dat0, }; #else static const struct mmc_ops sdhci_ops = { diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index c170c16d5ab..4bf8a9b92ce 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -72,10 +72,12 @@ static int mmc_resource_init(int sdc_no) priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE; priv->mclkreg = &ccm->sd1_clk_cfg; break; +#ifdef SUNXI_MMC2_BASE case 2: priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE; priv->mclkreg = &ccm->sd2_clk_cfg; break; +#endif #ifdef SUNXI_MMC3_BASE case 3: priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE; diff --git a/drivers/mtd/altera_qspi.c b/drivers/mtd/altera_qspi.c index 7bac599a54a..d31391f36a4 100644 --- a/drivers/mtd/altera_qspi.c +++ b/drivers/mtd/altera_qspi.c @@ -153,7 +153,6 @@ static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr) putc('\n'); instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; instr->state = MTD_ERASE_FAILED; - mtd_erase_callback(instr); return -EIO; } flash = pdata->base + addr; @@ -177,7 +176,6 @@ static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr) writel(stat, ®s->isr); /* clear isr */ instr->fail_addr = addr; instr->state = MTD_ERASE_FAILED; - mtd_erase_callback(instr); return -EIO; } if (flash_verbose) @@ -189,7 +187,6 @@ static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr) addr += mtd->erasesize; } instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/cfi_mtd.c b/drivers/mtd/cfi_mtd.c index 78293caa2f7..2295bb7220b 100644 --- a/drivers/mtd/cfi_mtd.c +++ b/drivers/mtd/cfi_mtd.c @@ -58,7 +58,6 @@ static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) } instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 684bc949985..af3c4765c4d 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -338,14 +338,6 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) return -EINVAL; } -static void concat_erase_callback(struct erase_info *instr) -{ - /* Nothing to do here in U-Boot */ -#ifndef __UBOOT__ - wake_up((wait_queue_head_t *) instr->priv); -#endif -} - static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) { int err; @@ -358,7 +350,6 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) init_waitqueue_head(&waitq); erase->mtd = mtd; - erase->callback = concat_erase_callback; erase->priv = (unsigned long) &waitq; /* @@ -498,8 +489,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) if (err) return err; - if (instr->callback) - instr->callback(instr); return 0; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 9496903e861..1d45fb55c72 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -906,13 +906,6 @@ void __put_mtd_device(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(__put_mtd_device); -/* - * Erase is an asynchronous operation. Device drivers are supposed - * to call instr->callback() whenever the operation completes, even - * if it completes with a failure. - * Callers are supposed to pass a callback function and wait for it - * to be called before writing to the block. - */ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr) @@ -922,7 +915,6 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; if (!instr->len) { instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } return mtd->_erase(mtd, instr); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index aa58f722dad..a435ce6d079 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -446,26 +446,15 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) int ret; instr->addr += mtd->offset; + ret = mtd->parent->_erase(mtd->parent, instr); - if (ret) { - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= mtd->offset; - instr->addr -= mtd->offset; - } - return ret; -} + if (ret && instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= mtd->offset; -void mtd_erase_callback(struct erase_info *instr) -{ - if (instr->mtd->_erase == part_erase) { - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= instr->mtd->offset; - instr->addr -= instr->mtd->offset; - } - if (instr->callback) - instr->callback(instr); + instr->addr -= mtd->offset; + + return ret; } -EXPORT_SYMBOL_GPL(mtd_erase_callback); static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 332f9d7591c..df9eae1691c 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -369,7 +369,7 @@ config NAND_MXC imply CMD_NAND help This enables the NAND driver for the NAND flash controller on the - i.MX27 / i.MX31 / i.MX5 rocessors. + i.MX27 / i.MX31 / i.MX5 processors. config NAND_MXS bool "MXS NAND support" @@ -480,7 +480,7 @@ comment "Generic NAND options" config SYS_NAND_BLOCK_SIZE hex "NAND chip eraseblock size" depends on ARCH_SUNXI || SPL_NAND_SUPPORT || TPL_NAND_SUPPORT - depends on !NAND_MXS_DT && !NAND_DENALI_DT && !NAND_LPC32XX_MLC + depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_FSL_IFC help Number of data bytes in one eraseblock for the NAND chip on the board. This is the multiple of NAND_PAGE_SIZE and the number of @@ -505,7 +505,7 @@ config SYS_NAND_PAGE_SIZE depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \ SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \ (NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER - depends on !NAND_MXS_DT && !NAND_DENALI_DT && !NAND_LPC32XX_MLC + depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC help Number of data bytes in one page for the NAND chip on the board, not including the OOB area. @@ -515,7 +515,7 @@ config SYS_NAND_OOBSIZE depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \ SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \ (NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER - depends on !NAND_MXS_DT && !NAND_DENALI_DT && !NAND_LPC32XX_MLC + depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC help Number of bytes in the Out-Of-Band area for the NAND chip on the board. diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c index 6b70d68fe7b..9e0b8afb522 100644 --- a/drivers/mtd/nand/raw/mxs_nand_spl.c +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c @@ -296,3 +296,9 @@ int nand_default_bbt(struct mtd_info *mtd) void nand_deselect(void) { } + +u32 nand_spl_adjust_offset(u32 sector, u32 offs) +{ + /* Handle the offset adjust in nand_spl_load_image,*/ + return offs; +} diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index b533683dfe6..f7616985d95 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -3602,10 +3602,6 @@ erase_exit: chip->select_chip(mtd, -1); nand_release_device(mtd); - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); - /* Return more or less happy */ return ret; } diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index f4a8e816398..6d643a8000d 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -14,6 +14,8 @@ #include <linux/mtd/spinand.h> #define SPINAND_MFR_MACRONIX 0xC2 +#define MACRONIX_ECCSR_MASK 0x0F + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -59,7 +61,13 @@ static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) SPI_MEM_OP_DUMMY(1, 1), SPI_MEM_OP_DATA_IN(1, eccsr, 1)); - return spi_mem_exec_op(spinand->slave, &op); + int ret = spi_mem_exec_op(spinand->slave, &op); + + if (ret) + return ret; + + *eccsr &= MACRONIX_ECCSR_MASK; + return 0; } static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 46aeef258d9..56e1858de45 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1836,9 +1836,6 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); diff --git a/drivers/mtd/spi/sf_mtd.c b/drivers/mtd/spi/sf_mtd.c index 04de8680809..0aed28a52b7 100644 --- a/drivers/mtd/spi/sf_mtd.c +++ b/drivers/mtd/spi/sf_mtd.c @@ -46,7 +46,6 @@ static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) } instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index f1b4e5ea8e3..4388a08a90d 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -908,30 +908,40 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { struct spi_nor *nor = mtd_to_spi_nor(mtd); + bool addr_known = false; u32 addr, len, rem; - int ret; + int ret, err; dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, (long long)instr->len); - if (!instr->len) - return 0; - div_u64_rem(instr->len, mtd->erasesize, &rem); - if (rem) - return -EINVAL; + if (rem) { + ret = -EINVAL; + goto err; + } addr = instr->addr; len = instr->len; + instr->state = MTD_ERASING; + addr_known = true; + while (len) { WATCHDOG_RESET(); + if (ctrlc()) { + addr_known = false; + ret = -EINTR; + goto erase_err; + } #ifdef CONFIG_SPI_FLASH_BAR ret = write_bar(nor, addr); if (ret < 0) - return ret; + goto erase_err; #endif - write_enable(nor); + ret = write_enable(nor); + if (ret < 0) + goto erase_err; ret = spi_nor_erase_sector(nor, addr); if (ret < 0) @@ -945,11 +955,24 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) goto erase_err; } + addr_known = false; erase_err: #ifdef CONFIG_SPI_FLASH_BAR - ret = clean_bar(nor); + err = clean_bar(nor); + if (!ret) + ret = err; #endif - write_disable(nor); + err = write_disable(nor); + if (!ret) + ret = err; + +err: + if (ret) { + instr->fail_addr = addr_known ? addr : MTD_FAIL_ADDR_UNKNOWN; + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + } return ret; } @@ -1665,9 +1688,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); - if (!len) - return 0; - for (i = 0; i < len; ) { ssize_t written; loff_t addr = to + i; @@ -3224,6 +3244,21 @@ static struct spi_nor_fixups s25hx_t_fixups = { .post_bfpt = s25hx_t_post_bfpt_fixup, .post_sfdp = s25hx_t_post_sfdp_fixup, }; + +static int s25fl256l_setup(struct spi_nor *nor, const struct flash_info *info, + const struct spi_nor_flash_parameter *params) +{ + return -ENOTSUPP; /* Bank Address Register is not supported */ +} + +static void s25fl256l_default_init(struct spi_nor *nor) +{ + nor->setup = s25fl256l_setup; +} + +static struct spi_nor_fixups s25fl256l_fixups = { + .default_init = s25fl256l_default_init, +}; #endif #ifdef CONFIG_SPI_FLASH_S28HS512T @@ -3646,6 +3681,10 @@ void spi_nor_set_fixups(struct spi_nor *nor) break; } } + + if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) && + !strcmp(nor->info->name, "s25fl256l")) + nor->fixups = &s25fl256l_fixups; #endif #ifdef CONFIG_SPI_FLASH_S28HS512T diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index 0bff52d5f2a..3ae7bb1ed7e 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -122,6 +122,11 @@ const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + INFO("gd25lq256d", 0xc86019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, #endif #ifdef CONFIG_SPI_FLASH_ISSI /* ISSI */ /* ISSI */ @@ -133,6 +138,8 @@ const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ) }, { INFO("is25lp256", 0x9d6019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ) }, + { INFO("is25lp512", 0x9d601a, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("is25wp032", 0x9d7016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("is25wp064", 0x9d7017, 0, 64 * 1024, 128, @@ -142,6 +149,8 @@ const struct flash_info spi_nor_ids[] = { { INFO("is25wp256", 0x9d7019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("is25wp512", 0x9d701a, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, #endif #ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ /* Macronix */ @@ -186,7 +195,7 @@ const struct flash_info spi_nor_ids[] = { { INFO6("mt25qu256a", 0x20bb19, 0x104400, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_FSR) }, { INFO("n25q256ax1", 0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | USE_FSR) }, { INFO6("mt25qu512a", 0x20bb20, 0x104400, 64 * 1024, 1024, - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_FSR) }, { INFO("n25q512a", 0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { INFO6("mt25ql512a", 0x20ba20, 0x104400, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, @@ -195,6 +204,7 @@ const struct flash_info spi_nor_ids[] = { { INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { INFO("mt25ql01g", 0x21ba20, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { INFO("mt25ql02g", 0x20ba22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE | SPI_NOR_4B_OPCODES) }, #ifdef CONFIG_SPI_FLASH_MT35XU { INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) }, #endif /* CONFIG_SPI_FLASH_MT35XU */ @@ -227,6 +237,7 @@ const struct flash_info spi_nor_ids[] = { { INFO("s25fl208k", 0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) }, { INFO("s25fl064l", 0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("s25fl128l", 0x016018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("s25fl256l", 0x016019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO6("s25hl512t", 0x342a1a, 0x0f0390, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_CLSR) }, diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index b8b878b9182..14be95b74bc 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -305,18 +305,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, } /** - * erase_callback - MTD erasure call-back. - * @ei: MTD erase information object. - * - * Note, even though MTD erase interface is asynchronous, all the current - * implementations are synchronous anyway. - */ -static void erase_callback(struct erase_info *ei) -{ - wake_up_interruptible((wait_queue_head_t *)ei->priv); -} - -/** * do_sync_erase - synchronously erase a physical eraseblock. * @ubi: UBI device description object * @pnum: the physical eraseblock number to erase @@ -346,7 +334,6 @@ retry: ei.mtd = ubi->mtd; ei.addr = (loff_t)pnum * ubi->peb_size; ei.len = ubi->peb_size; - ei.callback = erase_callback; ei.priv = (unsigned long)&wq; err = mtd_erase(ubi->mtd, &ei); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6c12959f379..c1a49173c28 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -344,6 +344,16 @@ config FMAN_ENET help This driver support the Freescale FMan Ethernet controller +config SYS_FMAN_FW_ADDR + hex "FMAN Firmware Address" + depends on FMAN_ENET + default 0x0 + +config SYS_QE_FMAN_FW_LENGTH + hex "FMAN QE Firmware length" + depends on FMAN_ENET || QE || U_QE + default 0x10000 + config FTMAC100 bool "Ftmac100 Ethernet Support" help @@ -440,7 +450,6 @@ config MVPP2 config MACB bool "Cadence MACB/GEM Ethernet Interface" - depends on DM_ETH select PHYLIB help The Cadence MACB ethernet interface is found on many Atmel diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index d52c986d4bd..bc1c31d4675 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -11,6 +11,7 @@ #include <image.h> #include <log.h> #include <malloc.h> +#include <mapmem.h> #include <asm/global_data.h> #include <linux/bug.h> #include <asm/io.h> @@ -38,6 +39,16 @@ #define MC_BOOT_ENV_VAR "mcinitcmd" #define MC_DRAM_BLOCK_DEFAULT_SIZE (512UL * 1024 * 1024) +#define MC_BUFFER_SIZE (1024 * 1024 * 16) +#define MAGIC_MC 0x4d430100 +#define MC_FW_ADDR_MASK_LOW 0xE0000000 +#define MC_FW_ADDR_MASK_HIGH 0X1FFFF +#define MC_STRUCT_BUFFER_OFFSET 0x01000000 +#define MC_OFFSET_DELTA MC_STRUCT_BUFFER_OFFSET + +#define LOG_HEADER_FLAG_BUFFER_WRAPAROUND 0x80000000 +#define LAST_BYTE(a) ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND)) + DECLARE_GLOBAL_DATA_PTR; static int mc_memset_resv_ram; static struct mc_version mc_ver_info; @@ -1773,11 +1784,78 @@ err: return err; } +static void print_k_bytes(const void *buf, ssize_t *size) +{ + while (*size > 0) { + int count = printf("%s", (char *)buf); + + buf += count; + *size -= count; + } +} + +static void mc_dump_log(void) +{ + struct mc_ccsr_registers __iomem *mc_ccsr_regs = MC_CCSR_BASE_ADDR; + u64 high = in_le64(&mc_ccsr_regs->reg_mcfbahr) & MC_FW_ADDR_MASK_HIGH; + u64 low = in_le64(&mc_ccsr_regs->reg_mcfbalr) & MC_FW_ADDR_MASK_LOW; + u32 buf_len, wrapped, last_byte, magic, buf_start; + u64 mc_addr = (high << 32) | low; + struct log_header *header; + ssize_t size, bytes_end; + const void *end_of_data; + const void *map_addr; + const void *end_addr; + const void *cur_ptr; + const void *buf; + + map_addr = map_sysmem(mc_addr + MC_STRUCT_BUFFER_OFFSET, + MC_BUFFER_SIZE); + header = (struct log_header *)map_addr; + last_byte = in_le32(&header->last_byte); + buf_len = in_le32(&header->buf_length); + magic = in_le32(&header->magic_word); + buf_start = in_le32(&header->buf_start); + buf = map_addr + buf_start - MC_OFFSET_DELTA; + end_addr = buf + buf_len; + wrapped = last_byte & LOG_HEADER_FLAG_BUFFER_WRAPAROUND; + end_of_data = buf + LAST_BYTE(last_byte); + + if (magic != MAGIC_MC) { + puts("Magic number is not valid\n"); + printf("expected = %08x, received = %08x\n", MAGIC_MC, magic); + goto err_magic; + } + + if (wrapped && end_of_data != end_addr) + cur_ptr = end_of_data + 1; + else + cur_ptr = buf; + + if (cur_ptr <= end_of_data) + size = end_of_data - cur_ptr; + else + size = (end_addr - cur_ptr) + (end_of_data - buf); + + bytes_end = end_addr - cur_ptr; + if (size > bytes_end) { + print_k_bytes(cur_ptr, &bytes_end); + + cur_ptr = buf; + size -= bytes_end; + } + + print_k_bytes(buf, &size); + +err_magic: + unmap_sysmem(map_addr); +} + static int do_fsl_mc(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { int err = 0; - if (argc < 3) + if (argc < 2) goto usage; switch (argv[1][0]) { @@ -1787,6 +1865,8 @@ static int do_fsl_mc(struct cmd_tbl *cmdtp, int flag, int argc, #ifdef CONFIG_SYS_LS_MC_DRAM_AIOP_IMG_OFFSET u64 aiop_fw_addr; #endif + if (argc < 3) + goto usage; sub_cmd = argv[2][0]; @@ -1918,6 +1998,12 @@ static int do_fsl_mc(struct cmd_tbl *cmdtp, int flag, int argc, } break; } + case 'd': + if (argc > 2) + goto usage; + + mc_dump_log(); + break; default: printf("Invalid option: %s\n", argv[1]); goto usage; @@ -1936,6 +2022,7 @@ U_BOOT_CMD( "fsl_mc lazyapply DPL [DPL_addr] - Apply DPL file on exit\n" "fsl_mc apply spb [spb_addr] - Apply SPB Soft Parser Blob\n" "fsl_mc start aiop [FW_addr] - Start AIOP\n" + "fsl_mc dump_log - Dump MC Log\n" ); void mc_env_boot(void) diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 8151104acfc..8c6461e717b 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -574,14 +574,9 @@ static int macb_phy_find(struct macb_device *macb, const char *name) #ifdef CONFIG_DM_ETH static int macb_sifive_clk_init(struct udevice *dev, ulong rate) { - fdt_addr_t addr; void *gemgxl_regs; - addr = dev_read_addr_index(dev, 1); - if (addr == FDT_ADDR_T_NONE) - return -ENODEV; - - gemgxl_regs = (void __iomem *)addr; + gemgxl_regs = dev_read_addr_index_ptr(dev, 1); if (!gemgxl_regs) return -ENODEV; @@ -1383,7 +1378,7 @@ static int macb_eth_probe(struct udevice *dev) macb->phy_addr = ofnode_read_u32_default(phandle_args.node, "reg", -1); - macb->regs = (void *)pdata->iobase; + macb->regs = (void *)(uintptr_t)pdata->iobase; macb->is_big_endian = (cpu_to_be32(0x12345678) == 0x12345678); @@ -1444,7 +1439,7 @@ static int macb_eth_of_to_plat(struct udevice *dev) { struct eth_pdata *pdata = dev_get_plat(dev); - pdata->iobase = (phys_addr_t)dev_remap_addr(dev); + pdata->iobase = (uintptr_t)dev_remap_addr(dev); if (!pdata->iobase) return -EINVAL; diff --git a/drivers/pci/pci-aardvark.c b/drivers/pci/pci-aardvark.c index 38eff495ab1..4e94b776c5b 100644 --- a/drivers/pci/pci-aardvark.c +++ b/drivers/pci/pci-aardvark.c @@ -445,7 +445,7 @@ static int pcie_advk_read_config(const struct udevice *bus, pci_dev_t bdf, * for returning CRS, so that if U-Boot does support CRS in the future, * it will work for Aardvark. */ - allow_crs = pcie->cfgcrssve; + allow_crs = (offset == PCI_VENDOR_ID) && (size == PCI_SIZE_32) && pcie->cfgcrssve; if (advk_readl(pcie, PIO_START)) { dev_err(pcie->dev, @@ -472,6 +472,9 @@ static int pcie_advk_read_config(const struct udevice *bus, pci_dev_t bdf, advk_writel(pcie, reg, PIO_ADDR_LS); advk_writel(pcie, 0, PIO_ADDR_MS); + /* Program the data strobe */ + advk_writel(pcie, 0xf, PIO_WR_DATA_STRB); + retry_count = 0; retry: @@ -581,6 +584,10 @@ static int pcie_advk_write_config(struct udevice *bus, pci_dev_t bdf, if (offset >= 0x10 && offset < 0x34) { data = pcie->cfgcache[(offset - 0x10) / 4]; data = pci_conv_size_to_32(data, value, offset, size); + /* This PCI bridge does not have configurable bars */ + if ((offset & ~3) == PCI_BASE_ADDRESS_0 || + (offset & ~3) == PCI_BASE_ADDRESS_1) + data = 0x0; pcie->cfgcache[(offset - 0x10) / 4] = data; } else if ((offset & ~3) == PCI_ROM_ADDRESS1) { data = advk_readl(pcie, PCIE_CORE_EXP_ROM_BAR_REG); diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 0c1d7cd770f..14cd82db6ff 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -36,6 +36,7 @@ DECLARE_GLOBAL_DATA_PTR; #define PCIE_DEV_REV_OFF 0x0008 #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) +#define PCIE_EXP_ROM_BAR_OFF 0x0030 #define PCIE_CAPAB_OFF 0x0060 #define PCIE_CTRL_STAT_OFF 0x0068 #define PCIE_HEADER_LOG_4_OFF 0x0128 @@ -52,15 +53,16 @@ DECLARE_GLOBAL_DATA_PTR; #define PCIE_CONF_BUS(b) (((b) & 0xff) << 16) #define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11) #define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8) -#define PCIE_CONF_ADDR(dev, reg) \ - (PCIE_CONF_BUS(PCI_BUS(dev)) | PCIE_CONF_DEV(PCI_DEV(dev)) | \ - PCIE_CONF_FUNC(PCI_FUNC(dev)) | PCIE_CONF_REG(reg) | \ +#define PCIE_CONF_ADDR(b, d, f, reg) \ + (PCIE_CONF_BUS(b) | PCIE_CONF_DEV(d) | \ + PCIE_CONF_FUNC(f) | PCIE_CONF_REG(reg) | \ PCIE_CONF_ADDR_EN) #define PCIE_CONF_DATA_OFF 0x18fc #define PCIE_MASK_OFF 0x1910 #define PCIE_MASK_ENABLE_INTS (0xf << 24) #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE BIT(0) +#define PCIE_CTRL_RC_MODE BIT(1) #define PCIE_STAT_OFF 0x1a04 #define PCIE_STAT_BUS (0xff << 8) #define PCIE_STAT_DEV (0x1f << 16) @@ -80,12 +82,13 @@ struct mvebu_pcie { int devfn; u32 lane_mask; int first_busno; - int local_dev; + int sec_busno; char name[16]; unsigned int mem_target; unsigned int mem_attr; unsigned int io_target; unsigned int io_attr; + u32 cfgcache[0x34 - 0x10]; }; /* @@ -94,7 +97,6 @@ struct mvebu_pcie { * and 64K of I/O space when registered. */ static void __iomem *mvebu_pcie_membase = (void __iomem *)MBUS_PCI_MEM_BASE; -#define PCIE_MEM_SIZE (128 << 20) static void __iomem *mvebu_pcie_iobase = (void __iomem *)MBUS_PCI_IO_BASE; static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) @@ -124,44 +126,27 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie *pcie, int devno) writel(stat, pcie->base + PCIE_STAT_OFF); } -static int mvebu_pcie_get_local_bus_nr(struct mvebu_pcie *pcie) -{ - u32 stat; - - stat = readl(pcie->base + PCIE_STAT_OFF); - return (stat & PCIE_STAT_BUS) >> 8; -} - -static int mvebu_pcie_get_local_dev_nr(struct mvebu_pcie *pcie) -{ - u32 stat; - - stat = readl(pcie->base + PCIE_STAT_OFF); - return (stat & PCIE_STAT_DEV) >> 16; -} - static inline struct mvebu_pcie *hose_to_pcie(struct pci_controller *hose) { return container_of(hose, struct mvebu_pcie, hose); } -static int mvebu_pcie_valid_addr(struct mvebu_pcie *pcie, pci_dev_t bdf) +static bool mvebu_pcie_valid_addr(struct mvebu_pcie *pcie, + int busno, int dev, int func) { - /* - * There are two devices visible on local bus: - * * on slot configured by function mvebu_pcie_set_local_dev_nr() - * (by default this register is set to 0) there is a - * "Marvell Memory controller", which isn't useful in root complex - * mode, - * * on all other slots the real PCIe card connected to the PCIe slot. - * - * We therefore allow access only to the real PCIe card. - */ - if (PCI_BUS(bdf) == pcie->first_busno && - PCI_DEV(bdf) != !pcie->local_dev) - return 0; + /* On primary bus is only one PCI Bridge */ + if (busno == pcie->first_busno && (dev != 0 || func != 0)) + return false; - return 1; + /* Access to other buses is possible when link is up */ + if (busno != pcie->first_busno && !mvebu_pcie_link_up(pcie)) + return false; + + /* On secondary bus can be only one PCIe device */ + if (busno == pcie->sec_busno && dev != 0) + return false; + + return true; } static int mvebu_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, @@ -169,24 +154,77 @@ static int mvebu_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, enum pci_size_t size) { struct mvebu_pcie *pcie = dev_get_plat(bus); - u32 data; + int busno = PCI_BUS(bdf) - dev_seq(bus); + u32 addr, data; debug("PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ", PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); - if (!mvebu_pcie_valid_addr(pcie, bdf)) { + if (!mvebu_pcie_valid_addr(pcie, busno, PCI_DEV(bdf), PCI_FUNC(bdf))) { debug("- out of range\n"); *valuep = pci_get_ff(size); return 0; } + /* + * mvebu has different internal registers mapped into PCI config space + * in range 0x10-0x34 for PCI bridge, so do not access PCI config space + * for this range and instead read content from driver virtual cfgcache + */ + if (busno == pcie->first_busno && offset >= 0x10 && offset < 0x34) { + data = pcie->cfgcache[(offset - 0x10) / 4]; + debug("(addr,size,val)=(0x%04x, %d, 0x%08x) from cfgcache\n", + offset, size, data); + *valuep = pci_conv_32_to_size(data, offset, size); + return 0; + } else if (busno == pcie->first_busno && + (offset & ~3) == PCI_ROM_ADDRESS1) { + /* mvebu has Expansion ROM Base Address (0x38) at offset 0x30 */ + offset -= PCI_ROM_ADDRESS1 - PCIE_EXP_ROM_BAR_OFF; + } + + /* + * PCI bridge is device 0 at primary bus but mvebu has it mapped on + * secondary bus with device number 1. + */ + if (busno == pcie->first_busno) + addr = PCIE_CONF_ADDR(pcie->sec_busno, 1, 0, offset); + else + addr = PCIE_CONF_ADDR(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); + /* write address */ - writel(PCIE_CONF_ADDR(bdf, offset), pcie->base + PCIE_CONF_ADDR_OFF); + writel(addr, pcie->base + PCIE_CONF_ADDR_OFF); /* read data */ - data = readl(pcie->base + PCIE_CONF_DATA_OFF); + switch (size) { + case PCI_SIZE_8: + data = readb(pcie->base + PCIE_CONF_DATA_OFF + (offset & 3)); + break; + case PCI_SIZE_16: + data = readw(pcie->base + PCIE_CONF_DATA_OFF + (offset & 2)); + break; + case PCI_SIZE_32: + data = readl(pcie->base + PCIE_CONF_DATA_OFF); + break; + default: + return -EINVAL; + } + + if (busno == pcie->first_busno && + (offset & ~3) == (PCI_HEADER_TYPE & ~3)) { + /* + * Change Header Type of PCI Bridge device to Type 1 + * (0x01, used by PCI Bridges) because mvebu reports + * Type 0 (0x00, used by Upstream and Endpoint devices). + */ + data = pci_conv_size_to_32(data, 0, offset, size); + data &= ~0x007f0000; + data |= PCI_HEADER_TYPE_BRIDGE << 16; + data = pci_conv_32_to_size(data, offset, size); + } + debug("(addr,size,val)=(0x%04x, %d, 0x%08x)\n", offset, size, data); - *valuep = pci_conv_32_to_size(data, offset, size); + *valuep = data; return 0; } @@ -196,23 +234,79 @@ static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf, enum pci_size_t size) { struct mvebu_pcie *pcie = dev_get_plat(bus); - u32 data; + int busno = PCI_BUS(bdf) - dev_seq(bus); + u32 addr, data; debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ", PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); debug("(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", offset, size, value); - if (!mvebu_pcie_valid_addr(pcie, bdf)) { + if (!mvebu_pcie_valid_addr(pcie, busno, PCI_DEV(bdf), PCI_FUNC(bdf))) { debug("- out of range\n"); return 0; } + /* + * mvebu has different internal registers mapped into PCI config space + * in range 0x10-0x34 for PCI bridge, so do not access PCI config space + * for this range and instead write content to driver virtual cfgcache + */ + if (busno == pcie->first_busno && offset >= 0x10 && offset < 0x34) { + debug("Writing to cfgcache only\n"); + data = pcie->cfgcache[(offset - 0x10) / 4]; + data = pci_conv_size_to_32(data, value, offset, size); + /* mvebu PCI bridge does not have configurable bars */ + if ((offset & ~3) == PCI_BASE_ADDRESS_0 || + (offset & ~3) == PCI_BASE_ADDRESS_1) + data = 0x0; + pcie->cfgcache[(offset - 0x10) / 4] = data; + /* mvebu has its own way how to set PCI primary bus number */ + if (offset == PCI_PRIMARY_BUS) { + pcie->first_busno = data & 0xff; + debug("Primary bus number was changed to %d\n", + pcie->first_busno); + } + /* mvebu has its own way how to set PCI secondary bus number */ + if (offset == PCI_SECONDARY_BUS || + (offset == PCI_PRIMARY_BUS && size != PCI_SIZE_8)) { + pcie->sec_busno = (data >> 8) & 0xff; + mvebu_pcie_set_local_bus_nr(pcie, pcie->sec_busno); + debug("Secondary bus number was changed to %d\n", + pcie->sec_busno); + } + return 0; + } else if (busno == pcie->first_busno && + (offset & ~3) == PCI_ROM_ADDRESS1) { + /* mvebu has Expansion ROM Base Address (0x38) at offset 0x30 */ + offset -= PCI_ROM_ADDRESS1 - PCIE_EXP_ROM_BAR_OFF; + } + + /* + * PCI bridge is device 0 at primary bus but mvebu has it mapped on + * secondary bus with device number 1. + */ + if (busno == pcie->first_busno) + addr = PCIE_CONF_ADDR(pcie->sec_busno, 1, 0, offset); + else + addr = PCIE_CONF_ADDR(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); + /* write address */ - writel(PCIE_CONF_ADDR(bdf, offset), pcie->base + PCIE_CONF_ADDR_OFF); + writel(addr, pcie->base + PCIE_CONF_ADDR_OFF); /* write data */ - data = pci_conv_size_to_32(0, value, offset, size); - writel(data, pcie->base + PCIE_CONF_DATA_OFF); + switch (size) { + case PCI_SIZE_8: + writeb(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 3)); + break; + case PCI_SIZE_16: + writew(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 2)); + break; + case PCI_SIZE_32: + writel(value, pcie->base + PCIE_CONF_DATA_OFF); + break; + default: + return -EINVAL; + } return 0; } @@ -277,32 +371,75 @@ static int mvebu_pcie_probe(struct udevice *dev) struct mvebu_pcie *pcie = dev_get_plat(dev); struct udevice *ctlr = pci_get_controller(dev); struct pci_controller *hose = dev_get_uclass_priv(ctlr); - int bus = dev_seq(dev); u32 reg; - debug("%s: PCIe %d.%d - up, base %08x\n", __func__, - pcie->port, pcie->lane, (u32)pcie->base); - - /* Read Id info and local bus/dev */ - debug("direct conf read %08x, local bus %d, local dev %d\n", - readl(pcie->base), mvebu_pcie_get_local_bus_nr(pcie), - mvebu_pcie_get_local_dev_nr(pcie)); + /* Setup PCIe controller to Root Complex mode */ + reg = readl(pcie->base + PCIE_CTRL_OFF); + reg |= PCIE_CTRL_RC_MODE; + writel(reg, pcie->base + PCIE_CTRL_OFF); - pcie->first_busno = bus; - pcie->local_dev = 1; + /* + * Change Class Code of PCI Bridge device to PCI Bridge (0x600400) + * because default value is Memory controller (0x508000) which + * U-Boot cannot recognize as P2P Bridge. + * + * Note that this mvebu PCI Bridge does not have compliant Type 1 + * Configuration Space. Header Type is reported as Type 0 and in + * range 0x10-0x34 it has aliased internal mvebu registers 0x10-0x34 + * (e.g. PCIE_BAR_LO_OFF) and register 0x38 is reserved. + * + * Driver for this range redirects access to virtual cfgcache[] buffer + * which avoids changing internal mvebu registers. And changes Header + * Type response value to Type 1. + */ + reg = readl(pcie->base + PCIE_DEV_REV_OFF); + reg &= ~0xffffff00; + reg |= (PCI_CLASS_BRIDGE_PCI << 8) << 8; + writel(reg, pcie->base + PCIE_DEV_REV_OFF); - mvebu_pcie_set_local_bus_nr(pcie, bus); - mvebu_pcie_set_local_dev_nr(pcie, pcie->local_dev); + /* + * mvebu uses local bus number and local device number to determinate + * type of config request. Type 0 is used if target bus number equals + * local bus number and target device number differs from local device + * number. Type 1 is used if target bus number differs from local bus + * number. And when target bus number equals local bus number and + * target device equals local device number then request is routed to + * PCI Bridge which represent local PCIe Root Port. + * + * It means that PCI primary and secondary buses shares one bus number + * which is configured via local bus number. Determination if config + * request should go to primary or secondary bus is done based on local + * device number. + * + * PCIe is point-to-point bus, so at secondary bus is always exactly one + * device with number 0. So set local device number to 1, it would not + * conflict with any device on secondary bus number and will ensure that + * accessing secondary bus and all buses behind secondary would work + * automatically and correctly. Therefore this configuration of local + * device number implies that setting of local bus number configures + * secondary bus number. Set it to 0 as U-Boot CONFIG_PCI_PNP code will + * later configure it via config write requests to the correct value. + * mvebu_pcie_write_config() catches config write requests which tries + * to change primary/secondary bus number and correctly updates local + * bus number based on new secondary bus number. + * + * With this configuration is PCI Bridge available at secondary bus as + * device number 1. But it must be available at primary bus as device + * number 0. So in mvebu_pcie_read_config() and mvebu_pcie_write_config() + * functions rewrite address to the real one when accessing primary bus. + */ + mvebu_pcie_set_local_bus_nr(pcie, 0); + mvebu_pcie_set_local_dev_nr(pcie, 1); pcie->mem.start = (u32)mvebu_pcie_membase; - pcie->mem.end = pcie->mem.start + PCIE_MEM_SIZE - 1; - mvebu_pcie_membase += PCIE_MEM_SIZE; + pcie->mem.end = pcie->mem.start + MBUS_PCI_MEM_SIZE - 1; + mvebu_pcie_membase += MBUS_PCI_MEM_SIZE; if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, (phys_addr_t)pcie->mem.start, - PCIE_MEM_SIZE)) { + MBUS_PCI_MEM_SIZE)) { printf("PCIe unable to add mbus window for mem at %08x+%08x\n", - (u32)pcie->mem.start, PCIE_MEM_SIZE); + (u32)pcie->mem.start, MBUS_PCI_MEM_SIZE); } pcie->io.start = (u32)mvebu_pcie_iobase; @@ -319,17 +456,9 @@ static int mvebu_pcie_probe(struct udevice *dev) /* Setup windows and configure host bridge */ mvebu_pcie_setup_wins(pcie); - /* Master + slave enable. */ - reg = readl(pcie->base + PCIE_CMD_OFF); - reg |= PCI_COMMAND_MEMORY; - reg |= PCI_COMMAND_IO; - reg |= PCI_COMMAND_MASTER; - reg |= BIT(10); /* disable interrupts */ - writel(reg, pcie->base + PCIE_CMD_OFF); - /* PCI memory space */ pci_set_region(hose->regions + 0, pcie->mem.start, - pcie->mem.start, PCIE_MEM_SIZE, PCI_REGION_MEM); + pcie->mem.start, MBUS_PCI_MEM_SIZE, PCI_REGION_MEM); pci_set_region(hose->regions + 1, 0, 0, gd->ram_size, @@ -342,6 +471,12 @@ static int mvebu_pcie_probe(struct udevice *dev) writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0)); writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); + /* PCI Bridge support 32-bit I/O and 64-bit prefetch mem addressing */ + pcie->cfgcache[(PCI_IO_BASE - 0x10) / 4] = + PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8); + pcie->cfgcache[(PCI_PREF_MEMORY_BASE - 0x10) / 4] = + PCI_PREF_RANGE_TYPE_64 | (PCI_PREF_RANGE_TYPE_64 << 16); + return 0; } @@ -466,13 +601,6 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) if (ret < 0) goto err; - /* Check link and skip ports that have no link */ - if (!mvebu_pcie_link_up(pcie)) { - debug("%s: %s - down\n", __func__, pcie->name); - ret = -ENODEV; - goto err; - } - return 0; err: @@ -504,7 +632,7 @@ static int mvebu_pcie_bind(struct udevice *parent) struct udevice *dev; ofnode subnode; - /* Lookup eth driver */ + /* Lookup pci driver */ drv = lists_uclass_lookup(UCLASS_PCI); if (!drv) { puts("Cannot find PCI driver\n"); diff --git a/drivers/pci/pcie_layerscape_fixup.c b/drivers/pci/pcie_layerscape_fixup.c index a58e7a3892a..8a2a0e1f4a9 100644 --- a/drivers/pci/pcie_layerscape_fixup.c +++ b/drivers/pci/pcie_layerscape_fixup.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright 2017-2020 NXP + * Copyright 2017-2021 NXP * Copyright 2014-2015 Freescale Semiconductor, Inc. * Layerscape PCIe driver */ @@ -24,6 +24,8 @@ #include "pcie_layerscape.h" #include "pcie_layerscape_fixup_common.h" +int next_stream_id; + static int fdt_pcie_get_nodeoffset(void *blob, struct ls_pcie_rc *pcie_rc) { int nodeoffset; @@ -607,6 +609,9 @@ static void ft_pcie_ls_setup(void *blob, struct ls_pcie_rc *pcie_rc) { ft_pcie_ep_fix(blob, pcie_rc); ft_pcie_rc_fix(blob, pcie_rc); + + pcie_rc->stream_id_cur = 0; + pcie_rc->next_lut_index = 0; } /* Fixup Kernel DT for PCIe */ @@ -618,6 +623,7 @@ void ft_pci_setup_ls(void *blob, struct bd_info *bd) ft_pcie_ls_setup(blob, pcie_rc); #if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2) + next_stream_id = FSL_PEX_STREAM_ID_START; fdt_fixup_pcie_ls(blob); #endif } diff --git a/drivers/pci/pcie_layerscape_fixup_common.c b/drivers/pci/pcie_layerscape_fixup_common.c index 3216a20027a..faccf6c141f 100644 --- a/drivers/pci/pcie_layerscape_fixup_common.c +++ b/drivers/pci/pcie_layerscape_fixup_common.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright 2019-2020 NXP + * Copyright 2019-2021 NXP * * PCIe DT fixup for NXP Layerscape SoCs * Author: Wasim Khan <wasim.khan@nxp.com> @@ -15,6 +15,8 @@ #include <fdt_support.h> #include "pcie_layerscape_fixup_common.h" +extern int next_stream_id; + void ft_pci_setup(void *blob, struct bd_info *bd) { #if defined(CONFIG_PCIE_LAYERSCAPE_GEN4) @@ -147,8 +149,6 @@ int pcie_next_streamid(int currentid, int idx) /* returns the next available streamid for pcie, -errno if failed */ int pcie_next_streamid(int currentid, int idx) { - static int next_stream_id = FSL_PEX_STREAM_ID_START; - if (next_stream_id > FSL_PEX_STREAM_ID_END) return -EINVAL; diff --git a/drivers/pci/pcie_layerscape_gen4.c b/drivers/pci/pcie_layerscape_gen4.c index 255e73181d5..6ecdd6af408 100644 --- a/drivers/pci/pcie_layerscape_gen4.c +++ b/drivers/pci/pcie_layerscape_gen4.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ OR X11 /* - * Copyright 2018-2020 NXP + * Copyright 2018-2021 NXP * * PCIe Gen4 driver for NXP Layerscape SoCs * Author: Hou Zhiqiang <Minder.Hou@gmail.com> @@ -305,8 +305,6 @@ static void ls_pcie_g4_setup_ctrl(struct ls_pcie_g4 *pcie) ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val); ls_pcie_g4_setup_wins(pcie); - - pcie->stream_id_cur = 0; } static void ls_pcie_g4_ep_inbound_win_set(struct ls_pcie_g4 *pcie, int pf, diff --git a/drivers/pci/pcie_layerscape_gen4_fixup.c b/drivers/pci/pcie_layerscape_gen4_fixup.c index e9ee15558e5..7d112341061 100644 --- a/drivers/pci/pcie_layerscape_gen4_fixup.c +++ b/drivers/pci/pcie_layerscape_gen4_fixup.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ OR X11 /* - * Copyright 2018-2020 NXP + * Copyright 2018-2021 NXP * * PCIe Gen4 driver for NXP Layerscape SoCs * Author: Hou Zhiqiang <Minder.Hou@gmail.com> @@ -223,6 +223,9 @@ static void ft_pcie_layerscape_gen4_setup(void *blob, struct ls_pcie_g4 *pcie) { ft_pcie_rc_layerscape_gen4_fix(blob, pcie); ft_pcie_ep_layerscape_gen4_fix(blob, pcie); + + pcie->stream_id_cur = 0; + pcie->next_lut_index = 0; } /* Fixup Kernel DT for PCIe */ diff --git a/drivers/pci/pcie_layerscape_rc.c b/drivers/pci/pcie_layerscape_rc.c index f50d6ef6539..17969e2f236 100644 --- a/drivers/pci/pcie_layerscape_rc.c +++ b/drivers/pci/pcie_layerscape_rc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright 2020 NXP + * Copyright 2020,2021 NXP * Layerscape PCIe driver */ @@ -21,6 +21,12 @@ DECLARE_GLOBAL_DATA_PTR; +struct ls_pcie_drvdata { + u32 lut_offset; + u32 ctrl_offset; + bool big_endian; +}; + static void ls_pcie_cfg0_set_busdev(struct ls_pcie_rc *pcie_rc, u32 busdev) { struct ls_pcie *pcie = pcie_rc->pcie; @@ -238,11 +244,11 @@ static void ls_pcie_setup_ctrl(struct ls_pcie_rc *pcie_rc) ls_pcie_dbi_ro_wr_dis(pcie); ls_pcie_disable_bars(pcie_rc); - pcie_rc->stream_id_cur = 0; } static int ls_pcie_probe(struct udevice *dev) { + const struct ls_pcie_drvdata *drvdata = (void *)dev_get_driver_data(dev); struct ls_pcie_rc *pcie_rc = dev_get_priv(dev); const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); @@ -260,8 +266,12 @@ static int ls_pcie_probe(struct udevice *dev) pcie_rc->pcie = pcie; + /* try resource name of the official binding first */ ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", - "dbi", &pcie_rc->dbi_res); + "regs", &pcie_rc->dbi_res); + if (ret) + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "dbi", &pcie_rc->dbi_res); if (ret) { printf("ls-pcie: resource \"dbi\" not found\n"); return ret; @@ -287,21 +297,29 @@ static int ls_pcie_probe(struct udevice *dev) if (pcie->mode == PCI_HEADER_TYPE_NORMAL) return 0; - ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", - "lut", &pcie_rc->lut_res); - if (!ret) - pcie->lut = map_physmem(pcie_rc->lut_res.start, - fdt_resource_size(&pcie_rc->lut_res), - MAP_NOCACHE); + if (drvdata) { + pcie->lut = pcie->dbi + drvdata->lut_offset; + } else { + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "lut", &pcie_rc->lut_res); + if (!ret) + pcie->lut = map_physmem(pcie_rc->lut_res.start, + fdt_resource_size(&pcie_rc->lut_res), + MAP_NOCACHE); + } - ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", - "ctrl", &pcie_rc->ctrl_res); - if (!ret) - pcie->ctrl = map_physmem(pcie_rc->ctrl_res.start, - fdt_resource_size(&pcie_rc->ctrl_res), - MAP_NOCACHE); - if (!pcie->ctrl) - pcie->ctrl = pcie->lut; + if (drvdata) { + pcie->ctrl = pcie->lut + drvdata->ctrl_offset; + } else { + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "ctrl", &pcie_rc->ctrl_res); + if (!ret) + pcie->ctrl = map_physmem(pcie_rc->ctrl_res.start, + fdt_resource_size(&pcie_rc->ctrl_res), + MAP_NOCACHE); + if (!pcie->ctrl) + pcie->ctrl = pcie->lut; + } if (!pcie->ctrl) { printf("%s: NOT find CTRL\n", dev->name); @@ -343,7 +361,10 @@ static int ls_pcie_probe(struct udevice *dev) pcie_rc->cfg1 = pcie_rc->cfg0 + fdt_resource_size(&pcie_rc->cfg_res) / 2; - pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian"); + if (drvdata) + pcie->big_endian = drvdata->big_endian; + else + pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian"); debug("%s dbi:%lx lut:%lx ctrl:0x%lx cfg0:0x%lx, big-endian:%d\n", dev->name, (unsigned long)pcie->dbi, (unsigned long)pcie->lut, @@ -373,8 +394,15 @@ static const struct dm_pci_ops ls_pcie_ops = { .write_config = ls_pcie_write_config, }; +static const struct ls_pcie_drvdata ls1028a_drvdata = { + .lut_offset = 0x80000, + .ctrl_offset = 0x40000, + .big_endian = false, +}; + static const struct udevice_id ls_pcie_ids[] = { { .compatible = "fsl,ls-pcie" }, + { .compatible = "fsl,ls1028a-pcie", .data = (ulong)&ls1028a_drvdata }, { } }; diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index 6bfb79cbcad..d3ff82f73a0 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -4,6 +4,7 @@ config PHY_SUN4I_USB bool "Allwinner Sun4I USB PHY driver" depends on ARCH_SUNXI + select DM_REGULATOR select PHY help Enable this to support the transceiver that is part of Allwinner diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 82713b83815..ab2a5d17fcf 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -26,6 +26,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/err.h> +#include <power/regulator.h> #define REG_ISCR 0x00 #define REG_PHYCTL_A10 0x04 @@ -137,6 +138,7 @@ struct sun4i_usb_phy_data { void __iomem *base; const struct sun4i_usb_phy_cfg *cfg; struct sun4i_usb_phy_plat *usb_phy; + struct udevice *vbus_power_supply; }; static int initial_usb_scan_delay = CONFIG_INITIAL_USB_SCAN_DELAY; @@ -391,22 +393,21 @@ int sun4i_usb_phy_vbus_detect(struct phy *phy) { struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; - int err, retries = 3; + int err = 1, retries = 3; - debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); - - if (usb_phy->gpio_vbus_det < 0) - return usb_phy->gpio_vbus_det; - - err = gpio_get_value(usb_phy->gpio_vbus_det); - /* - * Vbus may have been provided by the board and just been turned of - * some milliseconds ago on reset, what we're measuring then is a - * residual charge on Vbus, sleep a bit and try again. - */ - while (err > 0 && retries--) { - mdelay(100); + if (usb_phy->gpio_vbus_det >= 0) { err = gpio_get_value(usb_phy->gpio_vbus_det); + /* + * Vbus may have been provided by the board and just turned off + * some milliseconds ago on reset. What we're measuring then is + * a residual charge on Vbus. Sleep a bit and try again. + */ + while (err > 0 && retries--) { + mdelay(100); + err = gpio_get_value(usb_phy->gpio_vbus_det); + } + } else if (data->vbus_power_supply) { + err = regulator_get_enable(data->vbus_power_supply); } return err; @@ -417,8 +418,6 @@ int sun4i_usb_phy_id_detect(struct phy *phy) struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; - debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); - if (usb_phy->gpio_id_det < 0) return usb_phy->gpio_id_det; @@ -452,6 +451,9 @@ static int sun4i_usb_phy_probe(struct udevice *dev) if (IS_ERR(data->base)) return PTR_ERR(data->base); + device_get_supply_regulator(dev, "usb0_vbus_power-supply", + &data->vbus_power_supply); + data->usb_phy = plat; for (i = 0; i < data->cfg->num_phys; i++) { struct sun4i_usb_phy_plat *phy = &plat[i]; diff --git a/drivers/pinctrl/exynos/Kconfig b/drivers/pinctrl/exynos/Kconfig index 84b6aaae09c..a60f49869b4 100644 --- a/drivers/pinctrl/exynos/Kconfig +++ b/drivers/pinctrl/exynos/Kconfig @@ -8,3 +8,11 @@ config PINCTRL_EXYNOS7420 help Support pin multiplexing and pin configuration control on Samsung's Exynos7420 SoC. + +config PINCTRL_EXYNOS78x0 + bool "Samsung Exynos78x0 pinctrl driver" + depends on ARCH_EXYNOS && PINCTRL_FULL + select PINCTRL_EXYNOS + help + Support pin multiplexing and pin configuration control on + Samsung's Exynos78x0 SoC. diff --git a/drivers/pinctrl/exynos/Makefile b/drivers/pinctrl/exynos/Makefile index 6a14a474bf2..07db970ca94 100644 --- a/drivers/pinctrl/exynos/Makefile +++ b/drivers/pinctrl/exynos/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o obj-$(CONFIG_PINCTRL_EXYNOS7420) += pinctrl-exynos7420.o +obj-$(CONFIG_PINCTRL_EXYNOS78x0) += pinctrl-exynos78x0.o diff --git a/drivers/pinctrl/exynos/pinctrl-exynos.c b/drivers/pinctrl/exynos/pinctrl-exynos.c index 2640c8fcefc..898185479ba 100644 --- a/drivers/pinctrl/exynos/pinctrl-exynos.c +++ b/drivers/pinctrl/exynos/pinctrl-exynos.c @@ -5,6 +5,7 @@ * Thomas Abraham <thomas.ab@samsung.com> */ +#include <log.h> #include <common.h> #include <dm.h> #include <errno.h> @@ -38,9 +39,9 @@ static unsigned long pin_to_bank_base(struct udevice *dev, const char *pin_name, u32 *pin) { struct exynos_pinctrl_priv *priv = dev_get_priv(dev); - const struct samsung_pin_ctrl *pin_ctrl = priv->pin_ctrl; - const struct samsung_pin_bank_data *bank_data = pin_ctrl->pin_banks; - u32 nr_banks = pin_ctrl->nr_banks, idx = 0; + const struct samsung_pin_ctrl *pin_ctrl_array = priv->pin_ctrl; + const struct samsung_pin_bank_data *bank_data; + u32 nr_banks, pin_ctrl_idx = 0, idx = 0, bank_base; char bank[10]; /* @@ -55,11 +56,26 @@ static unsigned long pin_to_bank_base(struct udevice *dev, const char *pin_name, *pin = pin_name[++idx] - '0'; /* lookup the pin bank data using the pin bank name */ - for (idx = 0; idx < nr_banks; idx++) - if (!strcmp(bank, bank_data[idx].name)) + while (true) { + const struct samsung_pin_ctrl *pin_ctrl = &pin_ctrl_array[pin_ctrl_idx]; + + nr_banks = pin_ctrl->nr_banks; + if (!nr_banks) break; - return priv->base + bank_data[idx].offset; + bank_data = pin_ctrl->pin_banks; + for (idx = 0; idx < nr_banks; idx++) { + debug("pinctrl[%d] bank_data[%d] name is: %s\n", + pin_ctrl_idx, idx, bank_data[idx].name); + if (!strcmp(bank, bank_data[idx].name)) { + bank_base = priv->base + bank_data[idx].offset; + break; + } + } + pin_ctrl_idx++; + } + + return bank_base; } /** diff --git a/drivers/pinctrl/exynos/pinctrl-exynos78x0.c b/drivers/pinctrl/exynos/pinctrl-exynos78x0.c new file mode 100644 index 00000000000..1b696fdfd28 --- /dev/null +++ b/drivers/pinctrl/exynos/pinctrl-exynos78x0.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Exynos78x0 pinctrl driver. + * + * Copyright (c) 2020 Dzmitry Sankouski (dsankouski@gmail.com) + * + * based on drivers/pinctrl/exynos/pinctrl-exynos7420.c : + * Copyright (C) 2016 Samsung Electronics + * Thomas Abraham <thomas.ab@samsung.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <dm/pinctrl.h> +#include <dm/root.h> +#include <fdtdec.h> +#include <asm/arch/pinmux.h> +#include "pinctrl-exynos.h" + +static const struct pinctrl_ops exynos78x0_pinctrl_ops = { + .set_state = exynos_pinctrl_set_state +}; + +/* pin banks of exynos78x0 pin-controller 0 (ALIVE) */ +static const struct samsung_pin_bank_data exynos78x0_pin_banks0[] = { + EXYNOS_PIN_BANK(6, 0x000, "etc0"), + EXYNOS_PIN_BANK(3, 0x020, "etc1"), + EXYNOS_PIN_BANK(8, 0x040, "gpa0"), + EXYNOS_PIN_BANK(8, 0x060, "gpa1"), + EXYNOS_PIN_BANK(8, 0x080, "gpa2"), + EXYNOS_PIN_BANK(5, 0x0a0, "gpa3"), + EXYNOS_PIN_BANK(2, 0x0c0, "gpq0"), +}; + +/* pin banks of exynos78x0 pin-controller 1 (CCORE) */ +static const struct samsung_pin_bank_data exynos78x0_pin_banks1[] = { + EXYNOS_PIN_BANK(2, 0x000, "gpm0"), +}; + +/* pin banks of exynos78x0 pin-controller 2 (DISPAUD) */ +static const struct samsung_pin_bank_data exynos78x0_pin_banks2[] = { + EXYNOS_PIN_BANK(4, 0x000, "gpz0"), + EXYNOS_PIN_BANK(6, 0x020, "gpz1"), + EXYNOS_PIN_BANK(4, 0x040, "gpz2"), +}; + +/* pin banks of exynos78x0 pin-controller 4 (FSYS) */ +static const struct samsung_pin_bank_data exynos78x0_pin_banks4[] = { + EXYNOS_PIN_BANK(3, 0x000, "gpr0"), + EXYNOS_PIN_BANK(8, 0x020, "gpr1"), + EXYNOS_PIN_BANK(2, 0x040, "gpr2"), + EXYNOS_PIN_BANK(4, 0x060, "gpr3"), + EXYNOS_PIN_BANK(6, 0x080, "gpr4"), +}; + +/* pin banks of exynos78x0 pin-controller 6 (TOP) */ +static const struct samsung_pin_bank_data exynos78x0_pin_banks6[] = { + EXYNOS_PIN_BANK(4, 0x000, "gpb0"), + EXYNOS_PIN_BANK(3, 0x020, "gpc0"), + EXYNOS_PIN_BANK(4, 0x040, "gpc1"), + EXYNOS_PIN_BANK(4, 0x060, "gpc4"), + EXYNOS_PIN_BANK(2, 0x080, "gpc5"), + EXYNOS_PIN_BANK(4, 0x0a0, "gpc6"), + EXYNOS_PIN_BANK(2, 0x0c0, "gpc8"), + EXYNOS_PIN_BANK(2, 0x0e0, "gpc9"), + EXYNOS_PIN_BANK(7, 0x100, "gpd1"), + EXYNOS_PIN_BANK(6, 0x120, "gpd2"), + EXYNOS_PIN_BANK(8, 0x140, "gpd3"), + EXYNOS_PIN_BANK(7, 0x160, "gpd4"), + EXYNOS_PIN_BANK(5, 0x180, "gpd5"), + EXYNOS_PIN_BANK(3, 0x1a0, "gpe0"), + EXYNOS_PIN_BANK(4, 0x1c0, "gpf0"), + EXYNOS_PIN_BANK(2, 0x1e0, "gpf1"), + EXYNOS_PIN_BANK(2, 0x200, "gpf2"), + EXYNOS_PIN_BANK(4, 0x220, "gpf3"), + EXYNOS_PIN_BANK(5, 0x240, "gpf4"), +}; + +const struct samsung_pin_ctrl exynos78x0_pin_ctrl[] = { + { + /* pin-controller instance 0 Alive data */ + .pin_banks = exynos78x0_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos78x0_pin_banks0), + }, { + /* pin-controller instance 1 CCORE data */ + .pin_banks = exynos78x0_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos78x0_pin_banks1), + }, { + /* pin-controller instance 2 DISPAUD data */ + .pin_banks = exynos78x0_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos78x0_pin_banks2), + }, { + /* pin-controller instance 4 FSYS data */ + .pin_banks = exynos78x0_pin_banks4, + .nr_banks = ARRAY_SIZE(exynos78x0_pin_banks4), + }, { + /* pin-controller instance 6 TOP data */ + .pin_banks = exynos78x0_pin_banks6, + .nr_banks = ARRAY_SIZE(exynos78x0_pin_banks6), + }, + {/* list terminator */} +}; + +static const struct udevice_id exynos78x0_pinctrl_ids[] = { + { .compatible = "samsung,exynos78x0-pinctrl", + .data = (ulong)exynos78x0_pin_ctrl }, + { } +}; + +U_BOOT_DRIVER(pinctrl_exynos78x0) = { + .name = "pinctrl_exynos78x0", + .id = UCLASS_PINCTRL, + .of_match = exynos78x0_pinctrl_ids, + .priv_auto = sizeof(struct exynos_pinctrl_priv), + .ops = &exynos78x0_pinctrl_ops, + .probe = exynos_pinctrl_probe, +}; diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index 6c98538f562..5729799b122 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -10,7 +10,6 @@ #include <hwspinlock.h> #include <log.h> #include <malloc.h> -#include <asm/arch/gpio.h> #include <asm/gpio.h> #include <asm/io.h> #include <dm/device_compat.h> @@ -20,6 +19,8 @@ #include <linux/err.h> #include <linux/libfdt.h> +#include "../gpio/stm32_gpio_priv.h" + #define MAX_PINS_ONE_IP 70 #define MODE_BITS_MASK 3 #define OSPEED_MASK 3 diff --git a/drivers/power/axp152.c b/drivers/power/axp152.c index d6e36125c12..a93987c1538 100644 --- a/drivers/power/axp152.c +++ b/drivers/power/axp152.c @@ -79,6 +79,7 @@ int axp_init(void) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP152_SHUTDOWN, AXP152_POWEROFF); @@ -89,3 +90,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp209.c b/drivers/power/axp209.c index ade531940b9..3447b9f0113 100644 --- a/drivers/power/axp209.c +++ b/drivers/power/axp209.c @@ -230,6 +230,7 @@ int axp_init(void) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP209_SHUTDOWN, AXP209_POWEROFF); @@ -240,3 +241,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 3446fe7365d..d251c314b98 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -264,6 +264,7 @@ int axp_get_sid(unsigned int *sid) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP221_SHUTDOWN, AXP221_SHUTDOWN_POWEROFF); @@ -274,3 +275,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp305.c b/drivers/power/axp305.c index 0191e4d427e..049ef07f746 100644 --- a/drivers/power/axp305.c +++ b/drivers/power/axp305.c @@ -69,7 +69,7 @@ int axp_init(void) return ret; } -#ifndef CONFIG_PSCI_RESET +#if !CONFIG_IS_ENABLED(ARM_PSCI_FW) && !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP305_SHUTDOWN, AXP305_POWEROFF); diff --git a/drivers/power/axp809.c b/drivers/power/axp809.c index 0396502cdb5..d327a584ded 100644 --- a/drivers/power/axp809.c +++ b/drivers/power/axp809.c @@ -219,6 +219,7 @@ int axp_init(void) return pmic_bus_init(); } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP809_SHUTDOWN, AXP809_SHUTDOWN_POWEROFF); @@ -229,3 +230,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp818.c b/drivers/power/axp818.c index 2dc736467bb..08286ea3b55 100644 --- a/drivers/power/axp818.c +++ b/drivers/power/axp818.c @@ -255,6 +255,7 @@ int axp_init(void) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP818_SHUTDOWN, AXP818_SHUTDOWN_POWEROFF); @@ -265,3 +266,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 92e2ace279d..b9fda428df9 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -66,6 +66,8 @@ config PMIC_ACT8846 config PMIC_AXP bool "Enable Driver Model for X-Powers AXP PMICs" depends on DM_I2C + select SYSRESET_CMD_POWEROFF if SYSRESET && CMD_POWEROFF + imply CMD_POWEROFF if SYSRESET help This config enables driver-model PMIC uclass features for X-Powers AXP152, AXP2xx, and AXP8xx PMICs. diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index 74c94bdb47b..0f2b24a8b5f 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -1,8 +1,37 @@ // SPDX-License-Identifier: GPL-2.0+ +#include <axp_pmic.h> #include <dm.h> +#include <dm/lists.h> #include <i2c.h> #include <power/pmic.h> +#include <sysreset.h> + +#if CONFIG_IS_ENABLED(SYSRESET) +static int axp_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + int ret; + + if (type != SYSRESET_POWER_OFF) + return -EPROTONOSUPPORT; + + ret = pmic_clrsetbits(dev->parent, AXP152_SHUTDOWN, 0, AXP152_POWEROFF); + if (ret < 0) + return ret; + + return -EINPROGRESS; +} + +static struct sysreset_ops axp_sysreset_ops = { + .request = axp_sysreset_request, +}; + +U_BOOT_DRIVER(axp_sysreset) = { + .name = "axp_sysreset", + .id = UCLASS_SYSRESET, + .ops = &axp_sysreset_ops, +}; +#endif static int axp_pmic_reg_count(struct udevice *dev) { @@ -16,6 +45,24 @@ static struct dm_pmic_ops axp_pmic_ops = { .write = dm_i2c_write, }; +static int axp_pmic_bind(struct udevice *dev) +{ + int ret; + + ret = dm_scan_fdt_dev(dev); + if (ret) + return ret; + + if (CONFIG_IS_ENABLED(SYSRESET)) { + ret = device_bind_driver_to_node(dev, "axp_sysreset", "axp_sysreset", + dev_ofnode(dev), NULL); + if (ret) + return ret; + } + + return 0; +} + static const struct udevice_id axp_pmic_ids[] = { { .compatible = "x-powers,axp152" }, { .compatible = "x-powers,axp202" }, @@ -33,6 +80,6 @@ U_BOOT_DRIVER(axp_pmic) = { .name = "axp_pmic", .id = UCLASS_PMIC, .of_match = axp_pmic_ids, - .bind = dm_scan_fdt_dev, + .bind = axp_pmic_bind, .ops = &axp_pmic_ops, }; diff --git a/drivers/pwm/exynos_pwm.c b/drivers/pwm/exynos_pwm.c index 1afaf784dab..609025d680d 100644 --- a/drivers/pwm/exynos_pwm.c +++ b/drivers/pwm/exynos_pwm.c @@ -43,6 +43,10 @@ static int exynos_pwm_set_config(struct udevice *dev, uint channel, tcnt = period_ns / rate_ns; tcmp = duty_ns / rate_ns; debug("%s: tcnt %u, tcmp %u\n", __func__, tcnt, tcmp); + + /* Ensure that the comparitor will actually hit the target */ + if (tcmp == tcnt) + tcmp = tcnt - 1; offset = channel * 3; writel(tcnt, ®s->tcntb0 + offset); writel(tcmp, ®s->tcmpb0 + offset); diff --git a/drivers/qe/Kconfig b/drivers/qe/Kconfig index 553ed5780e5..c44a81f69a5 100644 --- a/drivers/qe/Kconfig +++ b/drivers/qe/Kconfig @@ -18,6 +18,10 @@ config U_QE help Choose this option to add support for U QUICC Engine. +config SYS_QE_FW_ADDR + hex "QE Firmware Address" + depends on FMAN_ENET || QE || U_QE + default 0x0 choice prompt "QUICC Engine FMan ethernet firmware location" depends on FMAN_ENET || QE diff --git a/drivers/ram/sifive/sifive_ddr.c b/drivers/ram/sifive/sifive_ddr.c index ba184660331..4bd69a62be2 100644 --- a/drivers/ram/sifive/sifive_ddr.c +++ b/drivers/ram/sifive/sifive_ddr.c @@ -313,7 +313,7 @@ static int sifive_ddr_setup(struct udevice *dev) sifive_ddr_phy_fixup(denali_phy); /* check size */ - priv->info.size = get_ram_size((long *)priv->info.base, + priv->info.size = get_ram_size((long *)(uintptr_t)priv->info.base, ddr_size); debug("%s : %lx\n", __func__, (uintptr_t)priv->info.size); @@ -369,9 +369,9 @@ static int sifive_ddr_probe(struct udevice *dev) return ret; } - priv->ctl = (struct sifive_ddrctl *)dev_read_addr_index(dev, 0); - priv->phy = (struct sifive_ddrphy *)dev_read_addr_index(dev, 1); - priv->physical_filter_ctrl = (u32 *)dev_read_addr_index(dev, 2); + priv->ctl = (struct sifive_ddrctl *)dev_read_addr_index_ptr(dev, 0); + priv->phy = (struct sifive_ddrphy *)dev_read_addr_index_ptr(dev, 1); + priv->physical_filter_ctrl = (u32 *)dev_read_addr_index_ptr(dev, 2); return sifive_ddr_setup(dev); #endif diff --git a/drivers/ram/stm32mp1/stm32mp1_ram.c b/drivers/ram/stm32mp1/stm32mp1_ram.c index 26f0b4f1eae..98fa1f4f118 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ram.c +++ b/drivers/ram/stm32mp1/stm32mp1_ram.c @@ -202,17 +202,16 @@ static int stm32mp1_ddr_probe(struct udevice *dev) priv->info.base = STM32_DDR_BASE; -#if !defined(CONFIG_TFABOOT) && \ - (!defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD)) - priv->info.size = 0; - ret = stm32mp1_ddr_setup(dev); + if (IS_ENABLED(CONFIG_SPL_BUILD)) { + priv->info.size = 0; + ret = stm32mp1_ddr_setup(dev); + + return log_ret(ret); + } - return log_ret(ret); -#else ofnode node = stm32mp1_ddr_get_ofnode(dev); priv->info.size = ofnode_read_u32_default(node, "st,mem-size", 0); return 0; -#endif } static int stm32mp1_ddr_get_info(struct udevice *dev, struct ram_info *info) diff --git a/drivers/rtc/ds1337.c b/drivers/rtc/ds1337.c index 4986c96f862..486c01f9ba2 100644 --- a/drivers/rtc/ds1337.c +++ b/drivers/rtc/ds1337.c @@ -306,7 +306,7 @@ static const struct rtc_ops ds1337_rtc_ops = { static const struct udevice_id ds1337_rtc_ids[] = { { .compatible = "ds1337" }, { .compatible = "ds1338" }, - { .compatible = "ds1338" }, + { .compatible = "ds1339" }, { } }; diff --git a/drivers/rtc/rv8803.c b/drivers/rtc/rv8803.c index acd50c65648..5bae39d6e09 100644 --- a/drivers/rtc/rv8803.c +++ b/drivers/rtc/rv8803.c @@ -157,6 +157,8 @@ static const struct rtc_ops rv8803_rtc_ops = { static const struct udevice_id rv8803_rtc_ids[] = { { .compatible = "microcrystal,rv8803", }, + { .compatible = "epson,rx8803" }, + { .compatible = "epson,rx8900" }, { } }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 122a39789cb..6c8fdda9a0a 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -290,12 +290,20 @@ config DEBUG_SBI_CONSOLE config DEBUG_UART_S5P bool "Samsung S5P" - depends on ARCH_EXYNOS || ARCH_S5PC1XX + depends on ARCH_APPLE || ARCH_EXYNOS || ARCH_S5PC1XX help Select this to enable a debug UART using the serial_s5p driver. You will need to provide parameters to make this work. The driver will be available until the real driver-model serial is running. +config DEBUG_UART_MSM_GENI + bool "Qualcomm snapdragon" + depends on ARCH_SNAPDRAGON + help + Select this to enable a debug UART using the serial_msm driver. You + will need to provide parameters to make this work. The driver will + be available until the real driver-model serial is running. + config DEBUG_UART_MESON bool "Amlogic Meson" depends on MESON_SERIAL @@ -737,7 +745,7 @@ config ROCKCHIP_SERIAL config S5P_SERIAL bool "Support for Samsung S5P UART" - depends on ARCH_EXYNOS || ARCH_S5PC1XX + depends on ARCH_APPLE || ARCH_EXYNOS || ARCH_S5PC1XX default y help Select this to enable Samsung S5P UART support. @@ -801,6 +809,16 @@ config MSM_SERIAL for example APQ8016 and MSM8916. Single baudrate is supported in current implementation (115200). +config MSM_GENI_SERIAL + bool "Qualcomm on-chip GENI UART" + help + Support UART based on Generic Interface (GENI) Serial Engine (SE), + used on Qualcomm Snapdragon SoCs. Should support all qualcomm SOCs + with Qualcomm Universal Peripheral (QUP) Wrapper cores, + i.e. newer ones, starting from SDM845. + Driver works in FIFO mode. + Multiple baudrates supported. + config OCTEON_SERIAL_BOOTCMD bool "MIPS Octeon PCI remote bootcmd input" depends on ARCH_OCTEON diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 4edd2aa9458..8168af640ff 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o obj-$(CONFIG_BCM283X_PL011_SERIAL) += serial_bcm283x_pl011.o obj-$(CONFIG_MSM_SERIAL) += serial_msm.o +obj-$(CONFIG_MSM_GENI_SERIAL) += serial_msm_geni.o obj-$(CONFIG_MVEBU_A3700_UART) += serial_mvebu_a3700.o obj-$(CONFIG_MPC8XX_CONS) += serial_mpc8xx.o obj-$(CONFIG_NULLDEV_SERIAL) += serial_nulldev.o diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 2b473d70f64..3c9a69598ad 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -553,6 +553,8 @@ static const struct dm_serial_ops lpuart_serial_ops = { static const struct udevice_id lpuart_serial_ids[] = { { .compatible = "fsl,ls1021a-lpuart", .data = LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG }, + { .compatible = "fsl,ls1028a-lpuart", + .data = LPUART_FLAG_REGMAP_32BIT_REG }, { .compatible = "fsl,imx7ulp-lpuart", .data = LPUART_FLAG_REGMAP_32BIT_REG }, { .compatible = "fsl,vf610-lpuart"}, diff --git a/drivers/serial/serial_msm_geni.c b/drivers/serial/serial_msm_geni.c new file mode 100644 index 00000000000..3e255a99dcc --- /dev/null +++ b/drivers/serial/serial_msm_geni.c @@ -0,0 +1,613 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm GENI serial engine UART driver + * + * (C) Copyright 2021 Dzmitry Sankouski <dsankouski@gmail.com> + * + * Based on Linux driver. + */ + +#include <asm/io.h> +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <dm/pinctrl.h> +#include <errno.h> +#include <linux/compiler.h> +#include <log.h> +#include <linux/delay.h> +#include <malloc.h> +#include <serial.h> +#include <watchdog.h> +#include <linux/bug.h> + +#define UART_OVERSAMPLING 32 +#define STALE_TIMEOUT 160 + +#define USEC_PER_SEC 1000000L + +/* Registers*/ +#define GENI_FORCE_DEFAULT_REG 0x20 +#define GENI_SER_M_CLK_CFG 0x48 +#define GENI_SER_S_CLK_CFG 0x4C +#define SE_HW_PARAM_0 0xE24 +#define SE_GENI_STATUS 0x40 +#define SE_GENI_S_CMD0 0x630 +#define SE_GENI_S_CMD_CTRL_REG 0x634 +#define SE_GENI_S_IRQ_CLEAR 0x648 +#define SE_GENI_S_IRQ_STATUS 0x640 +#define SE_GENI_S_IRQ_EN 0x644 +#define SE_GENI_M_CMD0 0x600 +#define SE_GENI_M_CMD_CTRL_REG 0x604 +#define SE_GENI_M_IRQ_CLEAR 0x618 +#define SE_GENI_M_IRQ_STATUS 0x610 +#define SE_GENI_M_IRQ_EN 0x614 +#define SE_GENI_TX_FIFOn 0x700 +#define SE_GENI_RX_FIFOn 0x780 +#define SE_GENI_TX_FIFO_STATUS 0x800 +#define SE_GENI_RX_FIFO_STATUS 0x804 +#define SE_GENI_TX_WATERMARK_REG 0x80C +#define SE_GENI_TX_PACKING_CFG0 0x260 +#define SE_GENI_TX_PACKING_CFG1 0x264 +#define SE_GENI_RX_PACKING_CFG0 0x284 +#define SE_GENI_RX_PACKING_CFG1 0x288 +#define SE_UART_RX_STALE_CNT 0x294 +#define SE_UART_TX_TRANS_LEN 0x270 +#define SE_UART_TX_STOP_BIT_LEN 0x26c +#define SE_UART_TX_WORD_LEN 0x268 +#define SE_UART_RX_WORD_LEN 0x28c +#define SE_UART_TX_TRANS_CFG 0x25c +#define SE_UART_TX_PARITY_CFG 0x2a4 +#define SE_UART_RX_TRANS_CFG 0x280 +#define SE_UART_RX_PARITY_CFG 0x2a8 + +#define M_TX_FIFO_WATERMARK_EN (BIT(30)) +#define DEF_TX_WM 2 +/* GENI_FORCE_DEFAULT_REG fields */ +#define FORCE_DEFAULT (BIT(0)) + +#define S_CMD_ABORT_EN (BIT(5)) + +#define UART_START_READ 0x1 + +/* GENI_M_CMD_CTRL_REG */ +#define M_GENI_CMD_CANCEL (BIT(2)) +#define M_GENI_CMD_ABORT (BIT(1)) +#define M_GENI_DISABLE (BIT(0)) + +#define M_CMD_ABORT_EN (BIT(5)) +#define M_CMD_DONE_EN (BIT(0)) +#define M_CMD_DONE_DISABLE_MASK (~M_CMD_DONE_EN) + +#define S_GENI_CMD_ABORT (BIT(1)) + +/* GENI_S_CMD0 fields */ +#define S_OPCODE_MSK (GENMASK(31, 27)) +#define S_PARAMS_MSK (GENMASK(26, 0)) + +/* GENI_STATUS fields */ +#define M_GENI_CMD_ACTIVE (BIT(0)) +#define S_GENI_CMD_ACTIVE (BIT(12)) +#define M_CMD_DONE_EN (BIT(0)) +#define S_CMD_DONE_EN (BIT(0)) + +#define M_OPCODE_SHIFT 27 +#define S_OPCODE_SHIFT 27 +#define M_TX_FIFO_WATERMARK_EN (BIT(30)) +#define UART_START_TX 0x1 +#define UART_CTS_MASK (BIT(1)) +#define M_SEC_IRQ_EN (BIT(31)) +#define TX_FIFO_WC_MSK (GENMASK(27, 0)) +#define RX_FIFO_WC_MSK (GENMASK(24, 0)) + +#define S_RX_FIFO_WATERMARK_EN (BIT(26)) +#define S_RX_FIFO_LAST_EN (BIT(27)) +#define M_RX_FIFO_WATERMARK_EN (BIT(26)) +#define M_RX_FIFO_LAST_EN (BIT(27)) + +/* GENI_SER_M_CLK_CFG/GENI_SER_S_CLK_CFG */ +#define SER_CLK_EN (BIT(0)) +#define CLK_DIV_MSK (GENMASK(15, 4)) +#define CLK_DIV_SHFT 4 + +/* SE_HW_PARAM_0 fields */ +#define TX_FIFO_WIDTH_MSK (GENMASK(29, 24)) +#define TX_FIFO_WIDTH_SHFT 24 +#define TX_FIFO_DEPTH_MSK (GENMASK(21, 16)) +#define TX_FIFO_DEPTH_SHFT 16 + +/* + * Predefined packing configuration of the serial engine (CFG0, CFG1 regs) + * for uart mode. + * + * Defines following configuration: + * - Bits of data per transfer word 8 + * - Number of words per fifo element 4 + * - Transfer from MSB to LSB or vice-versa false + */ +#define UART_PACKING_CFG0 0xf +#define UART_PACKING_CFG1 0x0 + +DECLARE_GLOBAL_DATA_PTR; + +struct msm_serial_data { + phys_addr_t base; + u32 baud; +}; + +unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, + 32000000, 48000000, 64000000, 80000000, + 96000000, 100000000}; + +/** + * get_clk_cfg() - Get clock rate to apply on clock supplier. + * @clk_freq: Desired clock frequency after build-in divider. + * + * Return: frequency, supported by clock supplier, multiple of clk_freq. + */ +static int get_clk_cfg(unsigned long clk_freq) +{ + for (int i = 0; i < ARRAY_SIZE(root_freq); i++) { + if (!(root_freq[i] % clk_freq)) + return root_freq[i]; + } + return 0; +} + +/** + * get_clk_div_rate() - Find clock supplier frequency, and calculate divisor. + * @baud: Baudrate. + * @sampling_rate: Clock ticks per character. + * @clk_div: Pointer to calculated divisor. + * + * This function searches for suitable frequency for clock supplier, + * calculates divisor for internal divider, based on found frequency, + * and stores divisor under clk_div pointer. + * + * Return: frequency, supported by clock supplier, multiple of clk_freq. + */ +static int get_clk_div_rate(u32 baud, + u64 sampling_rate, u32 *clk_div) +{ + unsigned long ser_clk; + unsigned long desired_clk; + + desired_clk = baud * sampling_rate; + ser_clk = get_clk_cfg(desired_clk); + if (!ser_clk) { + pr_err("%s: Can't find matching DFS entry for baud %d\n", + __func__, baud); + return ser_clk; + } + + *clk_div = ser_clk / desired_clk; + return ser_clk; +} + +static int geni_serial_set_clock_rate(struct udevice *dev, u64 rate) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, "se-clk"); + if (!clk) + return -EINVAL; + + ret = clk_set_rate(clk, rate); + return ret; +} + +/** + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine + * @base: Pointer to the concerned serial engine. + * + * This function is used to get the depth i.e. number of elements in the + * TX fifo of the serial engine. + * + * Return: TX fifo depth in units of FIFO words. + */ +static inline u32 geni_se_get_tx_fifo_depth(long base) +{ + u32 tx_fifo_depth; + + tx_fifo_depth = ((readl(base + SE_HW_PARAM_0) & TX_FIFO_DEPTH_MSK) >> + TX_FIFO_DEPTH_SHFT); + return tx_fifo_depth; +} + +/** + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine + * @base: Pointer to the concerned serial engine. + * + * This function is used to get the width i.e. word size per element in the + * TX fifo of the serial engine. + * + * Return: TX fifo width in bits + */ +static inline u32 geni_se_get_tx_fifo_width(long base) +{ + u32 tx_fifo_width; + + tx_fifo_width = ((readl(base + SE_HW_PARAM_0) & TX_FIFO_WIDTH_MSK) >> + TX_FIFO_WIDTH_SHFT); + return tx_fifo_width; +} + +static inline void geni_serial_baud(phys_addr_t base_address, u32 clk_div, + int baud) +{ + u32 s_clk_cfg = 0; + + s_clk_cfg |= SER_CLK_EN; + s_clk_cfg |= (clk_div << CLK_DIV_SHFT); + + writel(s_clk_cfg, base_address + GENI_SER_M_CLK_CFG); + writel(s_clk_cfg, base_address + GENI_SER_S_CLK_CFG); +} + +int msm_serial_setbrg(struct udevice *dev, int baud) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + priv->baud = baud; + u32 clk_div; + u64 clk_rate; + + clk_rate = get_clk_div_rate(baud, UART_OVERSAMPLING, &clk_div); + geni_serial_set_clock_rate(dev, clk_rate); + geni_serial_baud(priv->base, clk_div, baud); + + return 0; +} + +/** + * qcom_geni_serial_poll_bit() - Poll reg bit until desired value or timeout. + * @base: Pointer to the concerned serial engine. + * @offset: Offset to register address. + * @field: AND bitmask for desired bit. + * @set: Desired bit value. + * + * This function is used to get the width i.e. word size per element in the + * TX fifo of the serial engine. + * + * Return: true, when register bit equals desired value, false, when timeout + * reached. + */ +static bool qcom_geni_serial_poll_bit(const struct udevice *dev, int offset, + int field, bool set) +{ + u32 reg; + struct msm_serial_data *priv = dev_get_priv(dev); + unsigned int baud; + unsigned int tx_fifo_depth; + unsigned int tx_fifo_width; + unsigned int fifo_bits; + unsigned long timeout_us = 10000; + + baud = 115200; + + if (priv) { + baud = priv->baud; + if (!baud) + baud = 115200; + tx_fifo_depth = geni_se_get_tx_fifo_depth(priv->base); + tx_fifo_width = geni_se_get_tx_fifo_width(priv->base); + fifo_bits = tx_fifo_depth * tx_fifo_width; + /* + * Total polling iterations based on FIFO worth of bytes to be + * sent at current baud. Add a little fluff to the wait. + */ + timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500; + } + + timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10; + while (timeout_us) { + reg = readl(priv->base + offset); + if ((bool)(reg & field) == set) + return true; + udelay(10); + timeout_us -= 10; + } + return false; +} + +static void qcom_geni_serial_setup_tx(u64 base, u32 xmit_size) +{ + u32 m_cmd; + + writel(xmit_size, base + SE_UART_TX_TRANS_LEN); + m_cmd = UART_START_TX << M_OPCODE_SHIFT; + writel(m_cmd, base + SE_GENI_M_CMD0); +} + +static inline void qcom_geni_serial_poll_tx_done(const struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + int done = 0; + u32 irq_clear = M_CMD_DONE_EN; + + done = qcom_geni_serial_poll_bit(dev, SE_GENI_M_IRQ_STATUS, + M_CMD_DONE_EN, true); + if (!done) { + writel(M_GENI_CMD_ABORT, priv->base + SE_GENI_M_CMD_CTRL_REG); + irq_clear |= M_CMD_ABORT_EN; + qcom_geni_serial_poll_bit(dev, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + } + writel(irq_clear, priv->base + SE_GENI_M_IRQ_CLEAR); +} + +static u32 qcom_geni_serial_tx_empty(u64 base) +{ + return !readl(base + SE_GENI_TX_FIFO_STATUS); +} + +/** + * geni_se_setup_s_cmd() - Setup the secondary sequencer + * @se: Pointer to the concerned serial engine. + * @cmd: Command/Operation to setup in the secondary sequencer. + * @params: Parameter for the sequencer command. + * + * This function is used to configure the secondary sequencer with the + * command and its associated parameters. + */ +static inline void geni_se_setup_s_cmd(u64 base, u32 cmd, u32 params) +{ + u32 s_cmd; + + s_cmd = readl(base + SE_GENI_S_CMD0); + s_cmd &= ~(S_OPCODE_MSK | S_PARAMS_MSK); + s_cmd |= (cmd << S_OPCODE_SHIFT); + s_cmd |= (params & S_PARAMS_MSK); + writel(s_cmd, base + SE_GENI_S_CMD0); +} + +static void qcom_geni_serial_start_tx(u64 base) +{ + u32 irq_en; + u32 status; + + status = readl(base + SE_GENI_STATUS); + if (status & M_GENI_CMD_ACTIVE) + return; + + if (!qcom_geni_serial_tx_empty(base)) + return; + + irq_en = readl(base + SE_GENI_M_IRQ_EN); + irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; + + writel(DEF_TX_WM, base + SE_GENI_TX_WATERMARK_REG); + writel(irq_en, base + SE_GENI_M_IRQ_EN); +} + +static void qcom_geni_serial_start_rx(struct udevice *dev) +{ + u32 status; + struct msm_serial_data *priv = dev_get_priv(dev); + + status = readl(priv->base + SE_GENI_STATUS); + + geni_se_setup_s_cmd(priv->base, UART_START_READ, 0); + + setbits_le32(priv->base + SE_GENI_S_IRQ_EN, S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); + setbits_le32(priv->base + SE_GENI_M_IRQ_EN, M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); +} + +static void qcom_geni_serial_abort_rx(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN; + + writel(S_GENI_CMD_ABORT, priv->base + SE_GENI_S_CMD_CTRL_REG); + qcom_geni_serial_poll_bit(dev, SE_GENI_S_CMD_CTRL_REG, + S_GENI_CMD_ABORT, false); + writel(irq_clear, priv->base + SE_GENI_S_IRQ_CLEAR); + writel(FORCE_DEFAULT, priv->base + GENI_FORCE_DEFAULT_REG); +} + +static void msm_geni_serial_setup_rx(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + qcom_geni_serial_abort_rx(dev); + + writel(UART_PACKING_CFG0, priv->base + SE_GENI_RX_PACKING_CFG0); + writel(UART_PACKING_CFG1, priv->base + SE_GENI_RX_PACKING_CFG1); + + geni_se_setup_s_cmd(priv->base, UART_START_READ, 0); + + setbits_le32(priv->base + SE_GENI_S_IRQ_EN, S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); + setbits_le32(priv->base + SE_GENI_M_IRQ_EN, M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); +} + +static int msm_serial_putc(struct udevice *dev, const char ch) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + writel(DEF_TX_WM, priv->base + SE_GENI_TX_WATERMARK_REG); + qcom_geni_serial_setup_tx(priv->base, 1); + + qcom_geni_serial_poll_bit(dev, SE_GENI_M_IRQ_STATUS, + M_TX_FIFO_WATERMARK_EN, true); + + writel(ch, priv->base + SE_GENI_TX_FIFOn); + writel(M_TX_FIFO_WATERMARK_EN, priv->base + SE_GENI_M_IRQ_CLEAR); + + qcom_geni_serial_poll_tx_done(dev); + + return 0; +} + +static int msm_serial_getc(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + u32 rx_fifo; + u32 m_irq_status; + u32 s_irq_status; + + writel(1 << S_OPCODE_SHIFT, priv->base + SE_GENI_S_CMD0); + + qcom_geni_serial_poll_bit(dev, SE_GENI_M_IRQ_STATUS, M_SEC_IRQ_EN, + true); + + m_irq_status = readl(priv->base + SE_GENI_M_IRQ_STATUS); + s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS); + writel(m_irq_status, priv->base + SE_GENI_M_IRQ_CLEAR); + writel(s_irq_status, priv->base + SE_GENI_S_IRQ_CLEAR); + qcom_geni_serial_poll_bit(dev, SE_GENI_RX_FIFO_STATUS, RX_FIFO_WC_MSK, + true); + + if (!readl(priv->base + SE_GENI_RX_FIFO_STATUS)) + return 0; + + rx_fifo = readl(priv->base + SE_GENI_RX_FIFOn); + return rx_fifo & 0xff; +} + +static int msm_serial_pending(struct udevice *dev, bool input) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + if (input) + return readl(priv->base + SE_GENI_RX_FIFO_STATUS) & + RX_FIFO_WC_MSK; + else + return readl(priv->base + SE_GENI_TX_FIFO_STATUS) & + TX_FIFO_WC_MSK; + + return 0; +} + +static const struct dm_serial_ops msm_serial_ops = { + .putc = msm_serial_putc, + .pending = msm_serial_pending, + .getc = msm_serial_getc, + .setbrg = msm_serial_setbrg, +}; + +static inline void geni_serial_init(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + phys_addr_t base_address = priv->base; + u32 tx_trans_cfg; + u32 tx_parity_cfg = 0; /* Disable Tx Parity */ + u32 rx_trans_cfg = 0; + u32 rx_parity_cfg = 0; /* Disable Rx Parity */ + u32 stop_bit_len = 0; /* Default stop bit length - 1 bit */ + u32 bits_per_char; + + /* + * Ignore Flow control. + * n = 8. + */ + tx_trans_cfg = UART_CTS_MASK; + bits_per_char = BITS_PER_BYTE; + + /* + * Make an unconditional cancel on the main sequencer to reset + * it else we could end up in data loss scenarios. + */ + qcom_geni_serial_poll_tx_done(dev); + qcom_geni_serial_abort_rx(dev); + + writel(UART_PACKING_CFG0, base_address + SE_GENI_TX_PACKING_CFG0); + writel(UART_PACKING_CFG1, base_address + SE_GENI_TX_PACKING_CFG1); + writel(UART_PACKING_CFG0, base_address + SE_GENI_RX_PACKING_CFG0); + writel(UART_PACKING_CFG1, base_address + SE_GENI_RX_PACKING_CFG1); + + writel(tx_trans_cfg, base_address + SE_UART_TX_TRANS_CFG); + writel(tx_parity_cfg, base_address + SE_UART_TX_PARITY_CFG); + writel(rx_trans_cfg, base_address + SE_UART_RX_TRANS_CFG); + writel(rx_parity_cfg, base_address + SE_UART_RX_PARITY_CFG); + writel(bits_per_char, base_address + SE_UART_TX_WORD_LEN); + writel(bits_per_char, base_address + SE_UART_RX_WORD_LEN); + writel(stop_bit_len, base_address + SE_UART_TX_STOP_BIT_LEN); +} + +static int msm_serial_probe(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + /* No need to reinitialize the UART after relocation */ + if (gd->flags & GD_FLG_RELOC) + return 0; + + geni_serial_init(dev); + msm_geni_serial_setup_rx(dev); + qcom_geni_serial_start_rx(dev); + qcom_geni_serial_start_tx(priv->base); + + return 0; +} + +static int msm_serial_ofdata_to_platdata(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static const struct udevice_id msm_serial_ids[] = { + {.compatible = "qcom,msm-geni-uart"}, {}}; + +U_BOOT_DRIVER(serial_msm_geni) = { + .name = "serial_msm_geni", + .id = UCLASS_SERIAL, + .of_match = msm_serial_ids, + .of_to_plat = msm_serial_ofdata_to_platdata, + .priv_auto = sizeof(struct msm_serial_data), + .probe = msm_serial_probe, + .ops = &msm_serial_ops, +}; + +#ifdef CONFIG_DEBUG_UART_MSM_GENI + +static struct msm_serial_data init_serial_data = { + .base = CONFIG_DEBUG_UART_BASE +}; + +/* Serial dumb device, to reuse driver code */ +static struct udevice init_dev = { + .priv_ = &init_serial_data, +}; + +#include <debug_uart.h> + +#define CLK_DIV (CONFIG_DEBUG_UART_CLOCK / \ + (CONFIG_BAUDRATE * UART_OVERSAMPLING)) +#if (CONFIG_DEBUG_UART_CLOCK % (CONFIG_BAUDRATE * UART_OVERSAMPLING) > 0) +#error Clocks cannot be set at early debug. Change CONFIG_BAUDRATE +#endif + +static inline void _debug_uart_init(void) +{ + phys_addr_t base = CONFIG_DEBUG_UART_BASE; + + geni_serial_init(&init_dev); + geni_serial_baud(base, CLK_DIV, CONFIG_BAUDRATE); + qcom_geni_serial_start_tx(base); +} + +static inline void _debug_uart_putc(int ch) +{ + phys_addr_t base = CONFIG_DEBUG_UART_BASE; + + writel(DEF_TX_WM, base + SE_GENI_TX_WATERMARK_REG); + qcom_geni_serial_setup_tx(base, 1); + qcom_geni_serial_poll_bit(&init_dev, SE_GENI_M_IRQ_STATUS, + M_TX_FIFO_WATERMARK_EN, true); + + writel(ch, base + SE_GENI_TX_FIFOn); + writel(M_TX_FIFO_WATERMARK_EN, base + SE_GENI_M_IRQ_CLEAR); + qcom_geni_serial_poll_tx_done(&init_dev); +} + +DEBUG_UART_FUNCS + +#endif diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 6d09952a5dc..de420d2d945 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -14,24 +14,45 @@ #include <asm/global_data.h> #include <linux/compiler.h> #include <asm/io.h> +#if !CONFIG_IS_ENABLED(ARCH_APPLE) #include <asm/arch/clk.h> +#endif #include <asm/arch/uart.h> #include <serial.h> #include <clk.h> DECLARE_GLOBAL_DATA_PTR; -#define RX_FIFO_COUNT_SHIFT 0 -#define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT) -#define RX_FIFO_FULL (1 << 8) -#define TX_FIFO_COUNT_SHIFT 16 -#define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT) -#define TX_FIFO_FULL (1 << 24) +enum { + PORT_S5P = 0, + PORT_S5L +}; + +#define S5L_RX_FIFO_COUNT_SHIFT 0 +#define S5L_RX_FIFO_COUNT_MASK (0xf << S5L_RX_FIFO_COUNT_SHIFT) +#define S5L_RX_FIFO_FULL (1 << 8) +#define S5L_TX_FIFO_COUNT_SHIFT 4 +#define S5L_TX_FIFO_COUNT_MASK (0xf << S5L_TX_FIFO_COUNT_SHIFT) +#define S5L_TX_FIFO_FULL (1 << 9) + +#define S5P_RX_FIFO_COUNT_SHIFT 0 +#define S5P_RX_FIFO_COUNT_MASK (0xff << S5P_RX_FIFO_COUNT_SHIFT) +#define S5P_RX_FIFO_FULL (1 << 8) +#define S5P_TX_FIFO_COUNT_SHIFT 16 +#define S5P_TX_FIFO_COUNT_MASK (0xff << S5P_TX_FIFO_COUNT_SHIFT) +#define S5P_TX_FIFO_FULL (1 << 24) /* Information about a serial port */ struct s5p_serial_plat { struct s5p_uart *reg; /* address of registers in physical memory */ + u8 reg_width; /* register width */ u8 port_id; /* uart port number */ + u8 rx_fifo_count_shift; + u8 tx_fifo_count_shift; + u32 rx_fifo_count_mask; + u32 tx_fifo_count_mask; + u32 rx_fifo_full; + u32 tx_fifo_full; }; /* @@ -71,8 +92,8 @@ static void __maybe_unused s5p_serial_init(struct s5p_uart *uart) writel(0x245, &uart->ucon); } -static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk, - int baudrate) +static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, u8 reg_width, + uint uclk, int baudrate) { u32 val; @@ -82,6 +103,8 @@ static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk, if (s5p_uart_divslot()) writew(udivslot[val % 16], &uart->rest.slot); + else if (reg_width == 4) + writel(val % 16, &uart->rest.value); else writeb(val % 16, &uart->rest.value); } @@ -93,7 +116,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate) struct s5p_uart *const uart = plat->reg; u32 uclk; -#ifdef CONFIG_CLK_EXYNOS +#if CONFIG_IS_ENABLED(CLK_EXYNOS) || CONFIG_IS_ENABLED(ARCH_APPLE) struct clk clk; u32 ret; @@ -105,7 +128,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate) uclk = get_uart_clk(plat->port_id); #endif - s5p_serial_baud(uart, uclk, baudrate); + s5p_serial_baud(uart, plat->reg_width, uclk, baudrate); return 0; } @@ -144,11 +167,14 @@ static int s5p_serial_getc(struct udevice *dev) struct s5p_serial_plat *plat = dev_get_plat(dev); struct s5p_uart *const uart = plat->reg; - if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK)) + if (!(readl(&uart->ufstat) & plat->rx_fifo_count_mask)) return -EAGAIN; serial_err_check(uart, 0); - return (int)(readb(&uart->urxh) & 0xff); + if (plat->reg_width == 4) + return (int)(readl(&uart->urxh) & 0xff); + else + return (int)(readb(&uart->urxh) & 0xff); } static int s5p_serial_putc(struct udevice *dev, const char ch) @@ -156,10 +182,13 @@ static int s5p_serial_putc(struct udevice *dev, const char ch) struct s5p_serial_plat *plat = dev_get_plat(dev); struct s5p_uart *const uart = plat->reg; - if (readl(&uart->ufstat) & TX_FIFO_FULL) + if (readl(&uart->ufstat) & plat->tx_fifo_full) return -EAGAIN; - writeb(ch, &uart->utxh); + if (plat->reg_width == 4) + writel(ch, &uart->utxh); + else + writeb(ch, &uart->utxh); serial_err_check(uart, 1); return 0; @@ -171,15 +200,19 @@ static int s5p_serial_pending(struct udevice *dev, bool input) struct s5p_uart *const uart = plat->reg; uint32_t ufstat = readl(&uart->ufstat); - if (input) - return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT; - else - return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT; + if (input) { + return (ufstat & plat->rx_fifo_count_mask) >> + plat->rx_fifo_count_shift; + } else { + return (ufstat & plat->tx_fifo_count_mask) >> + plat->tx_fifo_count_shift; + } } static int s5p_serial_of_to_plat(struct udevice *dev) { struct s5p_serial_plat *plat = dev_get_plat(dev); + const ulong port_type = dev_get_driver_data(dev); fdt_addr_t addr; addr = dev_read_addr(dev); @@ -187,8 +220,26 @@ static int s5p_serial_of_to_plat(struct udevice *dev) return -EINVAL; plat->reg = (struct s5p_uart *)addr; + plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1); plat->port_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "id", dev_seq(dev)); + + if (port_type == PORT_S5L) { + plat->rx_fifo_count_shift = S5L_RX_FIFO_COUNT_SHIFT; + plat->rx_fifo_count_mask = S5L_RX_FIFO_COUNT_MASK; + plat->rx_fifo_full = S5L_RX_FIFO_FULL; + plat->tx_fifo_count_shift = S5L_TX_FIFO_COUNT_SHIFT; + plat->tx_fifo_count_mask = S5L_TX_FIFO_COUNT_MASK; + plat->tx_fifo_full = S5L_TX_FIFO_FULL; + } else { + plat->rx_fifo_count_shift = S5P_RX_FIFO_COUNT_SHIFT; + plat->rx_fifo_count_mask = S5P_RX_FIFO_COUNT_MASK; + plat->rx_fifo_full = S5P_RX_FIFO_FULL; + plat->tx_fifo_count_shift = S5P_TX_FIFO_COUNT_SHIFT; + plat->tx_fifo_count_mask = S5P_TX_FIFO_COUNT_MASK; + plat->tx_fifo_full = S5P_TX_FIFO_FULL; + } + return 0; } @@ -200,7 +251,8 @@ static const struct dm_serial_ops s5p_serial_ops = { }; static const struct udevice_id s5p_serial_ids[] = { - { .compatible = "samsung,exynos4210-uart" }, + { .compatible = "samsung,exynos4210-uart", .data = PORT_S5P }, + { .compatible = "apple,s5l-uart", .data = PORT_S5L }, { } }; @@ -221,19 +273,30 @@ U_BOOT_DRIVER(serial_s5p) = { static inline void _debug_uart_init(void) { + if (IS_ENABLED(CONFIG_DEBUG_UART_SKIP_INIT)) + return; + struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE; s5p_serial_init(uart); - s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); +#if CONFIG_IS_ENABLED(ARCH_APPLE) + s5p_serial_baud(uart, 4, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); +#else + s5p_serial_baud(uart, 1, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); +#endif } static inline void _debug_uart_putc(int ch) { struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE; - while (readl(&uart->ufstat) & TX_FIFO_FULL); - +#if CONFIG_IS_ENABLED(ARCH_APPLE) + while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL); + writel(ch, &uart->utxh); +#else + while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL); writeb(ch, &uart->utxh); +#endif } DEBUG_UART_FUNCS diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c index 8fe3508c640..62444e408a1 100644 --- a/drivers/spi/fsl_dspi.c +++ b/drivers/spi/fsl_dspi.c @@ -586,8 +586,9 @@ static int fsl_dspi_of_to_plat(struct udevice *bus) if (fdtdec_get_bool(blob, node, "big-endian")) plat->flags |= DSPI_FLAG_REGMAP_ENDIAN_BIG; - plat->num_chipselect = - fdtdec_get_int(blob, node, "num-cs", FSL_DSPI_MAX_CHIPSELECT); + plat->num_chipselect = fdtdec_get_int(blob, node, + "spi-num-chipselects", + FSL_DSPI_MAX_CHIPSELECT); addr = dev_read_addr(bus); if (addr == FDT_ADDR_T_NONE) { @@ -654,6 +655,7 @@ static const struct dm_spi_ops fsl_dspi_ops = { static const struct udevice_id fsl_dspi_ids[] = { { .compatible = "fsl,vf610-dspi" }, + { .compatible = "fsl,ls1021a-v1.0-dspi" }, { } }; diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index bba7a330e0c..b7c922b1dfd 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -41,6 +41,11 @@ #include <spi.h> #include <spi-mem.h> #include <asm/io.h> +#ifdef CONFIG_FSL_LAYERSCAPE +#include <asm/arch/clock.h> +#include <asm/arch/soc.h> +#include <asm/arch/speed.h> +#endif #include <linux/bitops.h> #include <linux/kernel.h> #include <linux/sizes.h> @@ -304,6 +309,9 @@ #define POLL_TOUT 5000 #define NXP_FSPI_MAX_CHIPSELECT 4 +/* Access flash memory using IP bus only */ +#define FSPI_QUIRK_USE_IP_ONLY BIT(0) + struct nxp_fspi_devtype_data { unsigned int rxfifo; unsigned int txfifo; @@ -312,7 +320,7 @@ struct nxp_fspi_devtype_data { bool little_endian; }; -static const struct nxp_fspi_devtype_data lx2160a_data = { +static struct nxp_fspi_devtype_data lx2160a_data = { .rxfifo = SZ_512, /* (64 * 64 bits) */ .txfifo = SZ_1K, /* (128 * 64 bits) */ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ @@ -320,7 +328,7 @@ static const struct nxp_fspi_devtype_data lx2160a_data = { .little_endian = true, /* little-endian */ }; -static const struct nxp_fspi_devtype_data imx8mm_data = { +static struct nxp_fspi_devtype_data imx8mm_data = { .rxfifo = SZ_512, /* (64 * 64 bits) */ .txfifo = SZ_1K, /* (128 * 64 bits) */ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ @@ -335,9 +343,14 @@ struct nxp_fspi { u32 memmap_phy; u32 memmap_phy_size; struct clk clk, clk_en; - const struct nxp_fspi_devtype_data *devtype_data; + struct nxp_fspi_devtype_data *devtype_data; }; +static inline int needs_ip_only(struct nxp_fspi *f) +{ + return f->devtype_data->quirks & FSPI_QUIRK_USE_IP_ONLY; +} + /* * R/W functions for big- or little-endian registers: * The FSPI controller's endianness is independent of @@ -521,8 +534,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f, for (i = 0; i < ARRAY_SIZE(lutval); i++) fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i)); - dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x]\n", - op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3]); + dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n", + op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes); /* lock LUT */ fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY); @@ -769,12 +782,14 @@ static int nxp_fspi_exec_op(struct spi_slave *slave, nxp_fspi_prepare_lut(f, op); /* - * If we have large chunks of data, we read them through the AHB bus - * by accessing the mapped memory. In all other cases we use - * IP commands to access the flash. + * If we have large chunks of data, we read them through the AHB bus by + * accessing the mapped memory. In all other cases we use IP commands + * to access the flash. Read via AHB bus may be corrupted due to + * existence of an errata and therefore discard AHB read in such cases. */ if (op->data.nbytes > (f->devtype_data->rxfifo - 4) && - op->data.dir == SPI_MEM_DATA_IN) { + op->data.dir == SPI_MEM_DATA_IN && + !needs_ip_only(f)) { nxp_fspi_read_ahb(f, op); } else { if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) @@ -808,9 +823,42 @@ static int nxp_fspi_adjust_op_size(struct spi_slave *slave, op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8); } + /* Limit data bytes to RX FIFO in case of IP read only */ + if (needs_ip_only(f) && + op->data.dir == SPI_MEM_DATA_IN && + op->data.nbytes > f->devtype_data->rxfifo) + op->data.nbytes = f->devtype_data->rxfifo; + return 0; } +#ifdef CONFIG_FSL_LAYERSCAPE +static void erratum_err050568(struct nxp_fspi *f) +{ + struct sys_info sysinfo; + u32 svr = 0, freq = 0; + + /* Check for LS1028A variants */ + svr = SVR_SOC_VER(get_svr()); + if (svr != SVR_LS1017A || + svr != SVR_LS1018A || + svr != SVR_LS1027A || + svr != SVR_LS1028A) { + dev_dbg(f->dev, "Errata applicable only for LS1028A variants\n"); + return; + } + + /* Read PLL frequency */ + get_sys_info(&sysinfo); + freq = sysinfo.freq_systembus / 1000000; /* Convert to MHz */ + dev_dbg(f->dev, "svr: %08x, Frequency: %dMhz\n", svr, freq); + + /* Use IP bus only if PLL is 300MHz */ + if (freq == 300) + f->devtype_data->quirks |= FSPI_QUIRK_USE_IP_ONLY; +} +#endif + static int nxp_fspi_default_setup(struct nxp_fspi *f) { void __iomem *base = f->iobase; @@ -831,6 +879,17 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f) return ret; #endif +#ifdef CONFIG_FSL_LAYERSCAPE + /* + * ERR050568: Flash access by FlexSPI AHB command may not work with + * platform frequency equal to 300 MHz on LS1028A. + * LS1028A reuses LX2160A compatible entry. Make errata applicable for + * Layerscape LS1028A platform family. + */ + if (device_is_compatible(f->dev, "nxp,lx2160a-fspi")) + erratum_err050568(f); +#endif + /* Reset the module */ /* w1c register, wait unit clear */ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0, diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c index 5a335e50aa6..27a035c0a59 100644 --- a/drivers/spmi/spmi-msm.c +++ b/drivers/spmi/spmi-msm.c @@ -19,39 +19,63 @@ DECLARE_GLOBAL_DATA_PTR; /* PMIC Arbiter configuration registers */ -#define PMIC_ARB_VERSION 0x0000 -#define PMIC_ARB_VERSION_V2_MIN 0x20010000 - -#define ARB_CHANNEL_OFFSET(n) (0x4 * (n)) -#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000) - -#define SPMI_REG_CMD0 0x0 -#define SPMI_REG_CONFIG 0x4 -#define SPMI_REG_STATUS 0x8 -#define SPMI_REG_WDATA 0x10 -#define SPMI_REG_RDATA 0x18 - -#define SPMI_CMD_OPCODE_SHIFT 27 -#define SPMI_CMD_SLAVE_ID_SHIFT 20 -#define SPMI_CMD_ADDR_SHIFT 12 -#define SPMI_CMD_ADDR_OFFSET_SHIFT 4 -#define SPMI_CMD_BYTE_CNT_SHIFT 0 - -#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00 -#define SPMI_CMD_EXT_REG_READ_LONG 0x01 - -#define SPMI_STATUS_DONE 0x1 +#define PMIC_ARB_VERSION 0x0000 +#define PMIC_ARB_VERSION_V2_MIN 0x20010000 +#define PMIC_ARB_VERSION_V3_MIN 0x30000000 +#define PMIC_ARB_VERSION_V5_MIN 0x50000000 + +#define APID_MAP_OFFSET_V1_V2_V3 (0x800) +#define APID_MAP_OFFSET_V5 (0x900) +#define ARB_CHANNEL_OFFSET(n) (0x4 * (n)) +#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000) +#define SPMI_V5_OBS_CH_OFFSET(chnl) ((chnl) * 0x80) +#define SPMI_V5_RW_CH_OFFSET(chnl) ((chnl) * 0x10000) + +#define SPMI_REG_CMD0 0x0 +#define SPMI_REG_CONFIG 0x4 +#define SPMI_REG_STATUS 0x8 +#define SPMI_REG_WDATA 0x10 +#define SPMI_REG_RDATA 0x18 + +#define SPMI_CMD_OPCODE_SHIFT 27 +#define SPMI_CMD_SLAVE_ID_SHIFT 20 +#define SPMI_CMD_ADDR_SHIFT 12 +#define SPMI_CMD_ADDR_OFFSET_SHIFT 4 +#define SPMI_CMD_BYTE_CNT_SHIFT 0 + +#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00 +#define SPMI_CMD_EXT_REG_READ_LONG 0x01 + +#define SPMI_STATUS_DONE 0x1 + +#define SPMI_MAX_CHANNELS 128 +#define SPMI_MAX_SLAVES 16 +#define SPMI_MAX_PERIPH 256 + +enum arb_ver { + V1 = 1, + V2, + V3, + V5 = 5 +}; -#define SPMI_MAX_CHANNELS 128 -#define SPMI_MAX_SLAVES 16 -#define SPMI_MAX_PERIPH 256 +/* + * PMIC arbiter version 5 uses different register offsets for read/write vs + * observer channels. + */ +enum pmic_arb_channel { + PMIC_ARB_CHANNEL_RW, + PMIC_ARB_CHANNEL_OBS, +}; struct msm_spmi_priv { - phys_addr_t arb_chnl; /* ARB channel mapping base */ + phys_addr_t arb_chnl; /* ARB channel mapping base */ phys_addr_t spmi_core; /* SPMI core */ - phys_addr_t spmi_obs; /* SPMI observer */ + phys_addr_t spmi_obs; /* SPMI observer */ /* SPMI channel map */ uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH]; + /* SPMI bus arbiter version */ + u32 arb_ver; }; static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, @@ -59,6 +83,7 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, { struct msm_spmi_priv *priv = dev_get_priv(dev); unsigned channel; + unsigned int ch_offset; uint32_t reg = 0; if (usid >= SPMI_MAX_SLAVES) @@ -69,8 +94,8 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, channel = priv->channel_map[usid][pid]; /* Disable IRQ mode for the current channel*/ - writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) + - SPMI_REG_CONFIG); + writel(0x0, + priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG); /* Write single byte */ writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA); @@ -82,6 +107,11 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT); reg |= 1; /* byte count */ + if (priv->arb_ver == V5) + ch_offset = SPMI_V5_RW_CH_OFFSET(channel); + else + ch_offset = SPMI_CH_OFFSET(channel); + /* Send write command */ writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0); @@ -104,6 +134,7 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) { struct msm_spmi_priv *priv = dev_get_priv(dev); unsigned channel; + unsigned int ch_offset; uint32_t reg = 0; if (usid >= SPMI_MAX_SLAVES) @@ -113,8 +144,13 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) channel = priv->channel_map[usid][pid]; + if (priv->arb_ver == V5) + ch_offset = SPMI_V5_OBS_CH_OFFSET(channel); + else + ch_offset = SPMI_CH_OFFSET(channel); + /* Disable IRQ mode for the current channel*/ - writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG); + writel(0x0, priv->spmi_obs + ch_offset + SPMI_REG_CONFIG); /* Prepare read command */ reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT; @@ -124,13 +160,12 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) reg |= 1; /* byte count */ /* Request read */ - writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0); + writel(reg, priv->spmi_obs + ch_offset + SPMI_REG_CMD0); /* Wait till CMD DONE status */ reg = 0; while (!reg) { - reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) + - SPMI_REG_STATUS); + reg = readl(priv->spmi_obs + ch_offset + SPMI_REG_STATUS); } if (reg ^ SPMI_STATUS_DONE) { @@ -139,8 +174,8 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) } /* Read the data */ - return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) + - SPMI_REG_RDATA) & 0xFF; + return readl(priv->spmi_obs + ch_offset + + SPMI_REG_RDATA) & 0xFF; } static struct dm_spmi_ops msm_spmi_ops = { @@ -150,31 +185,50 @@ static struct dm_spmi_ops msm_spmi_ops = { static int msm_spmi_probe(struct udevice *dev) { - struct udevice *parent = dev->parent; struct msm_spmi_priv *priv = dev_get_priv(dev); - int node = dev_of_offset(dev); + phys_addr_t config_addr; u32 hw_ver; - bool is_v1; + u32 version; int i; + int err; + + config_addr = dev_read_addr_index(dev, 0); + priv->spmi_core = dev_read_addr_index(dev, 1); + priv->spmi_obs = dev_read_addr_index(dev, 2); + + hw_ver = readl(config_addr + PMIC_ARB_VERSION); + + if (hw_ver < PMIC_ARB_VERSION_V3_MIN) { + priv->arb_ver = V2; + version = 2; + priv->arb_chnl = config_addr + APID_MAP_OFFSET_V1_V2_V3; + } else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) { + priv->arb_ver = V3; + version = 3; + priv->arb_chnl = config_addr + APID_MAP_OFFSET_V1_V2_V3; + } else { + priv->arb_ver = V5; + version = 5; + priv->arb_chnl = config_addr + APID_MAP_OFFSET_V5; + + if (err) { + dev_err(dev, "could not read APID->PPID mapping table, rc= %d\n", err); + return -1; + } + } - priv->arb_chnl = dev_read_addr(dev); - priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, - dev_of_offset(parent), node, "reg", 1, NULL, false); - priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, - dev_of_offset(parent), node, "reg", 2, NULL, false); - - hw_ver = readl(priv->arb_chnl + PMIC_ARB_VERSION - 0x800); - is_v1 = (hw_ver < PMIC_ARB_VERSION_V2_MIN); - - dev_dbg(dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2), hw_ver); + dev_dbg(dev, "PMIC Arb Version-%d (0x%x)\n", version, hw_ver); if (priv->arb_chnl == FDT_ADDR_T_NONE || priv->spmi_core == FDT_ADDR_T_NONE || priv->spmi_obs == FDT_ADDR_T_NONE) return -EINVAL; + dev_dbg(dev, "priv->arb_chnl address (%llu)\n", priv->arb_chnl); + dev_dbg(dev, "priv->spmi_core address (%llu)\n", priv->spmi_core); + dev_dbg(dev, "priv->spmi_obs address (%llu)\n", priv->spmi_obs); /* Scan peripherals connected to each SPMI channel */ - for (i = 0; i < SPMI_MAX_PERIPH ; i++) { + for (i = 0; i < SPMI_MAX_PERIPH; i++) { uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i)); uint8_t slave_id = (periph & 0xf0000) >> 16; uint8_t pid = (periph & 0xff00) >> 8; @@ -195,5 +249,5 @@ U_BOOT_DRIVER(msm_spmi) = { .of_match = msm_spmi_ids, .ops = &msm_spmi_ops, .probe = msm_spmi_probe, - .priv_auto = sizeof(struct msm_spmi_priv), + .priv_auto = sizeof(struct msm_spmi_priv), }; diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 43a948cfcde..f6d60038b89 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -118,8 +118,6 @@ config SYSRESET_TI_SCI This enables the system reset driver support over TI System Control Interface available on some new TI's SoCs. -endif - config SYSRESET_SYSCON bool "Enable support for mfd syscon reboot driver" select REGMAP @@ -133,6 +131,13 @@ config SYSRESET_WATCHDOG help Reboot support for generic watchdog reset. +config SYSRESET_WATCHDOG_AUTO + bool "Automatically register first watchdog with sysreset" + depends on SYSRESET_WATCHDOG + help + If enabled, the first watchdog (as selected by the watchdog uclass) + will automatically be registered with the watchdog reboot driver. + config SYSRESET_RESETCTL bool "Enable support for reset controller reboot driver" select DM_RESET @@ -162,4 +167,6 @@ config SYSRESET_MPC83XX help Reboot support for NXP MPC83xx SoCs. +endif + endmenu diff --git a/drivers/sysreset/sysreset_gpio.c b/drivers/sysreset/sysreset_gpio.c index 680b759eb3c..dfca10ccc8e 100644 --- a/drivers/sysreset/sysreset_gpio.c +++ b/drivers/sysreset/sysreset_gpio.c @@ -33,7 +33,7 @@ static struct sysreset_ops gpio_reboot_ops = { .request = gpio_reboot_request, }; -int gpio_reboot_probe(struct udevice *dev) +static int gpio_reboot_probe(struct udevice *dev) { struct gpio_reboot_priv *priv = dev_get_priv(dev); diff --git a/drivers/sysreset/sysreset_resetctl.c b/drivers/sysreset/sysreset_resetctl.c index c039521eb43..25bd5c9a7ff 100644 --- a/drivers/sysreset/sysreset_resetctl.c +++ b/drivers/sysreset/sysreset_resetctl.c @@ -26,7 +26,7 @@ static struct sysreset_ops resetctl_reboot_ops = { .request = resetctl_reboot_request, }; -int resetctl_reboot_probe(struct udevice *dev) +static int resetctl_reboot_probe(struct udevice *dev) { struct resetctl_reboot_priv *priv = dev_get_priv(dev); diff --git a/drivers/sysreset/sysreset_syscon.c b/drivers/sysreset/sysreset_syscon.c index 28fdfb09781..525faf2f89e 100644 --- a/drivers/sysreset/sysreset_syscon.c +++ b/drivers/sysreset/sysreset_syscon.c @@ -39,7 +39,7 @@ static struct sysreset_ops syscon_reboot_ops = { .request = syscon_reboot_request, }; -int syscon_reboot_probe(struct udevice *dev) +static int syscon_reboot_probe(struct udevice *dev) { struct syscon_reboot_priv *priv = dev_get_priv(dev); int err; diff --git a/drivers/sysreset/sysreset_watchdog.c b/drivers/sysreset/sysreset_watchdog.c index 0dc2d8b9b65..35efcac59dd 100644 --- a/drivers/sysreset/sysreset_watchdog.c +++ b/drivers/sysreset/sysreset_watchdog.c @@ -5,20 +5,22 @@ #include <common.h> #include <dm.h> +#include <dm/device-internal.h> #include <errno.h> +#include <malloc.h> #include <sysreset.h> #include <wdt.h> -struct wdt_reboot_priv { +struct wdt_reboot_plat { struct udevice *wdt; }; static int wdt_reboot_request(struct udevice *dev, enum sysreset_t type) { - struct wdt_reboot_priv *priv = dev_get_priv(dev); + struct wdt_reboot_plat *plat = dev_get_plat(dev); int ret; - ret = wdt_expire_now(priv->wdt, 0); + ret = wdt_expire_now(plat->wdt, 0); if (ret) return ret; @@ -29,13 +31,13 @@ static struct sysreset_ops wdt_reboot_ops = { .request = wdt_reboot_request, }; -int wdt_reboot_probe(struct udevice *dev) +static int wdt_reboot_of_to_plat(struct udevice *dev) { - struct wdt_reboot_priv *priv = dev_get_priv(dev); + struct wdt_reboot_plat *plat = dev_get_plat(dev); int err; err = uclass_get_device_by_phandle(UCLASS_WDT, dev, - "wdt", &priv->wdt); + "wdt", &plat->wdt); if (err) { pr_err("unable to find wdt device\n"); return err; @@ -53,7 +55,29 @@ U_BOOT_DRIVER(wdt_reboot) = { .name = "wdt_reboot", .id = UCLASS_SYSRESET, .of_match = wdt_reboot_ids, + .of_to_plat = wdt_reboot_of_to_plat, + .plat_auto = sizeof(struct wdt_reboot_plat), .ops = &wdt_reboot_ops, - .priv_auto = sizeof(struct wdt_reboot_priv), - .probe = wdt_reboot_probe, }; + +#if IS_ENABLED(CONFIG_SYSRESET_WATCHDOG_AUTO) +int sysreset_register_wdt(struct udevice *dev) +{ + struct wdt_reboot_plat *plat = malloc(sizeof(*plat)); + int ret; + + if (!plat) + return -ENOMEM; + + plat->wdt = dev; + + ret = device_bind(dev, DM_DRIVER_GET(wdt_reboot), + dev->name, plat, ofnode_null(), NULL); + if (ret) { + free(plat); + return ret; + } + + return 0; +} +#endif diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index 9eebab5cfd9..406ee8716e1 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -161,6 +161,15 @@ config TPM2_FTPM_TEE help This driver supports firmware TPM running in TEE. +config TPM2_MMIO + bool "MMIO based TPM2 Interface" + depends on TPM_V2 + help + This driver supports firmware TPM2.0 MMIO interface. + The usual TPM operations and the 'tpm' command can be used to talk + to the device using the standard TPM Interface Specification (TIS) + protocol. + endif # TPM_V2 endmenu diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index c65be526700..51725230c78 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o obj-$(CONFIG_$(SPL_TPL_)TPM2_CR50_I2C) += cr50_i2c.o obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o sandbox_common.o -obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o +obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_core.o tpm2_tis_spi.o obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o +obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o diff --git a/drivers/tpm/tpm2_tis_core.c b/drivers/tpm/tpm2_tis_core.c new file mode 100644 index 00000000000..ec8c730fe90 --- /dev/null +++ b/drivers/tpm/tpm2_tis_core.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, Linaro Limited + * + * Based on the Linux TIS core interface and U-Boot original SPI TPM driver + */ + +#include <common.h> +#include <dm.h> +#include <tpm-v2.h> +#include <linux/delay.h> +#include <linux/unaligned/be_byteshift.h> +#include "tpm_tis.h" + +int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) +{ + struct tpm_chip *chip = dev_get_priv(dev); + + if (size < 80) + return -ENOSPC; + + return snprintf(buf, size, + "%s v2.0: VendorID 0x%04x, DeviceID 0x%04x, RevisionID 0x%02x [%s]", + dev->name, chip->vend_dev & 0xFFFF, + chip->vend_dev >> 16, chip->rid, + (chip->is_open ? "open" : "closed")); +} + +/** + * tpm_tis_check_locality - Check the current TPM locality + * + * @dev: TPM device + * @loc: locality + * + * Return: True if the tested locality matches + */ +static bool tpm_tis_check_locality(struct udevice *dev, int loc) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + u8 locality; + + phy_ops->read_bytes(dev, TPM_ACCESS(loc), 1, &locality); + if ((locality & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID | + TPM_ACCESS_REQUEST_USE)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { + chip->locality = loc; + return true; + } + + return false; +} + +/** + * tpm_tis_request_locality - Request a locality from the TPM + * + * @dev: TPM device + * @loc: requested locality + * + * Return: 0 on success -1 on failure + */ +int tpm_tis_request_locality(struct udevice *dev, int loc) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + u8 buf = TPM_ACCESS_REQUEST_USE; + unsigned long start, stop; + + if (tpm_tis_check_locality(dev, loc)) + return 0; + + phy_ops->write_bytes(dev, TPM_ACCESS(loc), 1, &buf); + start = get_timer(0); + stop = chip->timeout_a; + do { + if (tpm_tis_check_locality(dev, loc)) + return 0; + mdelay(TPM_TIMEOUT_MS); + } while (get_timer(start) < stop); + + return -1; +} + +/** + * tpm_tis_status - Check the current device status + * + * @dev: TPM device + * @status: return value of status + * + * Return: 0 on success, negative on failure + */ +static int tpm_tis_status(struct udevice *dev, u8 *status) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + + if (chip->locality < 0) + return -EINVAL; + + phy_ops->read_bytes(dev, TPM_STS(chip->locality), 1, status); + + if ((*status & TPM_STS_READ_ZERO)) { + log_err("TPM returned invalid status\n"); + return -EINVAL; + } + + return 0; +} + +/** + * tpm_tis_release_locality - Release the requested locality + * + * @dev: TPM device + * @loc: requested locality + * + * Return: 0 on success, negative on failure + */ +int tpm_tis_release_locality(struct udevice *dev, int loc) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + u8 buf = TPM_ACCESS_ACTIVE_LOCALITY; + int ret; + + if (chip->locality < 0) + return 0; + + ret = phy_ops->write_bytes(dev, TPM_ACCESS(loc), 1, &buf); + chip->locality = -1; + + return ret; +} + +/** + * tpm_tis_wait_for_stat - Wait for TPM to become ready + * + * @dev: TPM device + * @mask: mask to match + * @timeout: timeout for retries + * @status: current status + * + * Return: 0 on success, negative on failure + */ +static int tpm_tis_wait_for_stat(struct udevice *dev, u8 mask, + unsigned long timeout, u8 *status) +{ + unsigned long start = get_timer(0); + unsigned long stop = timeout; + int ret; + + do { + mdelay(TPM_TIMEOUT_MS); + ret = tpm_tis_status(dev, status); + if (ret) + return ret; + + if ((*status & mask) == mask) + return 0; + } while (get_timer(start) < stop); + + return -ETIMEDOUT; +} + +/** + * tpm_tis_get_burstcount - Get the burstcount for the data FIFO + * + * @dev: TPM device + * @burstcount: current burstcount + * + * Return: 0 on success, negative on failure + */ +static int tpm_tis_get_burstcount(struct udevice *dev, size_t *burstcount) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + unsigned long start, stop; + u32 burst; + + if (chip->locality < 0) + return -EINVAL; + + /* wait for burstcount */ + start = get_timer(0); + /* + * This is the TPMv2 defined timeout. Change this in case you want to + * make the driver compatile to TPMv1 + */ + stop = chip->timeout_a; + do { + phy_ops->read32(dev, TPM_STS(chip->locality), &burst); + *burstcount = (burst >> 8) & 0xFFFF; + if (*burstcount) + return 0; + + mdelay(TPM_TIMEOUT_MS); + } while (get_timer(start) < stop); + + return -ETIMEDOUT; +} + +/** + * tpm_tis_ready - Cancel pending comands and get the device on a ready state + * + * @dev: TPM device + * + * Return: 0 on success, negative on failure + */ +static int tpm_tis_ready(struct udevice *dev) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + u8 data = TPM_STS_COMMAND_READY; + + /* This will cancel any pending commands */ + return phy_ops->write_bytes(dev, TPM_STS(chip->locality), 1, &data); +} + +int tpm_tis_send(struct udevice *dev, const u8 *buf, size_t len) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + size_t burstcnt, wr_size, sent = 0; + u8 data = TPM_STS_GO; + u8 status; + int ret; + + if (!chip) + return -ENODEV; + + ret = tpm_tis_request_locality(dev, 0); + if (ret < 0) + return -EBUSY; + + ret = tpm_tis_status(dev, &status); + if (ret) + goto release_locality; + + if (!(status & TPM_STS_COMMAND_READY)) { + ret = tpm_tis_ready(dev); + if (ret) { + log_err("Can't cancel previous TPM operation\n"); + goto release_locality; + } + ret = tpm_tis_wait_for_stat(dev, TPM_STS_COMMAND_READY, + chip->timeout_b, &status); + if (ret) { + log_err("TPM not ready\n"); + goto release_locality; + } + } + + while (len > 0) { + ret = tpm_tis_get_burstcount(dev, &burstcnt); + if (ret) + goto release_locality; + + wr_size = min(len, burstcnt); + ret = phy_ops->write_bytes(dev, TPM_DATA_FIFO(chip->locality), + wr_size, buf + sent); + if (ret < 0) + goto release_locality; + + ret = tpm_tis_wait_for_stat(dev, TPM_STS_VALID, + chip->timeout_c, &status); + if (ret) + goto release_locality; + + sent += wr_size; + len -= wr_size; + /* make sure the TPM expects more data */ + if (len && !(status & TPM_STS_DATA_EXPECT)) { + ret = -EIO; + goto release_locality; + } + } + + /* + * Make a final check ensuring everything is ok and the TPM expects no + * more data + */ + ret = tpm_tis_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c, + &status); + if (ret) + goto release_locality; + + if (status & TPM_STS_DATA_EXPECT) { + ret = -EIO; + goto release_locality; + } + + ret = phy_ops->write_bytes(dev, TPM_STS(chip->locality), 1, &data); + if (ret) + goto release_locality; + + return sent; + +release_locality: + tpm_tis_ready(dev); + tpm_tis_release_locality(dev, chip->locality); + + return ret; +} + +static int tpm_tis_recv_data(struct udevice *dev, u8 *buf, size_t count) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + int size = 0, len, ret; + size_t burstcnt; + u8 status; + + while (size < count && + tpm_tis_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->timeout_c, &status) == 0) { + ret = tpm_tis_get_burstcount(dev, &burstcnt); + if (ret) + return ret; + + len = min_t(int, burstcnt, count - size); + ret = phy_ops->read_bytes(dev, TPM_DATA_FIFO(chip->locality), + len, buf + size); + if (ret < 0) + return ret; + + size += len; + } + + return size; +} + +/** + * tpm_tis_recv - Receive data from a device + * + * @dev: TPM device + * @buf: buffer to copy data + * @size: buffer size + * + * Return: bytes read or negative on failure + */ +int tpm_tis_recv(struct udevice *dev, u8 *buf, size_t count) +{ + struct tpm_chip *chip = dev_get_priv(dev); + int size, expected; + + if (count < TPM_HEADER_SIZE) + return -E2BIG; + + size = tpm_tis_recv_data(dev, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + log_err("TPM error, unable to read header\n"); + goto out; + } + + expected = get_unaligned_be32(buf + TPM_CMD_COUNT_OFFSET); + if (expected > count) { + size = -EIO; + log_warning("Too much data: %d > %zu\n", expected, count); + goto out; + } + + size += tpm_tis_recv_data(dev, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + log(LOGC_NONE, LOGL_ERR, + "TPM error, unable to read remaining bytes of result\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_ready(dev); + /* acquired in tpm_tis_send */ + tpm_tis_release_locality(dev, chip->locality); + + return size; +} + +int tpm_tis_cleanup(struct udevice *dev) +{ + struct tpm_chip *chip = dev_get_priv(dev); + + tpm_tis_ready(dev); + tpm_tis_release_locality(dev, chip->locality); + + return 0; +} + +int tpm_tis_open(struct udevice *dev) +{ + struct tpm_chip *chip = dev_get_priv(dev); + int ret; + + if (chip->is_open) + return -EBUSY; + + ret = tpm_tis_request_locality(dev, 0); + if (!ret) + chip->is_open = 1; + + return ret; +} + +void tpm_tis_ops_register(struct udevice *dev, struct tpm_tis_phy_ops *ops) +{ + struct tpm_chip *chip = dev_get_priv(dev); + + chip->phy_ops = ops; +} + +static bool tis_check_ops(struct tpm_tis_phy_ops *phy_ops) +{ + if (!phy_ops || !phy_ops->read_bytes || !phy_ops->write_bytes || + !phy_ops->read32 || !phy_ops->write32) + return false; + + return true; +} + +int tpm_tis_init(struct udevice *dev) +{ + struct tpm_chip *chip = dev_get_priv(dev); + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; + int ret; + u32 tmp; + + if (!tis_check_ops(phy_ops)) { + log_err("Driver bug. No bus ops defined\n"); + return -1; + } + ret = tpm_tis_request_locality(dev, 0); + if (ret) + return ret; + + chip->timeout_a = TIS_SHORT_TIMEOUT_MS; + chip->timeout_b = TIS_LONG_TIMEOUT_MS; + chip->timeout_c = TIS_SHORT_TIMEOUT_MS; + chip->timeout_d = TIS_SHORT_TIMEOUT_MS; + + /* Disable interrupts */ + phy_ops->read32(dev, TPM_INT_ENABLE(chip->locality), &tmp); + tmp |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | + TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; + tmp &= ~TPM_GLOBAL_INT_ENABLE; + phy_ops->write32(dev, TPM_INT_ENABLE(chip->locality), tmp); + + phy_ops->read_bytes(dev, TPM_RID(chip->locality), 1, &chip->rid); + phy_ops->read32(dev, TPM_DID_VID(chip->locality), &chip->vend_dev); + + return tpm_tis_release_locality(dev, chip->locality); +} + +int tpm_tis_close(struct udevice *dev) +{ + struct tpm_chip *chip = dev_get_priv(dev); + int ret = 0; + + if (chip->is_open) { + ret = tpm_tis_release_locality(dev, chip->locality); + chip->is_open = 0; + } + + return ret; +} diff --git a/drivers/tpm/tpm2_tis_mmio.c b/drivers/tpm/tpm2_tis_mmio.c new file mode 100644 index 00000000000..9cedff22250 --- /dev/null +++ b/drivers/tpm/tpm2_tis_mmio.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * driver for mmio TCG/TIS TPM (trusted platform module). + * + * Specifications at www.trustedcomputinggroup.org + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <tpm-v2.h> +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/unaligned/be_byteshift.h> +#include "tpm_tis.h" +#include "tpm_internal.h" + +/** + * struct tpm_tis_chip_data - Information about an MMIO TPM + * @pcr_count: Number of PCR per bank + * @pcr_select_min: Minimum size in bytes of the pcrSelect array + * @iobase: Base address + */ +struct tpm_tis_chip_data { + unsigned int pcr_count; + unsigned int pcr_select_min; + void __iomem *iobase; +}; + +static int mmio_read_bytes(struct udevice *dev, u32 addr, u16 len, + u8 *result) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); + + while (len--) + *result++ = ioread8(drv_data->iobase + addr); + + return 0; +} + +static int mmio_write_bytes(struct udevice *dev, u32 addr, u16 len, + const u8 *value) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); + + while (len--) + iowrite8(*value++, drv_data->iobase + addr); + + return 0; +} + +static int mmio_read32(struct udevice *dev, u32 addr, u32 *result) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); + + *result = ioread32(drv_data->iobase + addr); + + return 0; +} + +static int mmio_write32(struct udevice *dev, u32 addr, u32 value) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); + + iowrite32(value, drv_data->iobase + addr); + + return 0; +} + +static struct tpm_tis_phy_ops phy_ops = { + .read_bytes = mmio_read_bytes, + .write_bytes = mmio_write_bytes, + .read32 = mmio_read32, + .write32 = mmio_write32, +}; + +static int tpm_tis_probe(struct udevice *dev) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); + struct tpm_chip_priv *priv = dev_get_uclass_priv(dev); + int ret = 0; + fdt_addr_t ioaddr; + u64 sz; + + ioaddr = dev_read_addr(dev); + if (ioaddr == FDT_ADDR_T_NONE) + return log_msg_ret("ioaddr", -EINVAL); + + ret = dev_read_u64(dev, "reg", &sz); + if (ret) + return -EINVAL; + + drv_data->iobase = ioremap(ioaddr, sz); + tpm_tis_ops_register(dev, &phy_ops); + ret = tpm_tis_init(dev); + if (ret) + goto iounmap; + + priv->pcr_count = drv_data->pcr_count; + priv->pcr_select_min = drv_data->pcr_select_min; + /* + * Although the driver probably works with a TPMv1 our Kconfig + * limits the driver to TPMv2 only + */ + priv->version = TPM_V2; + + return ret; +iounmap: + iounmap(drv_data->iobase); + + return -EINVAL; +} + +static int tpm_tis_remove(struct udevice *dev) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); + + iounmap(drv_data->iobase); + + return tpm_tis_cleanup(dev); +} + +static const struct tpm_ops tpm_tis_ops = { + .open = tpm_tis_open, + .close = tpm_tis_close, + .get_desc = tpm_tis_get_desc, + .send = tpm_tis_send, + .recv = tpm_tis_recv, + .cleanup = tpm_tis_cleanup, +}; + +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = { + .pcr_count = 24, + .pcr_select_min = 3, +}; + +static const struct udevice_id tpm_tis_ids[] = { + { + .compatible = "tcg,tpm-tis-mmio", + .data = (ulong)&tpm_tis_std_chip_data, + }, + { } +}; + +U_BOOT_DRIVER(tpm_tis_mmio) = { + .name = "tpm_tis_mmio", + .id = UCLASS_TPM, + .of_match = tpm_tis_ids, + .ops = &tpm_tis_ops, + .probe = tpm_tis_probe, + .remove = tpm_tis_remove, + .priv_auto = sizeof(struct tpm_chip), +}; diff --git a/drivers/tpm/tpm2_tis_spi.c b/drivers/tpm/tpm2_tis_spi.c index 1d24d32d867..58b6f335105 100644 --- a/drivers/tpm/tpm2_tis_spi.c +++ b/drivers/tpm/tpm2_tis_spi.c @@ -30,13 +30,6 @@ #include "tpm_tis.h" #include "tpm_internal.h" -#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) -#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) -#define TPM_STS(l) (0x0018 | ((l) << 12)) -#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) -#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) -#define TPM_RID(l) (0x0F04 | ((l) << 12)) - #define MAX_SPI_FRAMESIZE 64 /* Number of wait states to wait for */ @@ -165,7 +158,7 @@ release_bus: return ret; } -static int tpm_tis_spi_read(struct udevice *dev, u16 addr, u8 *in, u16 len) +static int tpm_tis_spi_read(struct udevice *dev, u32 addr, u16 len, u8 *in) { return tpm_tis_spi_xfer(dev, addr, NULL, in, len); } @@ -175,382 +168,24 @@ static int tpm_tis_spi_read32(struct udevice *dev, u32 addr, u32 *result) __le32 result_le; int ret; - ret = tpm_tis_spi_read(dev, addr, (u8 *)&result_le, sizeof(u32)); + ret = tpm_tis_spi_read(dev, addr, sizeof(u32), (u8 *)&result_le); if (!ret) *result = le32_to_cpu(result_le); return ret; } -static int tpm_tis_spi_write(struct udevice *dev, u16 addr, const u8 *out, - u16 len) -{ - return tpm_tis_spi_xfer(dev, addr, out, NULL, len); -} - -static int tpm_tis_spi_check_locality(struct udevice *dev, int loc) -{ - const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID; - struct tpm_chip *chip = dev_get_priv(dev); - u8 buf; - int ret; - - ret = tpm_tis_spi_read(dev, TPM_ACCESS(loc), &buf, 1); - if (ret) - return ret; - - if ((buf & mask) == mask) { - chip->locality = loc; - return 0; - } - - return -ENOENT; -} - -static void tpm_tis_spi_release_locality(struct udevice *dev, int loc, - bool force) -{ - const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID; - u8 buf; - if (tpm_tis_spi_read(dev, TPM_ACCESS(loc), &buf, 1) < 0) - return; - - if (force || (buf & mask) == mask) { - buf = TPM_ACCESS_ACTIVE_LOCALITY; - tpm_tis_spi_write(dev, TPM_ACCESS(loc), &buf, 1); - } -} - -static int tpm_tis_spi_request_locality(struct udevice *dev, int loc) +static int tpm_tis_spi_write(struct udevice *dev, u32 addr, u16 len, const u8 *out) { - struct tpm_chip *chip = dev_get_priv(dev); - unsigned long start, stop; - u8 buf = TPM_ACCESS_REQUEST_USE; - int ret; - - ret = tpm_tis_spi_check_locality(dev, loc); - if (!ret) - return 0; - - if (ret != -ENOENT) { - log(LOGC_NONE, LOGL_ERR, "%s: Failed to get locality: %d\n", - __func__, ret); - return ret; - } - - ret = tpm_tis_spi_write(dev, TPM_ACCESS(loc), &buf, 1); - if (ret) { - log(LOGC_NONE, LOGL_ERR, "%s: Failed to write to TPM: %d\n", - __func__, ret); - return ret; - } - - start = get_timer(0); - stop = chip->timeout_a; - do { - ret = tpm_tis_spi_check_locality(dev, loc); - if (!ret) - return 0; - - if (ret != -ENOENT) { - log(LOGC_NONE, LOGL_ERR, - "%s: Failed to get locality: %d\n", __func__, ret); - return ret; - } - - mdelay(TPM_TIMEOUT_MS); - } while (get_timer(start) < stop); - - log(LOGC_NONE, LOGL_ERR, "%s: Timeout getting locality: %d\n", __func__, - ret); - - return ret; -} - -static u8 tpm_tis_spi_status(struct udevice *dev, u8 *status) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - return tpm_tis_spi_read(dev, TPM_STS(chip->locality), status, 1); -} - -static int tpm_tis_spi_wait_for_stat(struct udevice *dev, u8 mask, - unsigned long timeout, u8 *status) -{ - unsigned long start = get_timer(0); - unsigned long stop = timeout; - int ret; - - do { - mdelay(TPM_TIMEOUT_MS); - ret = tpm_tis_spi_status(dev, status); - if (ret) - return ret; - - if ((*status & mask) == mask) - return 0; - } while (get_timer(start) < stop); - - return -ETIMEDOUT; -} - -static u8 tpm_tis_spi_valid_status(struct udevice *dev, u8 *status) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - return tpm_tis_spi_wait_for_stat(dev, TPM_STS_VALID, - chip->timeout_c, status); -} - -static int tpm_tis_spi_get_burstcount(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - unsigned long start, stop; - u32 burstcount, ret; - - /* wait for burstcount */ - start = get_timer(0); - stop = chip->timeout_d; - do { - ret = tpm_tis_spi_read32(dev, TPM_STS(chip->locality), - &burstcount); - if (ret) - return -EBUSY; - - burstcount = (burstcount >> 8) & 0xFFFF; - if (burstcount) - return burstcount; - - mdelay(TPM_TIMEOUT_MS); - } while (get_timer(start) < stop); - - return -EBUSY; -} - -static int tpm_tis_spi_cancel(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - u8 data = TPM_STS_COMMAND_READY; - - return tpm_tis_spi_write(dev, TPM_STS(chip->locality), &data, 1); -} - -static int tpm_tis_spi_recv_data(struct udevice *dev, u8 *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int size = 0, burstcnt, len, ret; - u8 status; - - while (size < count && - tpm_tis_spi_wait_for_stat(dev, - TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->timeout_c, &status) == 0) { - burstcnt = tpm_tis_spi_get_burstcount(dev); - if (burstcnt < 0) - return burstcnt; - - len = min_t(int, burstcnt, count - size); - ret = tpm_tis_spi_read(dev, TPM_DATA_FIFO(chip->locality), - buf + size, len); - if (ret < 0) - return ret; - - size += len; - } - - return size; -} - -static int tpm_tis_spi_recv(struct udevice *dev, u8 *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int size, expected; - - if (!chip) - return -ENODEV; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size = tpm_tis_spi_recv_data(dev, buf, TPM_HEADER_SIZE); - if (size < TPM_HEADER_SIZE) { - log(LOGC_NONE, LOGL_ERR, "TPM error, unable to read header\n"); - goto out; - } - - expected = get_unaligned_be32(buf + 2); - if (expected > count) { - size = -EIO; - goto out; - } - - size += tpm_tis_spi_recv_data(dev, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - log(LOGC_NONE, LOGL_ERR, - "TPM error, unable to read remaining bytes of result\n"); - size = -EIO; - goto out; - } - -out: - tpm_tis_spi_cancel(dev); - tpm_tis_spi_release_locality(dev, chip->locality, false); - - return size; -} - -static int tpm_tis_spi_send(struct udevice *dev, const u8 *buf, size_t len) -{ - struct tpm_chip *chip = dev_get_priv(dev); - u32 i, size; - u8 status; - int burstcnt, ret; - u8 data; - - if (!chip) - return -ENODEV; - - if (len > TPM_DEV_BUFSIZE) - return -E2BIG; /* Command is too long for our tpm, sorry */ - - ret = tpm_tis_spi_request_locality(dev, 0); - if (ret < 0) - return -EBUSY; - - /* - * Check if the TPM is ready. If not, if not, cancel the pending command - * and poll on the status to be finally ready. - */ - ret = tpm_tis_spi_status(dev, &status); - if (ret) - return ret; - - if (!(status & TPM_STS_COMMAND_READY)) { - /* Force the transition, usually this will be done at startup */ - ret = tpm_tis_spi_cancel(dev); - if (ret) { - log(LOGC_NONE, LOGL_ERR, - "%s: Could not cancel previous operation\n", - __func__); - goto out_err; - } - - ret = tpm_tis_spi_wait_for_stat(dev, TPM_STS_COMMAND_READY, - chip->timeout_b, &status); - if (ret < 0 || !(status & TPM_STS_COMMAND_READY)) { - log(LOGC_NONE, LOGL_ERR, - "status %d after wait for stat returned %d\n", - status, ret); - goto out_err; - } - } - - for (i = 0; i < len - 1;) { - burstcnt = tpm_tis_spi_get_burstcount(dev); - if (burstcnt < 0) - return burstcnt; - - size = min_t(int, len - i - 1, burstcnt); - ret = tpm_tis_spi_write(dev, TPM_DATA_FIFO(chip->locality), - buf + i, size); - if (ret < 0) - goto out_err; - - i += size; - } - - ret = tpm_tis_spi_valid_status(dev, &status); - if (ret) - goto out_err; - - if ((status & TPM_STS_DATA_EXPECT) == 0) { - ret = -EIO; - goto out_err; - } - - ret = tpm_tis_spi_write(dev, TPM_DATA_FIFO(chip->locality), - buf + len - 1, 1); - if (ret) - goto out_err; - - ret = tpm_tis_spi_valid_status(dev, &status); - if (ret) - goto out_err; - - if ((status & TPM_STS_DATA_EXPECT) != 0) { - ret = -EIO; - goto out_err; - } - - data = TPM_STS_GO; - ret = tpm_tis_spi_write(dev, TPM_STS(chip->locality), &data, 1); - if (ret) - goto out_err; - - return len; - -out_err: - tpm_tis_spi_cancel(dev); - tpm_tis_spi_release_locality(dev, chip->locality, false); - - return ret; -} - -static int tpm_tis_spi_cleanup(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - tpm_tis_spi_cancel(dev); - /* - * The TPM needs some time to clean up here, - * so we sleep rather than keeping the bus busy - */ - mdelay(2); - tpm_tis_spi_release_locality(dev, chip->locality, false); - - return 0; -} - -static int tpm_tis_spi_open(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - if (chip->is_open) - return -EBUSY; - - chip->is_open = 1; - - return 0; -} - -static int tpm_tis_spi_close(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - if (chip->is_open) { - tpm_tis_spi_release_locality(dev, chip->locality, true); - chip->is_open = 0; - } - - return 0; + return tpm_tis_spi_xfer(dev, addr, out, NULL, len); } -static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) +static int tpm_tis_spi_write32(struct udevice *dev, u32 addr, u32 value) { - struct tpm_chip *chip = dev_get_priv(dev); - - if (size < 80) - return -ENOSPC; + __le32 value_le = cpu_to_le32(value); - return snprintf(buf, size, - "%s v2.0: VendorID 0x%04x, DeviceID 0x%04x, RevisionID 0x%02x [%s]", - dev->name, chip->vend_dev & 0xFFFF, - chip->vend_dev >> 16, chip->rid, - (chip->is_open ? "open" : "closed")); + return tpm_tis_spi_write(dev, addr, sizeof(value), (u8 *)&value_le); } static int tpm_tis_wait_init(struct udevice *dev, int loc) @@ -565,7 +200,7 @@ static int tpm_tis_wait_init(struct udevice *dev, int loc) do { mdelay(TPM_TIMEOUT_MS); - ret = tpm_tis_spi_read(dev, TPM_ACCESS(loc), &status, 1); + ret = tpm_tis_spi_read(dev, TPM_ACCESS(loc), 1, &status); if (ret) break; @@ -576,6 +211,13 @@ static int tpm_tis_wait_init(struct udevice *dev, int loc) return -EIO; } +static struct tpm_tis_phy_ops phy_ops = { + .read_bytes = tpm_tis_spi_read, + .write_bytes = tpm_tis_spi_write, + .read32 = tpm_tis_spi_read32, + .write32 = tpm_tis_spi_write32, +}; + static int tpm_tis_spi_probe(struct udevice *dev) { struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); @@ -611,65 +253,38 @@ init: /* Ensure a minimum amount of time elapsed since reset of the TPM */ mdelay(drv_data->time_before_first_cmd_ms); - chip->locality = 0; - chip->timeout_a = TIS_SHORT_TIMEOUT_MS; - chip->timeout_b = TIS_LONG_TIMEOUT_MS; - chip->timeout_c = TIS_SHORT_TIMEOUT_MS; - chip->timeout_d = TIS_SHORT_TIMEOUT_MS; - priv->pcr_count = drv_data->pcr_count; - priv->pcr_select_min = drv_data->pcr_select_min; - ret = tpm_tis_wait_init(dev, chip->locality); if (ret) { log(LOGC_DM, LOGL_ERR, "%s: no device found\n", __func__); return ret; } - ret = tpm_tis_spi_request_locality(dev, chip->locality); - if (ret) { - log(LOGC_NONE, LOGL_ERR, "%s: could not request locality %d\n", - __func__, chip->locality); - return ret; - } - - ret = tpm_tis_spi_read32(dev, TPM_DID_VID(chip->locality), - &chip->vend_dev); - if (ret) { - log(LOGC_NONE, LOGL_ERR, - "%s: could not retrieve VendorID/DeviceID\n", __func__); - return ret; - } - - ret = tpm_tis_spi_read(dev, TPM_RID(chip->locality), &chip->rid, 1); - if (ret) { - log(LOGC_NONE, LOGL_ERR, "%s: could not retrieve RevisionID\n", - __func__); - return ret; - } + tpm_tis_ops_register(dev, &phy_ops); + ret = tpm_tis_init(dev); + if (ret) + goto err; - log(LOGC_NONE, LOGL_ERR, - "SPI TPMv2.0 found (vid:%04x, did:%04x, rid:%02x)\n", - chip->vend_dev & 0xFFFF, chip->vend_dev >> 16, chip->rid); + priv->pcr_count = drv_data->pcr_count; + priv->pcr_select_min = drv_data->pcr_select_min; + priv->version = TPM_V2; return 0; +err: + return -EINVAL; } -static int tpm_tis_spi_remove(struct udevice *dev) +static int tpm_tis_spi_remove(struct udevice *udev) { - struct tpm_chip *chip = dev_get_priv(dev); - - tpm_tis_spi_release_locality(dev, chip->locality, true); - - return 0; + return tpm_tis_cleanup(udev); } static const struct tpm_ops tpm_tis_spi_ops = { - .open = tpm_tis_spi_open, - .close = tpm_tis_spi_close, + .open = tpm_tis_open, + .close = tpm_tis_close, .get_desc = tpm_tis_get_desc, - .send = tpm_tis_spi_send, - .recv = tpm_tis_spi_recv, - .cleanup = tpm_tis_spi_cleanup, + .send = tpm_tis_send, + .recv = tpm_tis_recv, + .cleanup = tpm_tis_cleanup, }; static const struct tpm_tis_chip_data tpm_tis_std_chip_data = { diff --git a/drivers/tpm/tpm_tis.h b/drivers/tpm/tpm_tis.h index 2a160fe05c9..93f622f2910 100644 --- a/drivers/tpm/tpm_tis.h +++ b/drivers/tpm/tpm_tis.h @@ -21,6 +21,73 @@ #include <linux/compiler.h> #include <linux/types.h> +/** + * struct tpm_tis_phy_ops - low-level TPM bus operations + */ +struct tpm_tis_phy_ops { + /* read_bytes() - Read a number of bytes from the device + * + * @udev: TPM device + * @addr: offset from device base + * @len: len to read + * @result: data read + * + * @return: 0 on success, negative on failure + */ + int (*read_bytes)(struct udevice *udev, u32 addr, u16 len, + u8 *result); + /* write_bytes() - Read a number of bytes from the device + * + * @udev: TPM device + * @addr: offset from device base + * @len: len to read + * @value: data to write + * + * @return: 0 on success, negative on failure + */ + int (*write_bytes)(struct udevice *udev, u32 addr, u16 len, + const u8 *value); + /* read32() - Read a 32bit value of the device + * + * @udev: TPM device + * @addr: offset from device base + * @result: data read + * + * @return: 0 on success, negative on failure + */ + int (*read32)(struct udevice *udev, u32 addr, u32 *result); + /* write32() - write a 32bit value to the device + * + * @udev: TPM device + * @addr: offset from device base + * @src: data to write + * + * @return: 0 on success, negative on failure + */ + int (*write32)(struct udevice *udev, u32 addr, u32 src); +}; + +enum tis_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80000000, + TPM_INTF_BURST_COUNT_STATIC = 0x100, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_INT_EDGE_FALLING = 0x040, + TPM_INTF_INT_EDGE_RISING = 0x020, + TPM_INTF_INT_LEVEL_LOW = 0x010, + TPM_INTF_INT_LEVEL_HIGH = 0x008, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) +#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) +#define TPM_STS(l) (0x0018 | ((l) << 12)) +#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) +#define TPM_DID_VID(l) (0x0f00 | ((l) << 12)) +#define TPM_RID(l) (0x0f04 | ((l) << 12)) +#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) + enum tpm_timeout { TPM_TIMEOUT_MS = 5, TIS_SHORT_TIMEOUT_MS = 750, @@ -43,6 +110,7 @@ struct tpm_chip { u8 rid; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ ulong chip_type; + struct tpm_tis_phy_ops *phy_ops; }; struct tpm_input_header { @@ -130,4 +198,72 @@ enum tis_status { }; #endif +/** + * tpm_tis_open - Open the device and request locality 0 + * + * @dev: TPM device + * + * @return: 0 on success, negative on failure + */ +int tpm_tis_open(struct udevice *udev); +/** + * tpm_tis_close - Close the device and release locality + * + * @dev: TPM device + * + * @return: 0 on success, negative on failure + */ +int tpm_tis_close(struct udevice *udev); +/** tpm_tis_cleanup - Get the device in ready state and release locality + * + * @dev: TPM device + * + * @return: always 0 + */ +int tpm_tis_cleanup(struct udevice *udev); +/** + * tpm_tis_send - send data to the device + * + * @dev: TPM device + * @buf: buffer to send + * @len: size of the buffer + * + * @return: number of bytes sent or negative on failure + */ +int tpm_tis_send(struct udevice *udev, const u8 *buf, size_t len); +/** + * tpm_tis_recv_data - Receive data from a device. Wrapper for tpm_tis_recv + * + * @dev: TPM device + * @buf: buffer to copy data + * @size: buffer size + * + * @return: bytes read or negative on failure + */ +int tpm_tis_recv(struct udevice *udev, u8 *buf, size_t count); +/** + * tpm_tis_get_desc - Get the TPM description + * + * @dev: TPM device + * @buf: buffer to fill data + * @size: buffer size + * + * @return: Number of characters written (or would have been written) in buffer + */ +int tpm_tis_get_desc(struct udevice *udev, char *buf, int size); +/** + * tpm_tis_init - inititalize the device + * + * @dev: TPM device + * + * @return: 0 on success, negative on failure + */ +int tpm_tis_init(struct udevice *udev); +/** + * tpm_tis_ops_register - register the PHY ops for the device + * + * @dev: TPM device + * @ops: tpm_tis_phy_ops ops for the device + */ +void tpm_tis_ops_register(struct udevice *udev, struct tpm_tis_phy_ops *ops); #endif diff --git a/drivers/tpm/tpm_tis_infineon.c b/drivers/tpm/tpm_tis_infineon.c index f414e5657db..525ad72f4c9 100644 --- a/drivers/tpm/tpm_tis_infineon.c +++ b/drivers/tpm/tpm_tis_infineon.c @@ -50,10 +50,10 @@ static const char * const chip_name[] = { [UNKNOWN] = "unknown/fallback to slb9635", }; -#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) -#define TPM_STS(l) (0x0001 | ((l) << 4)) -#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) -#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) +#define TPM_INFINEON_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_INFINEON_STS(l) (0x0001 | ((l) << 4)) +#define TPM_INFINEON_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_INFINEON_DID_VID(l) (0x0006 | ((l) << 4)) /* * tpm_tis_i2c_read() - read from TPM register @@ -197,7 +197,7 @@ static int tpm_tis_i2c_check_locality(struct udevice *dev, int loc) u8 buf; int rc; - rc = tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1); + rc = tpm_tis_i2c_read(dev, TPM_INFINEON_ACCESS(loc), &buf, 1); if (rc < 0) return rc; @@ -215,12 +215,12 @@ static void tpm_tis_i2c_release_locality(struct udevice *dev, int loc, const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID; u8 buf; - if (tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1) < 0) + if (tpm_tis_i2c_read(dev, TPM_INFINEON_ACCESS(loc), &buf, 1) < 0) return; if (force || (buf & mask) == mask) { buf = TPM_ACCESS_ACTIVE_LOCALITY; - tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1); + tpm_tis_i2c_write(dev, TPM_INFINEON_ACCESS(loc), &buf, 1); } } @@ -240,7 +240,7 @@ static int tpm_tis_i2c_request_locality(struct udevice *dev, int loc) return rc; } - rc = tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1); + rc = tpm_tis_i2c_write(dev, TPM_INFINEON_ACCESS(loc), &buf, 1); if (rc) { debug("%s: Failed to write to TPM: %d\n", __func__, rc); return rc; @@ -271,7 +271,7 @@ static u8 tpm_tis_i2c_status(struct udevice *dev) /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */ u8 buf; - if (tpm_tis_i2c_read(dev, TPM_STS(chip->locality), &buf, 1) < 0) + if (tpm_tis_i2c_read(dev, TPM_INFINEON_STS(chip->locality), &buf, 1) < 0) return 0; else return buf; @@ -286,7 +286,7 @@ static int tpm_tis_i2c_ready(struct udevice *dev) u8 buf = TPM_STS_COMMAND_READY; debug("%s\n", __func__); - rc = tpm_tis_i2c_write_long(dev, TPM_STS(chip->locality), &buf, 1); + rc = tpm_tis_i2c_write_long(dev, TPM_INFINEON_STS(chip->locality), &buf, 1); if (rc) debug("%s: rc=%d\n", __func__, rc); @@ -306,7 +306,7 @@ static ssize_t tpm_tis_i2c_get_burstcount(struct udevice *dev) stop = chip->timeout_d; do { /* Note: STS is little endian */ - addr = TPM_STS(chip->locality) + 1; + addr = TPM_INFINEON_STS(chip->locality) + 1; if (tpm_tis_i2c_read(dev, addr, buf, 3) < 0) burstcnt = 0; else @@ -360,7 +360,7 @@ static int tpm_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count) if (burstcnt > (count - size)) burstcnt = count - size; - rc = tpm_tis_i2c_read(dev, TPM_DATA_FIFO(chip->locality), + rc = tpm_tis_i2c_read(dev, TPM_INFINEON_DATA_FIFO(chip->locality), &(buf[size]), burstcnt); if (rc == 0) size += burstcnt; @@ -462,7 +462,7 @@ static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len) burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN; #endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */ - rc = tpm_tis_i2c_write(dev, TPM_DATA_FIFO(chip->locality), + rc = tpm_tis_i2c_write(dev, TPM_INFINEON_DATA_FIFO(chip->locality), &(buf[count]), burstcnt); if (rc == 0) count += burstcnt; @@ -482,7 +482,7 @@ static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len) } /* Go and do it */ - rc = tpm_tis_i2c_write(dev, TPM_STS(chip->locality), &sts, 1); + rc = tpm_tis_i2c_write(dev, TPM_INFINEON_STS(chip->locality), &sts, 1); if (rc < 0) return rc; debug("%s: done, rc=%d\n", __func__, rc); @@ -525,7 +525,7 @@ static int tpm_tis_i2c_init(struct udevice *dev) return rc; /* Read four bytes from DID_VID register */ - if (tpm_tis_i2c_read(dev, TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) { + if (tpm_tis_i2c_read(dev, TPM_INFINEON_DID_VID(0), (uchar *)&vendor, 4) < 0) { tpm_tis_i2c_release_locality(dev, 0, 1); return -EIO; } @@ -583,7 +583,7 @@ static int tpm_tis_i2c_close(struct udevice *dev) return 0; } -static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) +static int tpm_tis_i2c_get_desc(struct udevice *dev, char *buf, int size) { struct tpm_chip *chip = dev_get_priv(dev); @@ -615,7 +615,7 @@ static int tpm_tis_i2c_probe(struct udevice *dev) static const struct tpm_ops tpm_tis_i2c_ops = { .open = tpm_tis_i2c_open, .close = tpm_tis_i2c_close, - .get_desc = tpm_tis_get_desc, + .get_desc = tpm_tis_i2c_get_desc, .send = tpm_tis_i2c_send, .recv = tpm_tis_i2c_recv, .cleanup = tpm_tis_i2c_cleanup, diff --git a/drivers/tpm/tpm_tis_lpc.c b/drivers/tpm/tpm_tis_lpc.c index 003c0d8816d..13a133d58eb 100644 --- a/drivers/tpm/tpm_tis_lpc.c +++ b/drivers/tpm/tpm_tis_lpc.c @@ -443,7 +443,7 @@ static int tpm_tis_lpc_open(struct udevice *dev) return 0; } -static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) +static int tpm_tis_lpc_get_desc(struct udevice *dev, char *buf, int size) { ulong chip_type = dev_get_driver_data(dev); @@ -458,7 +458,7 @@ static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) static const struct tpm_ops tpm_tis_lpc_ops = { .open = tpm_tis_lpc_open, .close = tpm_tis_lpc_close, - .get_desc = tpm_tis_get_desc, + .get_desc = tpm_tis_lpc_get_desc, .send = tis_senddata, .recv = tis_readresponse, }; diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 43564c9fbaf..ee0c064f1ff 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -29,7 +29,7 @@ enum usb_dr_mode usb_get_dr_mode(ofnode node) dr_mode = ofnode_read_string(node, "dr_mode"); if (!dr_mode) { - pr_err("usb dr_mode not found\n"); + pr_debug("usb dr_mode not found\n"); return USB_DR_MODE_UNKNOWN; } @@ -64,7 +64,7 @@ enum usb_device_speed usb_get_maximum_speed(ofnode node) max_speed = ofnode_read_string(node, "maximum-speed"); if (!max_speed) { - pr_err("usb maximum-speed not found\n"); + pr_debug("usb maximum-speed not found\n"); return USB_SPEED_UNKNOWN; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 93707e05fb1..62aa65bf0cd 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -53,6 +53,16 @@ config USB_DWC3_UNIPHIER Support of USB2/3 functionality in Socionext UniPhier platforms. Say 'Y' here if you have one such device. +config USB_DWC3_LAYERSCAPE + bool "Freescale Layerscape platform support" + depends on DM_USB && USB_DWC3 + depends on !USB_XHCI_FSL + help + Select this for Freescale Layerscape Platforms. + + Host and Peripheral operation modes are supported. OTG is not + supported. + menu "PHY Subsystem" config USB_DWC3_PHY_OMAP diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3e024e97e..0dd1ba87cd9 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o obj-$(CONFIG_USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o +obj-$(CONFIG_USB_DWC3_LAYERSCAPE) += dwc3-layerscape.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index dfd7cf683f7..ce1c0e88c2a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -93,6 +93,27 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) return 0; } +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + * @fladj: Value of GFLADJ_30MHZ to adjust frame length + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) +{ + u32 reg; + + if (dwc->revision < DWC3_REVISION_250A) + return; + + if (fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); +} + /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -441,6 +462,53 @@ static void dwc3_phy_setup(struct dwc3 *dwc) mdelay(100); } +/* set global incr burst type configuration registers */ +static void dwc3_set_incr_burst_type(struct dwc3 *dwc) +{ + struct udevice *dev = dwc->dev; + u32 cfg; + + if (!dwc->incrx_size) + return; + + cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + + /* Enable Undefined Length INCR Burst and Enable INCRx Burst */ + cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK; + if (dwc->incrx_mode) + cfg |= DWC3_GSBUSCFG0_INCRBRSTENA; + switch (dwc->incrx_size) { + case 256: + cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA; + break; + case 128: + cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA; + break; + case 64: + cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA; + break; + case 32: + cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA; + break; + case 16: + cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA; + break; + case 8: + cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA; + break; + case 4: + cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA; + break; + case 1: + break; + default: + dev_err(dev, "Invalid property\n"); + break; + } + + dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -569,6 +637,11 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err1; + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc, dwc->fladj); + + dwc3_set_incr_burst_type(dwc); + return 0; err1: @@ -892,6 +965,8 @@ void dwc3_of_parse(struct dwc3 *dwc) u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u32 val; + int i; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -958,6 +1033,26 @@ void dwc3_of_parse(struct dwc3 *dwc) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + + dev_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); + + /* + * Handle property "snps,incr-burst-type-adjustment". + * Get the number of value from this property: + * result <= 0, means this property is not supported. + * result = 1, means INCRx burst mode supported. + * result > 1, means undefined length burst mode supported. + */ + dwc->incrx_mode = INCRX_BURST_MODE; + dwc->incrx_size = 0; + for (i = 0; i < 8; i++) { + if (dev_read_u32_index(dev, "snps,incr-burst-type-adjustment", + i, &val)) + break; + + dwc->incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE; + dwc->incrx_size = max(dwc->incrx_size, val); + } } int dwc3_init(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 1502cb859a5..d7cce3a861a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -115,6 +115,7 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) #define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GFLADJ 0xc630 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -138,6 +139,17 @@ /* Bit fields */ +/* Global SoC Bus Configuration INCRx Register 0 */ +#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */ +#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */ +#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */ +#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */ +#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */ +#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */ +#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */ +#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ +#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff + /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) #define DWC3_GCTL_U2RSTECN (1 << 16) @@ -233,6 +245,10 @@ /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_EN_FPGA (1 << 7) +/* Global Frame Length Adjustment Register */ +#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) +#define DWC3_GFLADJ_30MHZ_MASK 0x3f + /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -812,6 +828,9 @@ struct dwc3 { u8 test_mode_nr; u8 lpm_nyet_threshold; u8 hird_threshold; + u32 fladj; + u8 incrx_mode; + u32 incrx_size; unsigned delayed_status:1; unsigned ep0_bounced:1; @@ -849,6 +868,9 @@ struct dwc3 { struct list_head list; }; +#define INCRX_BURST_MODE 0 +#define INCRX_UNDEF_LENGTH_BURST_MODE 1 + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/drivers/usb/dwc3/dwc3-layerscape.c b/drivers/usb/dwc3/dwc3-layerscape.c new file mode 100644 index 00000000000..79cf71f7a85 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-layerscape.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Layerscape DWC3 Glue layer + * + * Copyright (C) 2021 Michael Walle <michael@walle.cc> + * + * Based on dwc3-generic.c. + */ + +#include <common.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dwc3-uboot.h> +#include <linux/usb/gadget.h> +#include <usb.h> +#include "core.h" +#include "gadget.h" +#include <usb/xhci.h> + +struct dwc3_layerscape_plat { + fdt_addr_t base; + u32 maximum_speed; + enum usb_dr_mode dr_mode; +}; + +struct dwc3_layerscape_priv { + void *base; + struct dwc3 dwc3; + struct phy_bulk phys; +}; + +struct dwc3_layerscape_host_priv { + struct xhci_ctrl xhci_ctrl; + struct dwc3_layerscape_priv gen_priv; +}; + +static int dwc3_layerscape_probe(struct udevice *dev, + struct dwc3_layerscape_priv *priv) +{ + int rc; + struct dwc3_layerscape_plat *plat = dev_get_plat(dev); + struct dwc3 *dwc3 = &priv->dwc3; + + dwc3->dev = dev; + dwc3->maximum_speed = plat->maximum_speed; + dwc3->dr_mode = plat->dr_mode; + if (CONFIG_IS_ENABLED(OF_CONTROL)) + dwc3_of_parse(dwc3); + + rc = dwc3_setup_phy(dev, &priv->phys); + if (rc && rc != -ENOTSUPP) + return rc; + + priv->base = map_physmem(plat->base, DWC3_OTG_REGS_END, MAP_NOCACHE); + dwc3->regs = priv->base + DWC3_GLOBALS_REGS_START; + + rc = dwc3_init(dwc3); + if (rc) { + unmap_physmem(priv->base, MAP_NOCACHE); + return rc; + } + + return 0; +} + +static int dwc3_layerscape_remove(struct udevice *dev, + struct dwc3_layerscape_priv *priv) +{ + struct dwc3 *dwc3 = &priv->dwc3; + + dwc3_remove(dwc3); + dwc3_shutdown_phy(dev, &priv->phys); + unmap_physmem(dwc3->regs, MAP_NOCACHE); + + return 0; +} + +static int dwc3_layerscape_of_to_plat(struct udevice *dev) +{ + struct dwc3_layerscape_plat *plat = dev_get_plat(dev); + ofnode node = dev_ofnode(dev); + + plat->base = dev_read_addr(dev); + + plat->maximum_speed = usb_get_maximum_speed(node); + if (plat->maximum_speed == USB_SPEED_UNKNOWN) { + dev_dbg(dev, "No USB maximum speed specified. Using super speed\n"); + plat->maximum_speed = USB_SPEED_SUPER; + } + + plat->dr_mode = usb_get_dr_mode(node); + if (plat->dr_mode == USB_DR_MODE_UNKNOWN) { + dev_err(dev, "Invalid usb mode setup\n"); + return -ENODEV; + } + + return 0; +} + +#if CONFIG_IS_ENABLED(DM_USB_GADGET) +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + struct dwc3_layerscape_priv *priv = dev_get_priv(dev); + + dwc3_gadget_uboot_handle_interrupt(&priv->dwc3); + + return 0; +} + +static int dwc3_layerscape_peripheral_probe(struct udevice *dev) +{ + struct dwc3_layerscape_priv *priv = dev_get_priv(dev); + + return dwc3_layerscape_probe(dev, priv); +} + +static int dwc3_layerscape_peripheral_remove(struct udevice *dev) +{ + struct dwc3_layerscape_priv *priv = dev_get_priv(dev); + + return dwc3_layerscape_remove(dev, priv); +} + +U_BOOT_DRIVER(dwc3_layerscape_peripheral) = { + .name = "dwc3-layerscape-peripheral", + .id = UCLASS_USB_GADGET_GENERIC, + .of_to_plat = dwc3_layerscape_of_to_plat, + .probe = dwc3_layerscape_peripheral_probe, + .remove = dwc3_layerscape_peripheral_remove, + .priv_auto = sizeof(struct dwc3_layerscape_priv), + .plat_auto = sizeof(struct dwc3_layerscape_plat), +}; +#endif + +#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \ + !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST) +static int dwc3_layerscape_host_probe(struct udevice *dev) +{ + struct xhci_hcor *hcor; + struct xhci_hccr *hccr; + struct dwc3_layerscape_host_priv *priv = dev_get_priv(dev); + int rc; + + rc = dwc3_layerscape_probe(dev, &priv->gen_priv); + if (rc) + return rc; + + hccr = priv->gen_priv.base; + hcor = priv->gen_priv.base + HC_LENGTH(xhci_readl(&hccr->cr_capbase)); + + return xhci_register(dev, hccr, hcor); +} + +static int dwc3_layerscape_host_remove(struct udevice *dev) +{ + struct dwc3_layerscape_host_priv *priv = dev_get_priv(dev); + int rc; + + rc = xhci_deregister(dev); + if (rc) + return rc; + + return dwc3_layerscape_remove(dev, &priv->gen_priv); +} + +U_BOOT_DRIVER(dwc3_layerscape_host) = { + .name = "dwc3-layerscape-host", + .id = UCLASS_USB, + .of_to_plat = dwc3_layerscape_of_to_plat, + .probe = dwc3_layerscape_host_probe, + .remove = dwc3_layerscape_host_remove, + .priv_auto = sizeof(struct dwc3_layerscape_host_priv), + .plat_auto = sizeof(struct dwc3_layerscape_plat), + .ops = &xhci_usb_ops, + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif + +static int dwc3_layerscape_bind(struct udevice *dev) +{ + ofnode node = dev_ofnode(dev); + const char *name = ofnode_get_name(node); + enum usb_dr_mode dr_mode; + char *driver; + + dr_mode = usb_get_dr_mode(node); + + switch (dr_mode) { +#if CONFIG_IS_ENABLED(DM_USB_GADGET) + case USB_DR_MODE_PERIPHERAL: + dev_dbg(dev, "Using peripheral mode\n"); + driver = "dwc3-layerscape-peripheral"; + break; +#endif +#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD) + case USB_DR_MODE_HOST: + dev_dbg(dev, "Using host mode\n"); + driver = "dwc3-layerscape-host"; + break; +#endif + default: + dev_dbg(dev, "Unsupported dr_mode\n"); + return -ENODEV; + }; + + return device_bind_driver_to_node(dev, driver, name, node, NULL); +} + +static const struct udevice_id dwc3_layerscape_ids[] = { + { .compatible = "fsl,layerscape-dwc3" }, + { .compatible = "fsl,ls1028a-dwc3" }, + { } +}; + +U_BOOT_DRIVER(dwc3_layerscape_wrapper) = { + .name = "dwc3-layerscape-wrapper", + .id = UCLASS_NOP, + .of_match = dwc3_layerscape_ids, + .bind = dwc3_layerscape_bind, +}; diff --git a/drivers/usb/host/xhci-brcm.c b/drivers/usb/host/xhci-brcm.c index 27c4bbfcba7..fe17924028c 100644 --- a/drivers/usb/host/xhci-brcm.c +++ b/drivers/usb/host/xhci-brcm.c @@ -8,6 +8,7 @@ #include <fdtdec.h> #include <usb.h> #include <asm/io.h> +#include <dm/device_compat.h> #include <usb/xhci.h> #define DRD2U3H_XHC_REGS_AXIWRA 0xC08 diff --git a/drivers/usb/host/xhci-fsl.c b/drivers/usb/host/xhci-fsl.c index f062f12ade6..80871908dc1 100644 --- a/drivers/usb/host/xhci-fsl.c +++ b/drivers/usb/host/xhci-fsl.c @@ -159,6 +159,7 @@ static int xhci_fsl_remove(struct udevice *dev) static const struct udevice_id xhci_usb_ids[] = { { .compatible = "fsl,layerscape-dwc3", }, + { .compatible = "fsl,ls1028a-dwc3", }, { } }; diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 95eaf6d2365..e8dc0095ab5 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -112,6 +112,7 @@ int mtu3_gpd_ring_alloc(struct mtu3_ep *mep) memset(gpd, 0, QMU_GPD_RING_SIZE); ring->dma = (dma_addr_t)gpd; gpd_ring_init(ring, gpd); + mtu3_flush_cache((uintptr_t)gpd, sizeof(*gpd)); return 0; } diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index 6dd830cb73b..51f876cd711 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/drivers/usb/musb-new/Kconfig @@ -68,6 +68,7 @@ config USB_MUSB_PIC32 config USB_MUSB_SUNXI bool "Enable sunxi OTG / DRC USB controller" depends on ARCH_SUNXI + select USB_MUSB_PIO_ONLY default y ---help--- Say y here to enable support for the sunxi OTG / DRC USB controller diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2f4650f8309..a58f87f479b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -250,7 +250,7 @@ config VIDEO_COREBOOT config VIDEO_EFI bool "Enable EFI framebuffer driver support" - depends on EFI_STUB + depends on EFI_STUB || EFI_APP help Turn on this option to enable a framebuffeer driver when U-Boot is loaded as a payload (see README.u-boot_on_efi) by an EFI BIOS where diff --git a/drivers/video/dw_mipi_dsi.c b/drivers/video/dw_mipi_dsi.c index 9ae09eec12b..a5b38acabdb 100644 --- a/drivers/video/dw_mipi_dsi.c +++ b/drivers/video/dw_mipi_dsi.c @@ -17,7 +17,6 @@ #include <panel.h> #include <video.h> #include <asm/io.h> -#include <asm/arch/gpio.h> #include <dm/device-internal.h> #include <dm/device_compat.h> #include <linux/bitops.h> diff --git a/drivers/video/efi.c b/drivers/video/efi.c index c248bd352a7..5f9031f2ec5 100644 --- a/drivers/video/efi.c +++ b/drivers/video/efi.c @@ -50,6 +50,28 @@ static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size) *size = len; } +static int get_mode_info(struct vesa_mode_info *vesa) +{ + efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + struct efi_boot_services *boot = efi_get_boot(); + struct efi_gop_mode *mode; + struct efi_gop *gop; + int ret; + + if (!boot) + return log_msg_ret("sys", -ENOSYS); + ret = boot->locate_protocol(&efi_gop_guid, NULL, (void **)&gop); + if (ret) + return log_msg_ret("prot", -ENOTSUPP); + + mode = gop->mode; + vesa->phys_base_ptr = mode->fb_base; + vesa->x_resolution = mode->info->width; + vesa->y_resolution = mode->info->height; + + return 0; +} + static int save_vesa_mode(struct vesa_mode_info *vesa) { struct efi_entry_gopmode *mode; @@ -57,16 +79,23 @@ static int save_vesa_mode(struct vesa_mode_info *vesa) int size; int ret; - ret = efi_info_get(EFIET_GOP_MODE, (void **)&mode, &size); - if (ret == -ENOENT) { - debug("efi graphics output protocol mode not found\n"); - return -ENXIO; + if (IS_ENABLED(CONFIG_EFI_APP)) { + ret = get_mode_info(vesa); + if (ret) { + printf("EFI graphics output protocol not found\n"); + return -ENXIO; + } + } else { + ret = efi_info_get(EFIET_GOP_MODE, (void **)&mode, &size); + if (ret == -ENOENT) { + printf("EFI graphics output protocol mode not found\n"); + return -ENXIO; + } + vesa->phys_base_ptr = mode->fb_base; + vesa->x_resolution = mode->info->width; + vesa->y_resolution = mode->info->height; } - vesa->phys_base_ptr = mode->fb_base; - vesa->x_resolution = mode->info->width; - vesa->y_resolution = mode->info->height; - if (mode->info->pixel_format < EFI_GOT_BITMASK) { fbinfo = &efi_framebuffer_format_map[mode->info->pixel_format]; vesa->red_mask_size = fbinfo->red.size; diff --git a/drivers/video/stm32/stm32_dsi.c b/drivers/video/stm32/stm32_dsi.c index 4027e978c83..134abd93e19 100644 --- a/drivers/video/stm32/stm32_dsi.c +++ b/drivers/video/stm32/stm32_dsi.c @@ -21,7 +21,6 @@ #include <video.h> #include <video_bridge.h> #include <asm/io.h> -#include <asm/arch/gpio.h> #include <dm/device-internal.h> #include <dm/device_compat.h> #include <dm/lists.h> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c index f55a39498e0..65c882d9f12 100644 --- a/drivers/video/stm32/stm32_ltdc.c +++ b/drivers/video/stm32/stm32_ltdc.c @@ -17,7 +17,6 @@ #include <video.h> #include <video_bridge.h> #include <asm/io.h> -#include <asm/arch/gpio.h> #include <dm/device-internal.h> #include <dm/device_compat.h> #include <linux/bitops.h> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7d2d6ea5e90..1177f17fd8a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -12,6 +12,7 @@ config WATCHDOG config WATCHDOG_AUTOSTART bool "Automatically start watchdog timer" depends on WDT + default n if ARCH_SUNXI default y help Automatically start watchdog timer and start servicing it during @@ -27,6 +28,7 @@ config WATCHDOG_TIMEOUT_MSECS default 128000 if ARCH_MX31 || ARCH_MX5 || ARCH_MX6 default 128000 if ARCH_MX7 || ARCH_VF610 default 30000 if ARCH_SOCFPGA + default 16000 if ARCH_SUNXI default 60000 help Watchdog timeout in msec @@ -270,6 +272,13 @@ config WDT_STM32MP Enable the STM32 watchdog (IWDG) driver. Enable support to configure STM32's on-SoC watchdog. +config WDT_SUNXI + bool "Allwinner sunxi watchdog timer support" + depends on WDT && ARCH_SUNXI + default y + help + Enable support for the watchdog timer in Allwinner sunxi SoCs. + config XILINX_TB_WATCHDOG bool "Xilinx Axi watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f14415bb8e3..fa7ce583ce2 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -36,5 +36,6 @@ obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o +obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o diff --git a/drivers/watchdog/sandbox_wdt.c b/drivers/watchdog/sandbox_wdt.c index e05d82789f6..535614f04d6 100644 --- a/drivers/watchdog/sandbox_wdt.c +++ b/drivers/watchdog/sandbox_wdt.c @@ -39,6 +39,7 @@ static int sandbox_wdt_reset(struct udevice *dev) static int sandbox_wdt_expire_now(struct udevice *dev, ulong flags) { sandbox_wdt_start(dev, 1, flags); + sandbox_reset(); return 0; } diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index bec8827ceb1..0d6fb120654 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -134,7 +134,7 @@ static const struct wdt_ops sp805_wdt_ops = { }; static const struct udevice_id sp805_wdt_ids[] = { - { .compatible = "arm,sp805-wdt" }, + { .compatible = "arm,sp805" }, {} }; diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c new file mode 100644 index 00000000000..b40a1d29caa --- /dev/null +++ b/drivers/watchdog/sunxi_wdt.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Derived from linux/drivers/watchdog/sunxi_wdt.c: + * Copyright (C) 2013 Carlo Caione + * Copyright (C) 2012 Henrik Nordstrom + */ + +#include <dm.h> +#include <wdt.h> +#include <asm/io.h> +#include <linux/delay.h> + +#define MSEC_PER_SEC 1000 + +#define WDT_MAX_TIMEOUT 16 +#define WDT_TIMEOUT_MASK 0xf + +#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) + +#define WDT_MODE_EN BIT(0) + +struct sunxi_wdt_reg { + u8 wdt_ctrl; + u8 wdt_cfg; + u8 wdt_mode; + u8 wdt_timeout_shift; + u8 wdt_reset_mask; + u8 wdt_reset_val; + u32 wdt_key_val; +}; + +struct sunxi_wdt_priv { + void __iomem *base; + const struct sunxi_wdt_reg *regs; +}; + +/* Map of timeout in seconds to register value */ +static const u8 wdt_timeout_map[1 + WDT_MAX_TIMEOUT] = { + [0] = 0x0, + [1] = 0x1, + [2] = 0x2, + [3] = 0x3, + [4] = 0x4, + [5] = 0x5, + [6] = 0x6, + [7] = 0x7, + [8] = 0x7, + [9] = 0x8, + [10] = 0x8, + [11] = 0x9, + [12] = 0x9, + [13] = 0xa, + [14] = 0xa, + [15] = 0xb, + [16] = 0xb, +}; + +static int sunxi_wdt_reset(struct udevice *dev) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + const struct sunxi_wdt_reg *regs = priv->regs; + void __iomem *base = priv->base; + + writel(WDT_CTRL_RELOAD, base + regs->wdt_ctrl); + + return 0; +} + +static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + const struct sunxi_wdt_reg *regs = priv->regs; + void __iomem *base = priv->base; + u32 val; + + timeout /= MSEC_PER_SEC; + if (timeout > WDT_MAX_TIMEOUT) + timeout = WDT_MAX_TIMEOUT; + + /* Set system reset function */ + val = readl(base + regs->wdt_cfg); + val &= ~regs->wdt_reset_mask; + val |= regs->wdt_reset_val; + val |= regs->wdt_key_val; + writel(val, base + regs->wdt_cfg); + + /* Set timeout and enable watchdog */ + val = readl(base + regs->wdt_mode); + val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); + val |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; + val |= WDT_MODE_EN; + val |= regs->wdt_key_val; + writel(val, base + regs->wdt_mode); + + return sunxi_wdt_reset(dev); +} + +static int sunxi_wdt_stop(struct udevice *dev) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + const struct sunxi_wdt_reg *regs = priv->regs; + void __iomem *base = priv->base; + + writel(regs->wdt_key_val, base + regs->wdt_mode); + + return 0; +} + +static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags) +{ + int ret; + + ret = sunxi_wdt_start(dev, 0, flags); + if (ret) + return ret; + + mdelay(500); + + return 0; +} + +static const struct wdt_ops sunxi_wdt_ops = { + .reset = sunxi_wdt_reset, + .start = sunxi_wdt_start, + .stop = sunxi_wdt_stop, + .expire_now = sunxi_wdt_expire_now, +}; + +static const struct sunxi_wdt_reg sun4i_wdt_reg = { + .wdt_ctrl = 0x00, + .wdt_cfg = 0x04, + .wdt_mode = 0x04, + .wdt_timeout_shift = 3, + .wdt_reset_mask = 0x2, + .wdt_reset_val = 0x2, +}; + +static const struct sunxi_wdt_reg sun6i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x3, + .wdt_reset_val = 0x1, +}; + +static const struct sunxi_wdt_reg sun20i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + +static const struct udevice_id sunxi_wdt_ids[] = { + { .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg }, + { .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg }, + { .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg }, + { /* sentinel */ } +}; + +static int sunxi_wdt_probe(struct udevice *dev) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -EINVAL; + + priv->regs = (void *)dev_get_driver_data(dev); + if (!priv->regs) + return -EINVAL; + + sunxi_wdt_stop(dev); + + return 0; +} + +U_BOOT_DRIVER(sunxi_wdt) = { + .name = "sunxi_wdt", + .id = UCLASS_WDT, + .of_match = sunxi_wdt_ids, + .probe = sunxi_wdt_probe, + .priv_auto = sizeof(struct sunxi_wdt_priv), + .ops = &sunxi_wdt_ops, +}; diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index 7570710c4d9..6d0f4738676 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -10,6 +10,7 @@ #include <errno.h> #include <hang.h> #include <log.h> +#include <sysreset.h> #include <time.h> #include <wdt.h> #include <asm/global_data.h> @@ -44,6 +45,13 @@ static void init_watchdog_dev(struct udevice *dev) priv = dev_get_uclass_priv(dev); + if (IS_ENABLED(CONFIG_SYSRESET_WATCHDOG_AUTO)) { + ret = sysreset_register_wdt(dev); + if (ret) + printf("WDT: Failed to register %s for sysreset\n", + dev->name); + } + if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART)) { printf("WDT: Not starting %s\n", dev->name); return; |