aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/ata/sata_ceva.c2
-rw-r--r--drivers/cache/cache-sifive-ccache.c2
-rw-r--r--drivers/clk/clk_stm32mp1.c4
-rw-r--r--drivers/clk/sifive/sifive-prci.c6
-rw-r--r--drivers/clk/sunxi/Kconfig1
-rw-r--r--drivers/core/device.c8
-rw-r--r--drivers/core/fdtaddr.c11
-rw-r--r--drivers/crypto/fsl/fsl_hash.c14
-rw-r--r--drivers/ddr/fsl/fsl_ddr_gen4.c14
-rw-r--r--drivers/ddr/fsl/lc_common_dimm_params.c10
-rw-r--r--drivers/ddr/fsl/main.c11
-rw-r--r--drivers/ddr/fsl/util.c12
-rw-r--r--drivers/dfu/Kconfig14
-rw-r--r--drivers/dfu/dfu.c1
-rw-r--r--drivers/dfu/dfu_sf.c14
-rw-r--r--drivers/gpio/msm_gpio.c1
-rw-r--r--drivers/gpio/pm8916_gpio.c8
-rw-r--r--drivers/gpio/s5p_gpio.c1
-rw-r--r--drivers/gpio/sifive-gpio.c6
-rw-r--r--drivers/gpio/stm32_gpio.c3
-rw-r--r--drivers/gpio/stm32_gpio_priv.h86
-rw-r--r--drivers/i2c/Kconfig6
-rw-r--r--drivers/i2c/Makefile2
-rw-r--r--drivers/i2c/ocores_i2c.c2
-rw-r--r--drivers/iommu/Kconfig27
-rw-r--r--drivers/iommu/Makefile6
-rw-r--r--drivers/iommu/apple_dart.c59
-rw-r--r--drivers/iommu/iommu-uclass.c45
-rw-r--r--drivers/iommu/sandbox_iommu.c18
-rw-r--r--drivers/misc/Kconfig9
-rw-r--r--drivers/misc/Makefile2
-rw-r--r--drivers/mmc/arm_pl180_mmci.c14
-rw-r--r--drivers/mmc/arm_pl180_mmci.h1
-rw-r--r--drivers/mmc/fsl_esdhc.c16
-rw-r--r--drivers/mmc/fsl_esdhc_imx.c17
-rw-r--r--drivers/mmc/mmc.c4
-rw-r--r--drivers/mmc/sdhci.c20
-rw-r--r--drivers/mmc/sunxi_mmc.c2
-rw-r--r--drivers/mtd/altera_qspi.c3
-rw-r--r--drivers/mtd/cfi_mtd.c1
-rw-r--r--drivers/mtd/mtdconcat.c11
-rw-r--r--drivers/mtd/mtdcore.c8
-rw-r--r--drivers/mtd/mtdpart.c23
-rw-r--r--drivers/mtd/nand/raw/Kconfig8
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_spl.c6
-rw-r--r--drivers/mtd/nand/raw/nand_base.c4
-rw-r--r--drivers/mtd/nand/spi/macronix.c10
-rw-r--r--drivers/mtd/onenand/onenand_base.c3
-rw-r--r--drivers/mtd/spi/sf_mtd.c1
-rw-r--r--drivers/mtd/spi/spi-nor-core.c65
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c13
-rw-r--r--drivers/mtd/ubi/io.c13
-rw-r--r--drivers/net/Kconfig11
-rw-r--r--drivers/net/fsl-mc/mc.c89
-rw-r--r--drivers/net/macb.c11
-rw-r--r--drivers/pci/pci-aardvark.c9
-rw-r--r--drivers/pci/pci_mvebu.c286
-rw-r--r--drivers/pci/pcie_layerscape_fixup.c8
-rw-r--r--drivers/pci/pcie_layerscape_fixup_common.c6
-rw-r--r--drivers/pci/pcie_layerscape_gen4.c4
-rw-r--r--drivers/pci/pcie_layerscape_gen4_fixup.c5
-rw-r--r--drivers/pci/pcie_layerscape_rc.c64
-rw-r--r--drivers/phy/allwinner/Kconfig1
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c34
-rw-r--r--drivers/pinctrl/exynos/Kconfig8
-rw-r--r--drivers/pinctrl/exynos/Makefile1
-rw-r--r--drivers/pinctrl/exynos/pinctrl-exynos.c28
-rw-r--r--drivers/pinctrl/exynos/pinctrl-exynos78x0.c119
-rw-r--r--drivers/pinctrl/pinctrl_stm32.c3
-rw-r--r--drivers/power/axp152.c2
-rw-r--r--drivers/power/axp209.c2
-rw-r--r--drivers/power/axp221.c2
-rw-r--r--drivers/power/axp305.c2
-rw-r--r--drivers/power/axp809.c2
-rw-r--r--drivers/power/axp818.c2
-rw-r--r--drivers/power/pmic/Kconfig2
-rw-r--r--drivers/power/pmic/axp.c49
-rw-r--r--drivers/pwm/exynos_pwm.c4
-rw-r--r--drivers/qe/Kconfig4
-rw-r--r--drivers/ram/sifive/sifive_ddr.c8
-rw-r--r--drivers/ram/stm32mp1/stm32mp1_ram.c13
-rw-r--r--drivers/rtc/ds1337.c2
-rw-r--r--drivers/rtc/rv8803.c2
-rw-r--r--drivers/serial/Kconfig22
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_lpuart.c2
-rw-r--r--drivers/serial/serial_msm_geni.c613
-rw-r--r--drivers/serial/serial_s5p.c107
-rw-r--r--drivers/spi/fsl_dspi.c6
-rw-r--r--drivers/spi/nxp_fspi.c77
-rw-r--r--drivers/spmi/spmi-msm.c154
-rw-r--r--drivers/sysreset/Kconfig11
-rw-r--r--drivers/sysreset/sysreset_gpio.c2
-rw-r--r--drivers/sysreset/sysreset_resetctl.c2
-rw-r--r--drivers/sysreset/sysreset_syscon.c2
-rw-r--r--drivers/sysreset/sysreset_watchdog.c40
-rw-r--r--drivers/tpm/Kconfig9
-rw-r--r--drivers/tpm/Makefile3
-rw-r--r--drivers/tpm/tpm2_tis_core.c463
-rw-r--r--drivers/tpm/tpm2_tis_mmio.c157
-rw-r--r--drivers/tpm/tpm2_tis_spi.c447
-rw-r--r--drivers/tpm/tpm_tis.h136
-rw-r--r--drivers/tpm/tpm_tis_infineon.c34
-rw-r--r--drivers/tpm/tpm_tis_lpc.c4
-rw-r--r--drivers/usb/common/common.c4
-rw-r--r--drivers/usb/dwc3/Kconfig10
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/core.c95
-rw-r--r--drivers/usb/dwc3/core.h22
-rw-r--r--drivers/usb/dwc3/dwc3-layerscape.c222
-rw-r--r--drivers/usb/host/xhci-brcm.c1
-rw-r--r--drivers/usb/host/xhci-fsl.c1
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.c1
-rw-r--r--drivers/usb/musb-new/Kconfig1
-rw-r--r--drivers/video/Kconfig2
-rw-r--r--drivers/video/dw_mipi_dsi.c1
-rw-r--r--drivers/video/efi.c45
-rw-r--r--drivers/video/stm32/stm32_dsi.c1
-rw-r--r--drivers/video/stm32/stm32_ltdc.c1
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/sandbox_wdt.c1
-rw-r--r--drivers/watchdog/sp805_wdt.c2
-rw-r--r--drivers/watchdog/sunxi_wdt.c188
-rw-r--r--drivers/watchdog/wdt-uclass.c8
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, &regs->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(&regs->strobe_dllctrl, ESDHC_STROBE_DLL_CTRL_RESET);
+ /* clear the reset bit on strobe dll before any setting */
+ esdhc_write32(&regs->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(&regs->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(&regs->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(&regs->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, &regs->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, &regs->tcntb0 + offset);
writel(tcmp, &regs->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;