summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk_stm32mp1.c9
-rw-r--r--drivers/clk/imx/clk-imx8qm.c18
-rw-r--r--drivers/crypto/fsl/jr.c9
-rw-r--r--drivers/crypto/fsl/jr.h2
-rw-r--r--drivers/misc/imx8/fuse.c2
-rw-r--r--drivers/mtd/mtd_uboot.c5
-rw-r--r--drivers/net/dwc_eth_qos.c435
-rw-r--r--drivers/net/fec_mxc.c2
-rw-r--r--drivers/pci/pcie_imx.c220
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx6.c2
-rw-r--r--drivers/power/pmic/Kconfig7
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/bd71837.c89
-rw-r--r--drivers/power/regulator/pfuze100.c4
-rw-r--r--drivers/serial/serial_mxc.c1
-rw-r--r--drivers/spi/Kconfig8
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/mxc_spi.c77
-rw-r--r--drivers/spi/stm32_spi.c615
-rw-r--r--drivers/watchdog/Kconfig101
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/stm32mp_wdt.c135
22 files changed, 1550 insertions, 194 deletions
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c
index 6272b00b9e..f295e4864b 100644
--- a/drivers/clk/clk_stm32mp1.c
+++ b/drivers/clk/clk_stm32mp1.c
@@ -90,6 +90,7 @@
#define RCC_PLL4CSGR 0x8A4
#define RCC_I2C12CKSELR 0x8C0
#define RCC_I2C35CKSELR 0x8C4
+#define RCC_SPI2S1CKSELR 0x8D8
#define RCC_UART6CKSELR 0x8E4
#define RCC_UART24CKSELR 0x8E8
#define RCC_UART35CKSELR 0x8EC
@@ -298,6 +299,7 @@ enum stm32mp1_parent_sel {
_STGEN_SEL,
_DSI_SEL,
_ADC12_SEL,
+ _SPI1_SEL,
_PARENT_SEL_NB,
_UNKNOWN_SEL = 0xff,
};
@@ -519,6 +521,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 23, I2C3_K, _I2C35_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 24, I2C5_K, _I2C35_SEL),
+ STM32MP1_CLK_SET_CLR(RCC_MP_APB2ENSETR, 8, SPI1_K, _SPI1_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL),
STM32MP1_CLK_SET_CLR_F(RCC_MP_APB3ENSETR, 13, VREF, _PCLK3),
@@ -555,7 +558,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
STM32MP1_CLK_SET_CLR(RCC_MP_AHB5ENSETR, 0, GPIOZ, _UNKNOWN_SEL),
- STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 7, ETHCK, _ETH_SEL),
+ STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 7, ETHCK_K, _ETH_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 8, ETHTX, _UNKNOWN_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 9, ETHRX, _UNKNOWN_SEL),
STM32MP1_CLK_SET_CLR_F(RCC_MP_AHB6ENSETR, 10, ETHMAC, _ACLK),
@@ -589,6 +592,8 @@ static const u8 usbo_parents[] = {_PLL4_R, _USB_PHY_48};
static const u8 stgen_parents[] = {_HSI_KER, _HSE_KER};
static const u8 dsi_parents[] = {_DSI_PHY, _PLL4_P};
static const u8 adc_parents[] = {_PLL4_R, _CK_PER, _PLL3_Q};
+static const u8 spi_parents[] = {_PLL4_P, _PLL3_Q, _I2S_CKIN, _CK_PER,
+ _PLL3_R};
static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
STM32MP1_CLK_PARENT(_I2C12_SEL, RCC_I2C12CKSELR, 0, 0x7, i2c12_parents),
@@ -613,6 +618,7 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
STM32MP1_CLK_PARENT(_STGEN_SEL, RCC_STGENCKSELR, 0, 0x3, stgen_parents),
STM32MP1_CLK_PARENT(_DSI_SEL, RCC_DSICKSELR, 0, 0x1, dsi_parents),
STM32MP1_CLK_PARENT(_ADC12_SEL, RCC_ADCCKSELR, 0, 0x1, adc_parents),
+ STM32MP1_CLK_PARENT(_SPI1_SEL, RCC_SPI2S1CKSELR, 0, 0x7, spi_parents),
};
#ifdef STM32MP1_CLOCK_TREE_INIT
@@ -727,6 +733,7 @@ char * const stm32mp1_clk_parent_sel_name[_PARENT_SEL_NB] = {
[_STGEN_SEL] = "STGEN",
[_DSI_SEL] = "DSI",
[_ADC12_SEL] = "ADC12",
+ [_SPI1_SEL] = "SPI1",
};
static const struct stm32mp1_clk_data stm32mp1_data = {
diff --git a/drivers/clk/imx/clk-imx8qm.c b/drivers/clk/imx/clk-imx8qm.c
index 6b5561e178..a6b09d2109 100644
--- a/drivers/clk/imx/clk-imx8qm.c
+++ b/drivers/clk/imx/clk-imx8qm.c
@@ -80,6 +80,12 @@ ulong imx8_clk_get_rate(struct clk *clk)
resource = SC_R_SDHC_1;
pm_clk = SC_PM_CLK_PER;
break;
+ case IMX8QM_SDHC2_IPG_CLK:
+ case IMX8QM_SDHC2_CLK:
+ case IMX8QM_SDHC2_DIV:
+ resource = SC_R_SDHC_2;
+ pm_clk = SC_PM_CLK_PER;
+ break;
case IMX8QM_UART0_IPG_CLK:
case IMX8QM_UART0_CLK:
resource = SC_R_UART_0;
@@ -185,6 +191,12 @@ ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate)
resource = SC_R_SDHC_1;
pm_clk = SC_PM_CLK_PER;
break;
+ case IMX8QM_SDHC2_IPG_CLK:
+ case IMX8QM_SDHC2_CLK:
+ case IMX8QM_SDHC2_DIV:
+ resource = SC_R_SDHC_2;
+ pm_clk = SC_PM_CLK_PER;
+ break;
case IMX8QM_ENET0_IPG_CLK:
case IMX8QM_ENET0_AHB_CLK:
case IMX8QM_ENET0_REF_DIV:
@@ -273,6 +285,12 @@ int __imx8_clk_enable(struct clk *clk, bool enable)
resource = SC_R_SDHC_1;
pm_clk = SC_PM_CLK_PER;
break;
+ case IMX8QM_SDHC2_IPG_CLK:
+ case IMX8QM_SDHC2_CLK:
+ case IMX8QM_SDHC2_DIV:
+ resource = SC_R_SDHC_2;
+ pm_clk = SC_PM_CLK_PER;
+ break;
case IMX8QM_ENET0_IPG_CLK:
case IMX8QM_ENET0_AHB_CLK:
case IMX8QM_ENET0_REF_DIV:
diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c
index cc8d3b02a5..3121762364 100644
--- a/drivers/crypto/fsl/jr.c
+++ b/drivers/crypto/fsl/jr.c
@@ -578,8 +578,6 @@ int sec_init_idx(uint8_t sec_idx)
{
ccsr_sec_t *sec = (void *)SEC_ADDR(sec_idx);
uint32_t mcr = sec_in32(&sec->mcfgr);
- uint32_t jrown_ns;
- int i;
int ret = 0;
#ifdef CONFIG_FSL_CORENET
@@ -635,13 +633,6 @@ int sec_init_idx(uint8_t sec_idx)
#endif
#endif
- /* Set ownership of job rings to non-TrustZone mode by default */
- for (i = 0; i < ARRAY_SIZE(sec->jrliodnr); i++) {
- jrown_ns = sec_in32(&sec->jrliodnr[i].ms);
- jrown_ns |= JROWN_NS | JRMID_NS;
- sec_out32(&sec->jrliodnr[i].ms, jrown_ns);
- }
-
ret = jr_init(sec_idx);
if (ret < 0) {
printf("SEC initialization failed\n");
diff --git a/drivers/crypto/fsl/jr.h b/drivers/crypto/fsl/jr.h
index f6fbb44383..ffd3a19273 100644
--- a/drivers/crypto/fsl/jr.h
+++ b/drivers/crypto/fsl/jr.h
@@ -33,8 +33,6 @@
#define JRNSLIODN_MASK 0x0fff0000
#define JRSLIODN_SHIFT 0
#define JRSLIODN_MASK 0x00000fff
-#define JROWN_NS 0x00000008
-#define JRMID_NS 0x00000001
#define JQ_DEQ_ERR -1
#define JQ_DEQ_TO_ERR -2
diff --git a/drivers/misc/imx8/fuse.c b/drivers/misc/imx8/fuse.c
index 29d2256a22..2f2fad2c17 100644
--- a/drivers/misc/imx8/fuse.c
+++ b/drivers/misc/imx8/fuse.c
@@ -15,13 +15,11 @@ DECLARE_GLOBAL_DATA_PTR;
#define FSL_ECC_WORD_START_1 0x10
#define FSL_ECC_WORD_END_1 0x10F
-#ifdef CONFIG_IMX8QXP
#define FSL_ECC_WORD_START_2 0x220
#define FSL_ECC_WORD_END_2 0x31F
#define FSL_QXP_FUSE_GAP_START 0x110
#define FSL_QXP_FUSE_GAP_END 0x21F
-#endif
#define FSL_SIP_OTP_READ 0xc200000A
#define FSL_SIP_OTP_WRITE 0xc200000B
diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c
index d638f700d0..0a41ed477c 100644
--- a/drivers/mtd/mtd_uboot.c
+++ b/drivers/mtd/mtd_uboot.c
@@ -122,7 +122,6 @@ static const char *get_mtdparts(void)
{
__maybe_unused const char *mtdids = NULL;
static char tmp_parts[MTDPARTS_MAXLEN];
- static bool use_defaults = true;
const char *mtdparts = NULL;
if (gd->flags & GD_FLG_ENV_READY)
@@ -130,7 +129,7 @@ static const char *get_mtdparts(void)
else if (env_get_f("mtdparts", tmp_parts, sizeof(tmp_parts)) != -1)
mtdparts = tmp_parts;
- if (mtdparts || !use_defaults)
+ if (mtdparts)
return mtdparts;
#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
@@ -144,8 +143,6 @@ static const char *get_mtdparts(void)
if (mtdparts)
env_set("mtdparts", mtdparts);
- use_defaults = false;
-
return mtdparts;
}
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 590e756f5c..07b36675a7 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -26,7 +26,6 @@
* supports a single RGMII PHY. This configuration also has SW control over
* all clock and reset signals to the HW block.
*/
-
#include <common.h>
#include <clk.h>
#include <dm.h>
@@ -95,6 +94,7 @@ struct eqos_mac_regs {
#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK 3
#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED 0
#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB 2
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV 1
#define EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT 0
#define EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK 0xff
@@ -108,6 +108,7 @@ struct eqos_mac_regs {
#define EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT 16
#define EQOS_MAC_MDIO_ADDRESS_CR_SHIFT 8
#define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2
+#define EQOS_MAC_MDIO_ADDRESS_CR_250_300 5
#define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4)
#define EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT 2
#define EQOS_MAC_MDIO_ADDRESS_GOC_READ 3
@@ -260,6 +261,29 @@ struct eqos_desc {
struct eqos_config {
bool reg_access_always_ok;
+ int mdio_wait;
+ int swr_wait;
+ int config_mac;
+ int config_mac_mdio;
+ phy_interface_t (*interface)(struct udevice *dev);
+ struct eqos_ops *ops;
+};
+
+struct eqos_ops {
+ void (*eqos_inval_desc)(void *desc);
+ void (*eqos_flush_desc)(void *desc);
+ void (*eqos_inval_buffer)(void *buf, size_t size);
+ void (*eqos_flush_buffer)(void *buf, size_t size);
+ int (*eqos_probe_resources)(struct udevice *dev);
+ int (*eqos_remove_resources)(struct udevice *dev);
+ int (*eqos_stop_resets)(struct udevice *dev);
+ int (*eqos_start_resets)(struct udevice *dev);
+ void (*eqos_stop_clks)(struct udevice *dev);
+ int (*eqos_start_clks)(struct udevice *dev);
+ int (*eqos_calibrate_pads)(struct udevice *dev);
+ int (*eqos_disable_calibration)(struct udevice *dev);
+ int (*eqos_set_tx_clk_speed)(struct udevice *dev);
+ ulong (*eqos_get_tick_clk_rate)(struct udevice *dev);
};
struct eqos_priv {
@@ -276,6 +300,7 @@ struct eqos_priv {
struct clk clk_rx;
struct clk clk_ptp_ref;
struct clk clk_tx;
+ struct clk clk_ck;
struct clk clk_slave_bus;
struct mii_dev *mii;
struct phy_device *phy;
@@ -327,7 +352,7 @@ static void eqos_free_descs(void *descs)
#endif
}
-static void eqos_inval_desc(void *desc)
+static void eqos_inval_desc_tegra186(void *desc)
{
#ifndef CONFIG_SYS_NONCACHED_MEMORY
unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
@@ -338,14 +363,36 @@ static void eqos_inval_desc(void *desc)
#endif
}
-static void eqos_flush_desc(void *desc)
+static void eqos_inval_desc_stm32(void *desc)
+{
+#ifndef CONFIG_SYS_NONCACHED_MEMORY
+ unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)desc + EQOS_DESCRIPTOR_SIZE,
+ ARCH_DMA_MINALIGN);
+
+ invalidate_dcache_range(start, end);
+#endif
+}
+
+static void eqos_flush_desc_tegra186(void *desc)
{
#ifndef CONFIG_SYS_NONCACHED_MEMORY
flush_cache((unsigned long)desc, EQOS_DESCRIPTOR_SIZE);
#endif
}
-static void eqos_inval_buffer(void *buf, size_t size)
+static void eqos_flush_desc_stm32(void *desc)
+{
+#ifndef CONFIG_SYS_NONCACHED_MEMORY
+ unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)desc + EQOS_DESCRIPTOR_SIZE,
+ ARCH_DMA_MINALIGN);
+
+ flush_dcache_range(start, end);
+#endif
+}
+
+static void eqos_inval_buffer_tegra186(void *buf, size_t size)
{
unsigned long start = (unsigned long)buf & ~(ARCH_DMA_MINALIGN - 1);
unsigned long end = ALIGN(start + size, ARCH_DMA_MINALIGN);
@@ -353,11 +400,29 @@ static void eqos_inval_buffer(void *buf, size_t size)
invalidate_dcache_range(start, end);
}
-static void eqos_flush_buffer(void *buf, size_t size)
+static void eqos_inval_buffer_stm32(void *buf, size_t size)
+{
+ unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)buf + size,
+ ARCH_DMA_MINALIGN);
+
+ invalidate_dcache_range(start, end);
+}
+
+static void eqos_flush_buffer_tegra186(void *buf, size_t size)
{
flush_cache((unsigned long)buf, size);
}
+static void eqos_flush_buffer_stm32(void *buf, size_t size)
+{
+ unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)buf + size,
+ ARCH_DMA_MINALIGN);
+
+ flush_dcache_range(start, end);
+}
+
static int eqos_mdio_wait_idle(struct eqos_priv *eqos)
{
return wait_for_bit_le32(&eqos->mac_regs->mdio_address,
@@ -386,14 +451,14 @@ static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad,
EQOS_MAC_MDIO_ADDRESS_C45E;
val |= (mdio_addr << EQOS_MAC_MDIO_ADDRESS_PA_SHIFT) |
(mdio_reg << EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT) |
- (EQOS_MAC_MDIO_ADDRESS_CR_20_35 <<
+ (eqos->config->config_mac_mdio <<
EQOS_MAC_MDIO_ADDRESS_CR_SHIFT) |
(EQOS_MAC_MDIO_ADDRESS_GOC_READ <<
EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT) |
EQOS_MAC_MDIO_ADDRESS_GB;
writel(val, &eqos->mac_regs->mdio_address);
- udelay(10);
+ udelay(eqos->config->mdio_wait);
ret = eqos_mdio_wait_idle(eqos);
if (ret) {
@@ -432,14 +497,14 @@ static int eqos_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad,
EQOS_MAC_MDIO_ADDRESS_C45E;
val |= (mdio_addr << EQOS_MAC_MDIO_ADDRESS_PA_SHIFT) |
(mdio_reg << EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT) |
- (EQOS_MAC_MDIO_ADDRESS_CR_20_35 <<
+ (eqos->config->config_mac_mdio <<
EQOS_MAC_MDIO_ADDRESS_CR_SHIFT) |
(EQOS_MAC_MDIO_ADDRESS_GOC_WRITE <<
EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT) |
EQOS_MAC_MDIO_ADDRESS_GB;
writel(val, &eqos->mac_regs->mdio_address);
- udelay(10);
+ udelay(eqos->config->mdio_wait);
ret = eqos_mdio_wait_idle(eqos);
if (ret) {
@@ -509,6 +574,53 @@ err:
return ret;
}
+static int eqos_start_clks_stm32(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ int ret;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ ret = clk_enable(&eqos->clk_master_bus);
+ if (ret < 0) {
+ pr_err("clk_enable(clk_master_bus) failed: %d", ret);
+ goto err;
+ }
+
+ ret = clk_enable(&eqos->clk_rx);
+ if (ret < 0) {
+ pr_err("clk_enable(clk_rx) failed: %d", ret);
+ goto err_disable_clk_master_bus;
+ }
+
+ ret = clk_enable(&eqos->clk_tx);
+ if (ret < 0) {
+ pr_err("clk_enable(clk_tx) failed: %d", ret);
+ goto err_disable_clk_rx;
+ }
+
+ if (clk_valid(&eqos->clk_ck)) {
+ ret = clk_enable(&eqos->clk_ck);
+ if (ret < 0) {
+ pr_err("clk_enable(clk_ck) failed: %d", ret);
+ goto err_disable_clk_tx;
+ }
+ }
+
+ debug("%s: OK\n", __func__);
+ return 0;
+
+err_disable_clk_tx:
+ clk_disable(&eqos->clk_tx);
+err_disable_clk_rx:
+ clk_disable(&eqos->clk_rx);
+err_disable_clk_master_bus:
+ clk_disable(&eqos->clk_master_bus);
+err:
+ debug("%s: FAILED: %d\n", __func__, ret);
+ return ret;
+}
+
void eqos_stop_clks_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -524,6 +636,21 @@ void eqos_stop_clks_tegra186(struct udevice *dev)
debug("%s: OK\n", __func__);
}
+void eqos_stop_clks_stm32(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ clk_disable(&eqos->clk_tx);
+ clk_disable(&eqos->clk_rx);
+ clk_disable(&eqos->clk_master_bus);
+ if (clk_valid(&eqos->clk_ck))
+ clk_disable(&eqos->clk_ck);
+
+ debug("%s: OK\n", __func__);
+}
+
static int eqos_start_resets_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -563,6 +690,11 @@ static int eqos_start_resets_tegra186(struct udevice *dev)
return 0;
}
+static int eqos_start_resets_stm32(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_stop_resets_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -573,6 +705,11 @@ static int eqos_stop_resets_tegra186(struct udevice *dev)
return 0;
}
+static int eqos_stop_resets_stm32(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_calibrate_pads_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -632,6 +769,23 @@ static ulong eqos_get_tick_clk_rate_tegra186(struct udevice *dev)
return clk_get_rate(&eqos->clk_slave_bus);
}
+static ulong eqos_get_tick_clk_rate_stm32(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+
+ return clk_get_rate(&eqos->clk_master_bus);
+}
+
+static int eqos_calibrate_pads_stm32(struct udevice *dev)
+{
+ return 0;
+}
+
+static int eqos_disable_calibration_stm32(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_set_full_duplex(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -726,6 +880,11 @@ static int eqos_set_tx_clk_speed_tegra186(struct udevice *dev)
return 0;
}
+static int eqos_set_tx_clk_speed_stm32(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_adjust_link(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -766,23 +925,23 @@ static int eqos_adjust_link(struct udevice *dev)
}
if (en_calibration) {
- ret = eqos_calibrate_pads_tegra186(dev);
+ ret = eqos->config->ops->eqos_calibrate_pads(dev);
if (ret < 0) {
- pr_err("eqos_calibrate_pads_tegra186() failed: %d", ret);
+ pr_err("eqos_calibrate_pads() failed: %d",
+ ret);
return ret;
}
} else {
- ret = eqos_disable_calibration_tegra186(dev);
+ ret = eqos->config->ops->eqos_disable_calibration(dev);
if (ret < 0) {
- pr_err("eqos_disable_calibration_tegra186() failed: %d",
- ret);
+ pr_err("eqos_disable_calibration() failed: %d",
+ ret);
return ret;
}
}
-
- ret = eqos_set_tx_clk_speed_tegra186(dev);
+ ret = eqos->config->ops->eqos_set_tx_clk_speed(dev);
if (ret < 0) {
- pr_err("eqos_set_tx_clk_speed_tegra186() failed: %d", ret);
+ pr_err("eqos_set_tx_clk_speed() failed: %d", ret);
return ret;
}
@@ -846,15 +1005,15 @@ static int eqos_start(struct udevice *dev)
eqos->tx_desc_idx = 0;
eqos->rx_desc_idx = 0;
- ret = eqos_start_clks_tegra186(dev);
+ ret = eqos->config->ops->eqos_start_clks(dev);
if (ret < 0) {
- pr_err("eqos_start_clks_tegra186() failed: %d", ret);
+ pr_err("eqos_start_clks() failed: %d", ret);
goto err;
}
- ret = eqos_start_resets_tegra186(dev);
+ ret = eqos->config->ops->eqos_start_resets(dev);
if (ret < 0) {
- pr_err("eqos_start_resets_tegra186() failed: %d", ret);
+ pr_err("eqos_start_resets() failed: %d", ret);
goto err_stop_clks;
}
@@ -863,32 +1022,41 @@ static int eqos_start(struct udevice *dev)
eqos->reg_access_ok = true;
ret = wait_for_bit_le32(&eqos->dma_regs->mode,
- EQOS_DMA_MODE_SWR, false, 10, false);
+ EQOS_DMA_MODE_SWR, false,
+ eqos->config->swr_wait, false);
if (ret) {
pr_err("EQOS_DMA_MODE_SWR stuck");
goto err_stop_resets;
}
- ret = eqos_calibrate_pads_tegra186(dev);
+ ret = eqos->config->ops->eqos_calibrate_pads(dev);
if (ret < 0) {
- pr_err("eqos_calibrate_pads_tegra186() failed: %d", ret);
+ pr_err("eqos_calibrate_pads() failed: %d", ret);
goto err_stop_resets;
}
+ rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
- rate = eqos_get_tick_clk_rate_tegra186(dev);
val = (rate / 1000000) - 1;
writel(val, &eqos->mac_regs->us_tic_counter);
- eqos->phy = phy_connect(eqos->mii, 0, dev, 0);
+ /*
+ * if PHY was already connected and configured,
+ * don't need to reconnect/reconfigure again
+ */
if (!eqos->phy) {
- pr_err("phy_connect() failed");
- goto err_stop_resets;
- }
- ret = phy_config(eqos->phy);
- if (ret < 0) {
- pr_err("phy_config() failed: %d", ret);
- goto err_shutdown_phy;
+ eqos->phy = phy_connect(eqos->mii, 0, dev,
+ eqos->config->interface(dev));
+ if (!eqos->phy) {
+ pr_err("phy_connect() failed");
+ goto err_stop_resets;
+ }
+ ret = phy_config(eqos->phy);
+ if (ret < 0) {
+ pr_err("phy_config() failed: %d", ret);
+ goto err_shutdown_phy;
+ }
}
+
ret = phy_startup(eqos->phy);
if (ret < 0) {
pr_err("phy_startup() failed: %d", ret);
@@ -993,7 +1161,7 @@ static int eqos_start(struct udevice *dev)
clrsetbits_le32(&eqos->mac_regs->rxq_ctrl0,
EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK <<
EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT,
- EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB <<
+ eqos->config->config_mac <<
EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT);
/* Set TX flow control parameters */
@@ -1074,7 +1242,7 @@ static int eqos_start(struct udevice *dev)
(i * EQOS_MAX_PACKET_SIZE));
rx_desc->des3 |= EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
}
- flush_cache((unsigned long)eqos->descs, EQOS_DESCRIPTORS_SIZE);
+ eqos->config->ops->eqos_flush_desc(eqos->descs);
writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress);
writel((ulong)eqos->tx_descs, &eqos->dma_regs->ch0_txdesc_list_address);
@@ -1113,11 +1281,10 @@ static int eqos_start(struct udevice *dev)
err_shutdown_phy:
phy_shutdown(eqos->phy);
- eqos->phy = NULL;
err_stop_resets:
- eqos_stop_resets_tegra186(dev);
+ eqos->config->ops->eqos_stop_resets(dev);
err_stop_clks:
- eqos_stop_clks_tegra186(dev);
+ eqos->config->ops->eqos_stop_clks(dev);
err:
pr_err("FAILED: %d", ret);
return ret;
@@ -1170,10 +1337,9 @@ void eqos_stop(struct udevice *dev)
if (eqos->phy) {
phy_shutdown(eqos->phy);
- eqos->phy = NULL;
}
- eqos_stop_resets_tegra186(dev);
- eqos_stop_clks_tegra186(dev);
+ eqos->config->ops->eqos_stop_resets(dev);
+ eqos->config->ops->eqos_stop_clks(dev);
debug("%s: OK\n", __func__);
}
@@ -1188,7 +1354,7 @@ int eqos_send(struct udevice *dev, void *packet, int length)
length);
memcpy(eqos->tx_dma_buf, packet, length);
- eqos_flush_buffer(eqos->tx_dma_buf, length);
+ eqos->config->ops->eqos_flush_buffer(eqos->tx_dma_buf, length);
tx_desc = &(eqos->tx_descs[eqos->tx_desc_idx]);
eqos->tx_desc_idx++;
@@ -1203,12 +1369,12 @@ int eqos_send(struct udevice *dev, void *packet, int length)
*/
mb();
tx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | length;
- eqos_flush_desc(tx_desc);
+ eqos->config->ops->eqos_flush_desc(tx_desc);
writel((ulong)(tx_desc + 1), &eqos->dma_regs->ch0_txdesc_tail_pointer);
for (i = 0; i < 1000000; i++) {
- eqos_inval_desc(tx_desc);
+ eqos->config->ops->eqos_inval_desc(tx_desc);
if (!(readl(&tx_desc->des3) & EQOS_DESC3_OWN))
return 0;
udelay(1);
@@ -1238,7 +1404,7 @@ int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
length = rx_desc->des3 & 0x7fff;
debug("%s: *packetp=%p, length=%d\n", __func__, *packetp, length);
- eqos_inval_buffer(*packetp, length);
+ eqos->config->ops->eqos_inval_buffer(*packetp, length);
return length;
}
@@ -1269,7 +1435,7 @@ int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
*/
mb();
rx_desc->des3 |= EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
- eqos_flush_desc(rx_desc);
+ eqos->config->ops->eqos_flush_desc(rx_desc);
writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
@@ -1304,7 +1470,7 @@ static int eqos_probe_resources_core(struct udevice *dev)
ret = -ENOMEM;
goto err_free_descs;
}
- debug("%s: rx_dma_buf=%p\n", __func__, eqos->rx_dma_buf);
+ debug("%s: tx_dma_buf=%p\n", __func__, eqos->tx_dma_buf);
eqos->rx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_RX_BUFFER_SIZE);
if (!eqos->rx_dma_buf) {
@@ -1312,7 +1478,7 @@ static int eqos_probe_resources_core(struct udevice *dev)
ret = -ENOMEM;
goto err_free_tx_dma_buf;
}
- debug("%s: tx_dma_buf=%p\n", __func__, eqos->tx_dma_buf);
+ debug("%s: rx_dma_buf=%p\n", __func__, eqos->rx_dma_buf);
eqos->rx_pkt = malloc(EQOS_MAX_PACKET_SIZE);
if (!eqos->rx_pkt) {
@@ -1424,6 +1590,98 @@ err_free_reset_eqos:
return ret;
}
+/* board-specific Ethernet Interface initializations. */
+__weak int board_interface_eth_init(int interface_type, bool eth_clk_sel_reg,
+ bool eth_ref_clk_sel_reg)
+{
+ return 0;
+}
+
+static int eqos_probe_resources_stm32(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ int ret;
+ phy_interface_t interface;
+ bool eth_clk_sel_reg = false;
+ bool eth_ref_clk_sel_reg = false;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ interface = eqos->config->interface(dev);
+
+ if (interface == PHY_INTERFACE_MODE_NONE) {
+ pr_err("Invalid PHY interface\n");
+ return -EINVAL;
+ }
+
+ /* Gigabit Ethernet 125MHz clock selection. */
+ eth_clk_sel_reg = dev_read_bool(dev, "st,eth_clk_sel");
+
+ /* Ethernet 50Mhz RMII clock selection */
+ eth_ref_clk_sel_reg =
+ dev_read_bool(dev, "st,eth_ref_clk_sel");
+
+ ret = board_interface_eth_init(interface, eth_clk_sel_reg,
+ eth_ref_clk_sel_reg);
+ if (ret)
+ return -EINVAL;
+
+ ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus);
+ if (ret) {
+ pr_err("clk_get_by_name(master_bus) failed: %d", ret);
+ goto err_probe;
+ }
+
+ ret = clk_get_by_name(dev, "mac-clk-rx", &eqos->clk_rx);
+ if (ret) {
+ pr_err("clk_get_by_name(rx) failed: %d", ret);
+ goto err_free_clk_master_bus;
+ }
+
+ ret = clk_get_by_name(dev, "mac-clk-tx", &eqos->clk_tx);
+ if (ret) {
+ pr_err("clk_get_by_name(tx) failed: %d", ret);
+ goto err_free_clk_rx;
+ }
+
+ /* Get ETH_CLK clocks (optional) */
+ ret = clk_get_by_name(dev, "eth-ck", &eqos->clk_ck);
+ if (ret)
+ pr_warn("No phy clock provided %d", ret);
+
+ debug("%s: OK\n", __func__);
+ return 0;
+
+err_free_clk_rx:
+ clk_free(&eqos->clk_rx);
+err_free_clk_master_bus:
+ clk_free(&eqos->clk_master_bus);
+err_probe:
+
+ debug("%s: returns %d\n", __func__, ret);
+ return ret;
+}
+
+static phy_interface_t eqos_get_interface_stm32(struct udevice *dev)
+{
+ const char *phy_mode;
+ phy_interface_t interface = PHY_INTERFACE_MODE_NONE;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode",
+ NULL);
+ if (phy_mode)
+ interface = phy_get_interface_by_name(phy_mode);
+
+ return interface;
+}
+
+static phy_interface_t eqos_get_interface_tegra186(struct udevice *dev)
+{
+ return PHY_INTERFACE_MODE_MII;
+}
+
static int eqos_remove_resources_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1442,6 +1700,22 @@ static int eqos_remove_resources_tegra186(struct udevice *dev)
return 0;
}
+static int eqos_remove_resources_stm32(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ clk_free(&eqos->clk_tx);
+ clk_free(&eqos->clk_rx);
+ clk_free(&eqos->clk_master_bus);
+ if (clk_valid(&eqos->clk_ck))
+ clk_free(&eqos->clk_ck);
+
+ debug("%s: OK\n", __func__);
+ return 0;
+}
+
static int eqos_probe(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1468,15 +1742,16 @@ static int eqos_probe(struct udevice *dev)
return ret;
}
- ret = eqos_probe_resources_tegra186(dev);
+ ret = eqos->config->ops->eqos_probe_resources(dev);
if (ret < 0) {
- pr_err("eqos_probe_resources_tegra186() failed: %d", ret);
+ pr_err("eqos_probe_resources() failed: %d", ret);
goto err_remove_resources_core;
}
eqos->mii = mdio_alloc();
if (!eqos->mii) {
pr_err("mdio_alloc() failed");
+ ret = -ENOMEM;
goto err_remove_resources_tegra;
}
eqos->mii->read = eqos_mdio_read;
@@ -1496,7 +1771,7 @@ static int eqos_probe(struct udevice *dev)
err_free_mdio:
mdio_free(eqos->mii);
err_remove_resources_tegra:
- eqos_remove_resources_tegra186(dev);
+ eqos->config->ops->eqos_remove_resources(dev);
err_remove_resources_core:
eqos_remove_resources_core(dev);
@@ -1512,7 +1787,8 @@ static int eqos_remove(struct udevice *dev)
mdio_unregister(eqos->mii);
mdio_free(eqos->mii);
- eqos_remove_resources_tegra186(dev);
+ eqos->config->ops->eqos_remove_resources(dev);
+
eqos_probe_resources_core(dev);
debug("%s: OK\n", __func__);
@@ -1528,8 +1804,58 @@ static const struct eth_ops eqos_ops = {
.write_hwaddr = eqos_write_hwaddr,
};
+static struct eqos_ops eqos_tegra186_ops = {
+ .eqos_inval_desc = eqos_inval_desc_tegra186,
+ .eqos_flush_desc = eqos_flush_desc_tegra186,
+ .eqos_inval_buffer = eqos_inval_buffer_tegra186,
+ .eqos_flush_buffer = eqos_flush_buffer_tegra186,
+ .eqos_probe_resources = eqos_probe_resources_tegra186,
+ .eqos_remove_resources = eqos_remove_resources_tegra186,
+ .eqos_stop_resets = eqos_stop_resets_tegra186,
+ .eqos_start_resets = eqos_start_resets_tegra186,
+ .eqos_stop_clks = eqos_stop_clks_tegra186,
+ .eqos_start_clks = eqos_start_clks_tegra186,
+ .eqos_calibrate_pads = eqos_calibrate_pads_tegra186,
+ .eqos_disable_calibration = eqos_disable_calibration_tegra186,
+ .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_tegra186,
+ .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_tegra186
+};
+
static const struct eqos_config eqos_tegra186_config = {
.reg_access_always_ok = false,
+ .mdio_wait = 10,
+ .swr_wait = 10,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_20_35,
+ .interface = eqos_get_interface_tegra186,
+ .ops = &eqos_tegra186_ops
+};
+
+static struct eqos_ops eqos_stm32_ops = {
+ .eqos_inval_desc = eqos_inval_desc_stm32,
+ .eqos_flush_desc = eqos_flush_desc_stm32,
+ .eqos_inval_buffer = eqos_inval_buffer_stm32,
+ .eqos_flush_buffer = eqos_flush_buffer_stm32,
+ .eqos_probe_resources = eqos_probe_resources_stm32,
+ .eqos_remove_resources = eqos_remove_resources_stm32,
+ .eqos_stop_resets = eqos_stop_resets_stm32,
+ .eqos_start_resets = eqos_start_resets_stm32,
+ .eqos_stop_clks = eqos_stop_clks_stm32,
+ .eqos_start_clks = eqos_start_clks_stm32,
+ .eqos_calibrate_pads = eqos_calibrate_pads_stm32,
+ .eqos_disable_calibration = eqos_disable_calibration_stm32,
+ .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_stm32,
+ .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_stm32
+};
+
+static const struct eqos_config eqos_stm32_config = {
+ .reg_access_always_ok = false,
+ .mdio_wait = 10000,
+ .swr_wait = 50,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+ .interface = eqos_get_interface_stm32,
+ .ops = &eqos_stm32_ops
};
static const struct udevice_id eqos_ids[] = {
@@ -1537,6 +1863,11 @@ static const struct udevice_id eqos_ids[] = {
.compatible = "nvidia,tegra186-eqos",
.data = (ulong)&eqos_tegra186_config
},
+ {
+ .compatible = "snps,dwmac-4.20a",
+ .data = (ulong)&eqos_stm32_config
+ },
+
{ }
};
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
index a672250e16..d7c080943a 100644
--- a/drivers/net/fec_mxc.c
+++ b/drivers/net/fec_mxc.c
@@ -604,7 +604,7 @@ static int fec_init(struct eth_device *dev, bd_t *bd)
writel(0x00000000, &fec->eth->gaddr2);
/* Do not access reserved register */
- if (!is_mx6ul() && !is_mx6ull() && !is_imx8m()) {
+ if (!is_mx6ul() && !is_mx6ull() && !is_imx8() && !is_imx8m()) {
/* clear MIB RAM */
for (i = mib_ptr; i <= mib_ptr + 0xfc; i += 4)
writel(0, i);
diff --git a/drivers/pci/pcie_imx.c b/drivers/pci/pcie_imx.c
index fcc4ab7139..10b8fb4c88 100644
--- a/drivers/pci/pcie_imx.c
+++ b/drivers/pci/pcie_imx.c
@@ -16,6 +16,7 @@
#include <asm/arch/crm_regs.h>
#include <asm/gpio.h>
#include <asm/io.h>
+#include <dm.h>
#include <linux/sizes.h>
#include <errno.h>
#include <asm/arch/sys_proto.h>
@@ -92,6 +93,11 @@
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
#define PCIE_ATU_UPPER_TARGET 0x91C
+struct imx_pcie_priv {
+ void __iomem *dbi_base;
+ void __iomem *cfg_base;
+};
+
/*
* PHY access functions
*/
@@ -225,13 +231,13 @@ static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
return 0;
}
-static int imx6_pcie_link_up(void)
+static int imx6_pcie_link_up(struct imx_pcie_priv *priv)
{
u32 rc, ltssm;
int rx_valid, temp;
/* link is debug bit 36, debug register 1 starts at bit 32 */
- rc = readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R1);
+ rc = readl(priv->dbi_base + PCIE_PHY_DEBUG_R1);
if ((rc & PCIE_PHY_DEBUG_R1_LINK_UP) &&
!(rc & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING))
return -EAGAIN;
@@ -243,8 +249,8 @@ static int imx6_pcie_link_up(void)
* && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
* to gen2 is stuck
*/
- pcie_phy_read((void *)MX6_DBI_ADDR, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
- ltssm = readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R0) & 0x3F;
+ pcie_phy_read(priv->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
+ ltssm = readl(priv->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F;
if (rx_valid & 0x01)
return 0;
@@ -254,15 +260,15 @@ static int imx6_pcie_link_up(void)
printf("transition to gen2 is stuck, reset PHY!\n");
- pcie_phy_read((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, &temp);
+ pcie_phy_read(priv->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
- pcie_phy_write((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, temp);
+ pcie_phy_write(priv->dbi_base, PHY_RX_OVRD_IN_LO, temp);
udelay(3000);
- pcie_phy_read((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, &temp);
+ pcie_phy_read(priv->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
- pcie_phy_write((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, temp);
+ pcie_phy_write(priv->dbi_base, PHY_RX_OVRD_IN_LO, temp);
return 0;
}
@@ -270,7 +276,7 @@ static int imx6_pcie_link_up(void)
/*
* iATU region setup
*/
-static int imx_pcie_regions_setup(void)
+static int imx_pcie_regions_setup(struct imx_pcie_priv *priv)
{
/*
* i.MX6 defines 16MB in the AXI address map for PCIe.
@@ -285,24 +291,27 @@ static int imx_pcie_regions_setup(void)
*/
/* CMD reg:I/O space, MEM space, and Bus Master Enable */
- setbits_le32(MX6_DBI_ADDR | PCI_COMMAND,
+ setbits_le32(priv->dbi_base + PCI_COMMAND,
PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
/* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
- setbits_le32(MX6_DBI_ADDR + PCI_CLASS_REVISION,
+ setbits_le32(priv->dbi_base + PCI_CLASS_REVISION,
PCI_CLASS_BRIDGE_PCI << 16);
/* Region #0 is used for Outbound CFG space access. */
- writel(0, MX6_DBI_ADDR + PCIE_ATU_VIEWPORT);
+ writel(0, priv->dbi_base + PCIE_ATU_VIEWPORT);
- writel(MX6_ROOT_ADDR, MX6_DBI_ADDR + PCIE_ATU_LOWER_BASE);
- writel(0, MX6_DBI_ADDR + PCIE_ATU_UPPER_BASE);
- writel(MX6_ROOT_ADDR + MX6_ROOT_SIZE, MX6_DBI_ADDR + PCIE_ATU_LIMIT);
+ writel(lower_32_bits((uintptr_t)priv->cfg_base),
+ priv->dbi_base + PCIE_ATU_LOWER_BASE);
+ writel(upper_32_bits((uintptr_t)priv->cfg_base),
+ priv->dbi_base + PCIE_ATU_UPPER_BASE);
+ writel(lower_32_bits((uintptr_t)priv->cfg_base + MX6_ROOT_SIZE),
+ priv->dbi_base + PCIE_ATU_LIMIT);
- writel(0, MX6_DBI_ADDR + PCIE_ATU_LOWER_TARGET);
- writel(0, MX6_DBI_ADDR + PCIE_ATU_UPPER_TARGET);
- writel(PCIE_ATU_TYPE_CFG0, MX6_DBI_ADDR + PCIE_ATU_CR1);
- writel(PCIE_ATU_ENABLE, MX6_DBI_ADDR + PCIE_ATU_CR2);
+ writel(0, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
+ writel(0, priv->dbi_base + PCIE_ATU_UPPER_TARGET);
+ writel(PCIE_ATU_TYPE_CFG0, priv->dbi_base + PCIE_ATU_CR1);
+ writel(PCIE_ATU_ENABLE, priv->dbi_base + PCIE_ATU_CR2);
return 0;
}
@@ -310,23 +319,24 @@ static int imx_pcie_regions_setup(void)
/*
* PCI Express accessors
*/
-static uint32_t get_bus_address(pci_dev_t d, int where)
+static void __iomem *get_bus_address(struct imx_pcie_priv *priv,
+ pci_dev_t d, int where)
{
- uint32_t va_address;
+ void __iomem *va_address;
/* Reconfigure Region #0 */
- writel(0, MX6_DBI_ADDR + PCIE_ATU_VIEWPORT);
+ writel(0, priv->dbi_base + PCIE_ATU_VIEWPORT);
if (PCI_BUS(d) < 2)
- writel(PCIE_ATU_TYPE_CFG0, MX6_DBI_ADDR + PCIE_ATU_CR1);
+ writel(PCIE_ATU_TYPE_CFG0, priv->dbi_base + PCIE_ATU_CR1);
else
- writel(PCIE_ATU_TYPE_CFG1, MX6_DBI_ADDR + PCIE_ATU_CR1);
+ writel(PCIE_ATU_TYPE_CFG1, priv->dbi_base + PCIE_ATU_CR1);
if (PCI_BUS(d) == 0) {
- va_address = MX6_DBI_ADDR;
+ va_address = priv->dbi_base;
} else {
- writel(d << 8, MX6_DBI_ADDR + PCIE_ATU_LOWER_TARGET);
- va_address = MX6_IO_ADDR + SZ_16M - SZ_1M;
+ writel(d << 8, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
+ va_address = priv->cfg_base;
}
va_address += (where & ~0x3);
@@ -374,10 +384,10 @@ static void imx_pcie_fix_dabt_handler(bool set)
}
}
-static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
- int where, u32 *val)
+static int imx_pcie_read_cfg(struct imx_pcie_priv *priv, pci_dev_t d,
+ int where, u32 *val)
{
- uint32_t va_address;
+ void __iomem *va_address;
int ret;
ret = imx_pcie_addr_valid(d);
@@ -386,7 +396,7 @@ static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
return 0;
}
- va_address = get_bus_address(d, where);
+ va_address = get_bus_address(priv, d, where);
/*
* Read the PCIe config space. We must replace the DABT handler
@@ -403,17 +413,17 @@ static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
return 0;
}
-static int imx_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
- int where, u32 val)
+static int imx_pcie_write_cfg(struct imx_pcie_priv *priv, pci_dev_t d,
+ int where, u32 val)
{
- uint32_t va_address = 0;
+ void __iomem *va_address = NULL;
int ret;
ret = imx_pcie_addr_valid(d);
if (ret)
return ret;
- va_address = get_bus_address(d, where);
+ va_address = get_bus_address(priv, d, where);
/*
* Write the PCIe config space. We must replace the DABT handler
@@ -430,7 +440,8 @@ static int imx_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
/*
* Initial bus setup
*/
-static int imx6_pcie_assert_core_reset(bool prepare_for_boot)
+static int imx6_pcie_assert_core_reset(struct imx_pcie_priv *priv,
+ bool prepare_for_boot)
{
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
@@ -465,12 +476,12 @@ static int imx6_pcie_assert_core_reset(bool prepare_for_boot)
gpr12 = readl(&iomuxc_regs->gpr[12]);
if ((gpr1 & IOMUXC_GPR1_PCIE_REF_CLK_EN) &&
(gpr12 & IOMUXC_GPR12_PCIE_CTL_2)) {
- val = readl(MX6_DBI_ADDR + PCIE_PL_PFLR);
+ val = readl(priv->dbi_base + PCIE_PL_PFLR);
val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
val |= PCIE_PL_PFLR_FORCE_LINK;
imx_pcie_fix_dabt_handler(true);
- writel(val, MX6_DBI_ADDR + PCIE_PL_PFLR);
+ writel(val, priv->dbi_base + PCIE_PL_PFLR);
imx_pcie_fix_dabt_handler(false);
gpr12 &= ~IOMUXC_GPR12_PCIE_CTL_2;
@@ -602,17 +613,17 @@ static int imx6_pcie_deassert_core_reset(void)
return 0;
}
-static int imx_pcie_link_up(void)
+static int imx_pcie_link_up(struct imx_pcie_priv *priv)
{
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
uint32_t tmp;
int count = 0;
- imx6_pcie_assert_core_reset(false);
+ imx6_pcie_assert_core_reset(priv, false);
imx6_pcie_init_phy();
imx6_pcie_deassert_core_reset();
- imx_pcie_regions_setup();
+ imx_pcie_regions_setup(priv);
/*
* By default, the subordinate is set equally to the secondary
@@ -621,9 +632,9 @@ static int imx_pcie_link_up(void)
* Force the PCIe RC subordinate to 0xff, otherwise no downstream
* devices will be detected if the enumeration is applied strictly.
*/
- tmp = readl(MX6_DBI_ADDR + 0x18);
+ tmp = readl(priv->dbi_base + 0x18);
tmp |= (0xff << 16);
- writel(tmp, MX6_DBI_ADDR + 0x18);
+ writel(tmp, priv->dbi_base + 0x18);
/*
* FIXME: Force the PCIe RC to Gen1 operation
@@ -631,15 +642,15 @@ static int imx_pcie_link_up(void)
* up, otherwise no downstream devices are detected. After the
* link is up, a managed Gen1->Gen2 transition can be initiated.
*/
- tmp = readl(MX6_DBI_ADDR + 0x7c);
+ tmp = readl(priv->dbi_base + 0x7c);
tmp &= ~0xf;
tmp |= 0x1;
- writel(tmp, MX6_DBI_ADDR + 0x7c);
+ writel(tmp, priv->dbi_base + 0x7c);
/* LTSSM enable, starting link. */
setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE);
- while (!imx6_pcie_link_up()) {
+ while (!imx6_pcie_link_up(priv)) {
udelay(10);
count++;
if (count >= 4000) {
@@ -647,8 +658,8 @@ static int imx_pcie_link_up(void)
puts("PCI: pcie phy link never came up\n");
#endif
debug("DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R0),
- readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R1));
+ readl(priv->dbi_base + PCIE_PHY_DEBUG_R0),
+ readl(priv->dbi_base + PCIE_PHY_DEBUG_R1));
return -EINVAL;
}
}
@@ -656,6 +667,30 @@ static int imx_pcie_link_up(void)
return 0;
}
+#if !CONFIG_IS_ENABLED(DM_PCI)
+static struct imx_pcie_priv imx_pcie_priv = {
+ .dbi_base = (void __iomem *)MX6_DBI_ADDR,
+ .cfg_base = (void __iomem *)MX6_ROOT_ADDR,
+};
+
+static struct imx_pcie_priv *priv = &imx_pcie_priv;
+
+static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
+ int where, u32 *val)
+{
+ struct imx_pcie_priv *priv = hose->priv_data;
+
+ return imx_pcie_read_cfg(priv, d, where, val);
+}
+
+static int imx_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
+ int where, u32 val)
+{
+ struct imx_pcie_priv *priv = hose->priv_data;
+
+ return imx_pcie_write_cfg(priv, d, where, val);
+}
+
void imx_pcie_init(void)
{
/* Static instance of the controller. */
@@ -665,6 +700,8 @@ void imx_pcie_init(void)
memset(&pcc, 0, sizeof(pcc));
+ hose->priv_data = priv;
+
/* PCI I/O space */
pci_set_region(&hose->regions[0],
MX6_IO_ADDR, MX6_IO_ADDR,
@@ -691,7 +728,7 @@ void imx_pcie_init(void)
imx_pcie_write_config);
/* Start the controller. */
- ret = imx_pcie_link_up();
+ ret = imx_pcie_link_up(priv);
if (!ret) {
pci_register_hose(hose);
@@ -701,7 +738,7 @@ void imx_pcie_init(void)
void imx_pcie_remove(void)
{
- imx6_pcie_assert_core_reset(true);
+ imx6_pcie_assert_core_reset(priv, true);
}
/* Probe function. */
@@ -709,3 +746,86 @@ void pci_init_board(void)
{
imx_pcie_init();
}
+#else
+static int imx_pcie_dm_read_config(struct udevice *dev, pci_dev_t bdf,
+ uint offset, ulong *value,
+ enum pci_size_t size)
+{
+ struct imx_pcie_priv *priv = dev_get_priv(dev);
+ u32 tmpval;
+ int ret;
+
+ ret = imx_pcie_read_cfg(priv, bdf, offset, &tmpval);
+ if (ret)
+ return ret;
+
+ *value = pci_conv_32_to_size(tmpval, offset, size);
+ return 0;
+}
+
+static int imx_pcie_dm_write_config(struct udevice *dev, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct imx_pcie_priv *priv = dev_get_priv(dev);
+ u32 tmpval, newval;
+ int ret;
+
+ ret = imx_pcie_read_cfg(priv, bdf, offset, &tmpval);
+ if (ret)
+ return ret;
+
+ newval = pci_conv_size_to_32(tmpval, value, offset, size);
+ return imx_pcie_write_cfg(priv, bdf, offset, newval);
+}
+
+static int imx_pcie_dm_probe(struct udevice *dev)
+{
+ struct imx_pcie_priv *priv = dev_get_priv(dev);
+
+ return imx_pcie_link_up(priv);
+}
+
+static int imx_pcie_dm_remove(struct udevice *dev)
+{
+ struct imx_pcie_priv *priv = dev_get_priv(dev);
+
+ imx6_pcie_assert_core_reset(priv, true);
+
+ return 0;
+}
+
+static int imx_pcie_ofdata_to_platdata(struct udevice *dev)
+{
+ struct imx_pcie_priv *priv = dev_get_priv(dev);
+
+ priv->dbi_base = (void __iomem *)devfdt_get_addr_index(dev, 0);
+ priv->cfg_base = (void __iomem *)devfdt_get_addr_index(dev, 1);
+ if (!priv->dbi_base || !priv->cfg_base)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct dm_pci_ops imx_pcie_ops = {
+ .read_config = imx_pcie_dm_read_config,
+ .write_config = imx_pcie_dm_write_config,
+};
+
+static const struct udevice_id imx_pcie_ids[] = {
+ { .compatible = "fsl,imx6q-pcie" },
+ { }
+};
+
+U_BOOT_DRIVER(imx_pcie) = {
+ .name = "imx_pcie",
+ .id = UCLASS_PCI,
+ .of_match = imx_pcie_ids,
+ .ops = &imx_pcie_ops,
+ .probe = imx_pcie_dm_probe,
+ .remove = imx_pcie_dm_remove,
+ .ofdata_to_platdata = imx_pcie_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct imx_pcie_priv),
+ .flags = DM_FLAG_OS_PREPARE,
+};
+#endif
diff --git a/drivers/pinctrl/nxp/pinctrl-imx6.c b/drivers/pinctrl/nxp/pinctrl-imx6.c
index d7c95bb738..0c1e7a9c05 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx6.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx6.c
@@ -10,7 +10,7 @@
#include "pinctrl-imx.h"
-static struct imx_pinctrl_soc_info imx6_pinctrl_soc_info;
+static struct imx_pinctrl_soc_info imx6_pinctrl_soc_info __section(".data");
/* FIXME Before reloaction, BSS is overlapped with DT area */
static struct imx_pinctrl_soc_info imx6ul_pinctrl_soc_info = {
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index b0cd260354..450935fdc1 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -48,6 +48,13 @@ config PMIC_AS3722
interface and is designs to cover most of the power managementment
required for a tablets or laptop.
+config DM_PMIC_BD71837
+ bool "Enable Driver Model for PMIC BD71837"
+ depends on DM_PMIC
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC BD71837. The driver implements read/write operations.
+
config DM_PMIC_FAN53555
bool "Enable support for OnSemi FAN53555"
depends on DM_PMIC && DM_REGULATOR && DM_I2C
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index ce250cb155..888dbb2857 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DM_PMIC_FAN53555) += fan53555.o
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o
obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o
+obj-$(CONFIG_$(SPL_)DM_PMIC_BD71837) += bd71837.o
obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o
obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o
obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
diff --git a/drivers/power/pmic/bd71837.c b/drivers/power/pmic/bd71837.c
new file mode 100644
index 0000000000..24d9f7fab7
--- /dev/null
+++ b/drivers/power/pmic/bd71837.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/bd71837.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_children_info[] = {
+ /* buck */
+ { .prefix = "b", .driver = BD71837_REGULATOR_DRIVER},
+ /* ldo */
+ { .prefix = "l", .driver = BD71837_REGULATOR_DRIVER},
+ { },
+};
+
+static int bd71837_reg_count(struct udevice *dev)
+{
+ return BD71837_REG_NUM;
+}
+
+static int bd71837_write(struct udevice *dev, uint reg, const uint8_t *buff,
+ int len)
+{
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ pr_err("write error to device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int bd71837_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ if (dm_i2c_read(dev, reg, buff, len)) {
+ pr_err("read error from device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int bd71837_bind(struct udevice *dev)
+{
+ int children;
+ ofnode regulators_node;
+
+ regulators_node = dev_read_subnode(dev, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ debug("%s: %s regulators subnode not found!", __func__,
+ dev->name);
+ return -ENXIO;
+ }
+
+ debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+ if (!children)
+ debug("%s: %s - no child found\n", __func__, dev->name);
+
+ /* Always return success for this device */
+ return 0;
+}
+
+static struct dm_pmic_ops bd71837_ops = {
+ .reg_count = bd71837_reg_count,
+ .read = bd71837_read,
+ .write = bd71837_write,
+};
+
+static const struct udevice_id bd71837_ids[] = {
+ { .compatible = "rohm,bd71837", .data = 0x4b, },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_bd71837) = {
+ .name = "bd71837 pmic",
+ .id = UCLASS_PMIC,
+ .of_match = bd71837_ids,
+ .bind = bd71837_bind,
+ .ops = &bd71837_ops,
+};
diff --git a/drivers/power/regulator/pfuze100.c b/drivers/power/regulator/pfuze100.c
index 99073d6018..d6d35f3a39 100644
--- a/drivers/power/regulator/pfuze100.c
+++ b/drivers/power/regulator/pfuze100.c
@@ -482,11 +482,11 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
debug("Set voltage for REGULATOR_TYPE_FIXED regulator\n");
return -EINVAL;
} else if (desc->volt_table) {
- for (i = 0; i < desc->vsel_mask; i++) {
+ for (i = 0; i <= desc->vsel_mask; i++) {
if (*uV == desc->volt_table[i])
break;
}
- if (i == desc->vsel_mask) {
+ if (i == desc->vsel_mask + 1) {
debug("Unsupported voltage %u\n", *uV);
return -EINVAL;
}
diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c
index 476df25805..a435e68005 100644
--- a/drivers/serial/serial_mxc.c
+++ b/drivers/serial/serial_mxc.c
@@ -342,6 +342,7 @@ static int mxc_serial_ofdata_to_platdata(struct udevice *dev)
}
static const struct udevice_id mxc_serial_ids[] = {
+ { .compatible = "fsl,imx6sx-uart" },
{ .compatible = "fsl,imx6ul-uart" },
{ .compatible = "fsl,imx7d-uart" },
{ .compatible = "fsl,imx6q-uart" },
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index eb32f082fe..04ddb32a8f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -239,6 +239,14 @@ config STM32_QSPI
used to access the SPI NOR flash chips on platforms embedding
this ST IP core.
+config STM32_SPI
+ bool "STM32 SPI driver"
+ depends on ARCH_STM32MP
+ help
+ Enable the STM32 Serial Peripheral Interface (SPI) driver for STM32MP
+ SoCs. This uses driver model and requires a device tree binding to
+ operate.
+
config TEGRA114_SPI
bool "nVidia Tegra114 SPI driver"
help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 8be9a4baa2..3f9f2fab2b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_SPI_SUNXI) += spi-sunxi.o
obj-$(CONFIG_SH_SPI) += sh_spi.o
obj-$(CONFIG_SH_QSPI) += sh_qspi.o
obj-$(CONFIG_STM32_QSPI) += stm32_qspi.o
+obj-$(CONFIG_STM32_SPI) += stm32_spi.o
obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index 6846762719..d94aaf9fdb 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -38,6 +38,8 @@ __weak int board_spi_cs_gpio(unsigned bus, unsigned cs)
#define CONFIG_SYS_SPI_MXC_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */
#endif
+#define MAX_CS_COUNT 4
+
struct mxc_spi_slave {
struct spi_slave slave;
unsigned long base;
@@ -50,6 +52,8 @@ struct mxc_spi_slave {
unsigned int max_hz;
unsigned int mode;
struct gpio_desc ss;
+ struct gpio_desc cs_gpios[MAX_CS_COUNT];
+ struct udevice *dev;
};
static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave)
@@ -59,22 +63,38 @@ static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave)
static void mxc_spi_cs_activate(struct mxc_spi_slave *mxcs)
{
- if (CONFIG_IS_ENABLED(DM_SPI)) {
- dm_gpio_set_value(&mxcs->ss, 1);
- } else {
- if (mxcs->gpio > 0)
- gpio_set_value(mxcs->gpio, mxcs->ss_pol);
- }
+#if defined(CONFIG_DM_SPI)
+ struct udevice *dev = mxcs->dev;
+ struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
+
+ u32 cs = slave_plat->cs;
+
+ if (!dm_gpio_is_valid(&mxcs->cs_gpios[cs]))
+ return;
+
+ dm_gpio_set_value(&mxcs->cs_gpios[cs], 1);
+#else
+ if (mxcs->gpio > 0)
+ gpio_set_value(mxcs->gpio, mxcs->ss_pol);
+#endif
}
static void mxc_spi_cs_deactivate(struct mxc_spi_slave *mxcs)
{
- if (CONFIG_IS_ENABLED(DM_SPI)) {
- dm_gpio_set_value(&mxcs->ss, 0);
- } else {
- if (mxcs->gpio > 0)
- gpio_set_value(mxcs->gpio, !(mxcs->ss_pol));
- }
+#if defined(CONFIG_DM_SPI)
+ struct udevice *dev = mxcs->dev;
+ struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
+
+ u32 cs = slave_plat->cs;
+
+ if (!dm_gpio_is_valid(&mxcs->cs_gpios[cs]))
+ return;
+
+ dm_gpio_set_value(&mxcs->cs_gpios[cs], 0);
+#else
+ if (mxcs->gpio > 0)
+ gpio_set_value(mxcs->gpio, !(mxcs->ss_pol));
+#endif
}
u32 get_cspi_div(u32 div)
@@ -488,28 +508,35 @@ void spi_release_bus(struct spi_slave *slave)
static int mxc_spi_probe(struct udevice *bus)
{
- struct mxc_spi_slave *plat = bus->platdata;
struct mxc_spi_slave *mxcs = dev_get_platdata(bus);
int node = dev_of_offset(bus);
const void *blob = gd->fdt_blob;
int ret;
+ int i;
- if (gpio_request_by_name(bus, "cs-gpios", 0, &plat->ss,
- GPIOD_IS_OUT)) {
- dev_err(bus, "No cs-gpios property\n");
- return -EINVAL;
+ ret = gpio_request_list_by_name(bus, "cs-gpios", mxcs->cs_gpios,
+ ARRAY_SIZE(mxcs->cs_gpios), 0);
+ if (ret < 0) {
+ pr_err("Can't get %s gpios! Error: %d", bus->name, ret);
+ return ret;
}
- plat->base = devfdt_get_addr(bus);
- if (plat->base == FDT_ADDR_T_NONE)
- return -ENODEV;
+ for (i = 0; i < ARRAY_SIZE(mxcs->cs_gpios); i++) {
+ if (!dm_gpio_is_valid(&mxcs->cs_gpios[i]))
+ continue;
- ret = dm_gpio_set_value(&plat->ss, 0);
- if (ret) {
- dev_err(bus, "Setting cs error\n");
- return ret;
+ ret = dm_gpio_set_dir_flags(&mxcs->cs_gpios[i],
+ GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
+ if (ret) {
+ dev_err(bus, "Setting cs %d error\n", i);
+ return ret;
+ }
}
+ mxcs->base = devfdt_get_addr(bus);
+ if (mxcs->base == FDT_ADDR_T_NONE)
+ return -ENODEV;
+
mxcs->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency",
20000000);
@@ -530,6 +557,8 @@ static int mxc_spi_claim_bus(struct udevice *dev)
struct mxc_spi_slave *mxcs = dev_get_platdata(dev->parent);
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
+ mxcs->dev = dev;
+
return mxc_spi_claim_bus_internal(mxcs, slave_plat->cs);
}
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
new file mode 100644
index 0000000000..34b217584d
--- /dev/null
+++ b/drivers/spi/stm32_spi.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * Driver for STMicroelectronics Serial peripheral interface (SPI)
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <reset.h>
+#include <spi.h>
+
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+
+/* STM32 SPI registers */
+#define STM32_SPI_CR1 0x00
+#define STM32_SPI_CR2 0x04
+#define STM32_SPI_CFG1 0x08
+#define STM32_SPI_CFG2 0x0C
+#define STM32_SPI_SR 0x14
+#define STM32_SPI_IFCR 0x18
+#define STM32_SPI_TXDR 0x20
+#define STM32_SPI_RXDR 0x30
+#define STM32_SPI_I2SCFGR 0x50
+
+/* STM32_SPI_CR1 bit fields */
+#define SPI_CR1_SPE BIT(0)
+#define SPI_CR1_MASRX BIT(8)
+#define SPI_CR1_CSTART BIT(9)
+#define SPI_CR1_CSUSP BIT(10)
+#define SPI_CR1_HDDIR BIT(11)
+#define SPI_CR1_SSI BIT(12)
+
+/* STM32_SPI_CR2 bit fields */
+#define SPI_CR2_TSIZE GENMASK(15, 0)
+
+/* STM32_SPI_CFG1 bit fields */
+#define SPI_CFG1_DSIZE GENMASK(4, 0)
+#define SPI_CFG1_DSIZE_MIN 3
+#define SPI_CFG1_FTHLV_SHIFT 5
+#define SPI_CFG1_FTHLV GENMASK(8, 5)
+#define SPI_CFG1_MBR_SHIFT 28
+#define SPI_CFG1_MBR GENMASK(30, 28)
+#define SPI_CFG1_MBR_MIN 0
+#define SPI_CFG1_MBR_MAX FIELD_GET(SPI_CFG1_MBR, SPI_CFG1_MBR)
+
+/* STM32_SPI_CFG2 bit fields */
+#define SPI_CFG2_COMM_SHIFT 17
+#define SPI_CFG2_COMM GENMASK(18, 17)
+#define SPI_CFG2_MASTER BIT(22)
+#define SPI_CFG2_LSBFRST BIT(23)
+#define SPI_CFG2_CPHA BIT(24)
+#define SPI_CFG2_CPOL BIT(25)
+#define SPI_CFG2_SSM BIT(26)
+#define SPI_CFG2_AFCNTR BIT(31)
+
+/* STM32_SPI_SR bit fields */
+#define SPI_SR_RXP BIT(0)
+#define SPI_SR_TXP BIT(1)
+#define SPI_SR_EOT BIT(3)
+#define SPI_SR_TXTF BIT(4)
+#define SPI_SR_OVR BIT(6)
+#define SPI_SR_SUSP BIT(11)
+#define SPI_SR_RXPLVL_SHIFT 13
+#define SPI_SR_RXPLVL GENMASK(14, 13)
+#define SPI_SR_RXWNE BIT(15)
+
+/* STM32_SPI_IFCR bit fields */
+#define SPI_IFCR_ALL GENMASK(11, 3)
+
+/* STM32_SPI_I2SCFGR bit fields */
+#define SPI_I2SCFGR_I2SMOD BIT(0)
+
+#define MAX_CS_COUNT 4
+
+/* SPI Master Baud Rate min/max divisor */
+#define STM32_MBR_DIV_MIN (2 << SPI_CFG1_MBR_MIN)
+#define STM32_MBR_DIV_MAX (2 << SPI_CFG1_MBR_MAX)
+
+#define STM32_SPI_TIMEOUT_US 100000
+
+/* SPI Communication mode */
+#define SPI_FULL_DUPLEX 0
+#define SPI_SIMPLEX_TX 1
+#define SPI_SIMPLEX_RX 2
+#define SPI_HALF_DUPLEX 3
+
+struct stm32_spi_priv {
+ void __iomem *base;
+ struct clk clk;
+ struct reset_ctl rst_ctl;
+ struct gpio_desc cs_gpios[MAX_CS_COUNT];
+ ulong bus_clk_rate;
+ unsigned int fifo_size;
+ unsigned int cur_bpw;
+ unsigned int cur_hz;
+ unsigned int cur_xferlen; /* current transfer length in bytes */
+ int tx_len; /* number of data to be written in bytes */
+ int rx_len; /* number of data to be read in bytes */
+ const void *tx_buf; /* data to be written, or NULL */
+ void *rx_buf; /* data to be read, or NULL */
+ u32 cur_mode;
+ bool cs_high;
+};
+
+static void stm32_spi_write_txfifo(struct stm32_spi_priv *priv)
+{
+ while ((priv->tx_len > 0) &&
+ (readl(priv->base + STM32_SPI_SR) & SPI_SR_TXP)) {
+ u32 offs = priv->cur_xferlen - priv->tx_len;
+
+ if (priv->tx_len >= sizeof(u32) &&
+ IS_ALIGNED((uintptr_t)(priv->tx_buf + offs), sizeof(u32))) {
+ const u32 *tx_buf32 = (const u32 *)(priv->tx_buf + offs);
+
+ writel(*tx_buf32, priv->base + STM32_SPI_TXDR);
+ priv->tx_len -= sizeof(u32);
+ } else if (priv->tx_len >= sizeof(u16) &&
+ IS_ALIGNED((uintptr_t)(priv->tx_buf + offs), sizeof(u16))) {
+ const u16 *tx_buf16 = (const u16 *)(priv->tx_buf + offs);
+
+ writew(*tx_buf16, priv->base + STM32_SPI_TXDR);
+ priv->tx_len -= sizeof(u16);
+ } else {
+ const u8 *tx_buf8 = (const u8 *)(priv->tx_buf + offs);
+
+ writeb(*tx_buf8, priv->base + STM32_SPI_TXDR);
+ priv->tx_len -= sizeof(u8);
+ }
+ }
+
+ debug("%s: %d bytes left\n", __func__, priv->tx_len);
+}
+
+static void stm32_spi_read_rxfifo(struct stm32_spi_priv *priv)
+{
+ u32 sr = readl(priv->base + STM32_SPI_SR);
+ u32 rxplvl = (sr & SPI_SR_RXPLVL) >> SPI_SR_RXPLVL_SHIFT;
+
+ while ((priv->rx_len > 0) &&
+ ((sr & SPI_SR_RXP) ||
+ ((sr & SPI_SR_EOT) && ((sr & SPI_SR_RXWNE) || (rxplvl > 0))))) {
+ u32 offs = priv->cur_xferlen - priv->rx_len;
+
+ if (IS_ALIGNED((uintptr_t)(priv->rx_buf + offs), sizeof(u32)) &&
+ (priv->rx_len >= sizeof(u32) || (sr & SPI_SR_RXWNE))) {
+ u32 *rx_buf32 = (u32 *)(priv->rx_buf + offs);
+
+ *rx_buf32 = readl(priv->base + STM32_SPI_RXDR);
+ priv->rx_len -= sizeof(u32);
+ } else if (IS_ALIGNED((uintptr_t)(priv->rx_buf + offs), sizeof(u16)) &&
+ (priv->rx_len >= sizeof(u16) ||
+ (!(sr & SPI_SR_RXWNE) &&
+ (rxplvl >= 2 || priv->cur_bpw > 8)))) {
+ u16 *rx_buf16 = (u16 *)(priv->rx_buf + offs);
+
+ *rx_buf16 = readw(priv->base + STM32_SPI_RXDR);
+ priv->rx_len -= sizeof(u16);
+ } else {
+ u8 *rx_buf8 = (u8 *)(priv->rx_buf + offs);
+
+ *rx_buf8 = readb(priv->base + STM32_SPI_RXDR);
+ priv->rx_len -= sizeof(u8);
+ }
+
+ sr = readl(priv->base + STM32_SPI_SR);
+ rxplvl = (sr & SPI_SR_RXPLVL) >> SPI_SR_RXPLVL_SHIFT;
+ }
+
+ debug("%s: %d bytes left\n", __func__, priv->rx_len);
+}
+
+static int stm32_spi_enable(struct stm32_spi_priv *priv)
+{
+ debug("%s\n", __func__);
+
+ /* Enable the SPI hardware */
+ setbits_le32(priv->base + STM32_SPI_CR1, SPI_CR1_SPE);
+
+ return 0;
+}
+
+static int stm32_spi_disable(struct stm32_spi_priv *priv)
+{
+ debug("%s\n", __func__);
+
+ /* Disable the SPI hardware */
+ clrbits_le32(priv->base + STM32_SPI_CR1, SPI_CR1_SPE);
+
+ return 0;
+}
+
+static int stm32_spi_claim_bus(struct udevice *slave)
+{
+ struct udevice *bus = dev_get_parent(slave);
+ struct stm32_spi_priv *priv = dev_get_priv(bus);
+
+ debug("%s\n", __func__);
+
+ /* Enable the SPI hardware */
+ return stm32_spi_enable(priv);
+}
+
+static int stm32_spi_release_bus(struct udevice *slave)
+{
+ struct udevice *bus = dev_get_parent(slave);
+ struct stm32_spi_priv *priv = dev_get_priv(bus);
+
+ debug("%s\n", __func__);
+
+ /* Disable the SPI hardware */
+ return stm32_spi_disable(priv);
+}
+
+static void stm32_spi_stopxfer(struct udevice *dev)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(dev);
+ u32 cr1, sr;
+ int ret;
+
+ debug("%s\n", __func__);
+
+ cr1 = readl(priv->base + STM32_SPI_CR1);
+
+ if (!(cr1 & SPI_CR1_SPE))
+ return;
+
+ /* Wait on EOT or suspend the flow */
+ ret = readl_poll_timeout(priv->base + STM32_SPI_SR, sr,
+ !(sr & SPI_SR_EOT), 100000);
+ if (ret < 0) {
+ if (cr1 & SPI_CR1_CSTART) {
+ writel(cr1 | SPI_CR1_CSUSP, priv->base + STM32_SPI_CR1);
+ if (readl_poll_timeout(priv->base + STM32_SPI_SR,
+ sr, !(sr & SPI_SR_SUSP),
+ 100000) < 0)
+ dev_err(dev, "Suspend request timeout\n");
+ }
+ }
+
+ /* clear status flags */
+ setbits_le32(priv->base + STM32_SPI_IFCR, SPI_IFCR_ALL);
+}
+
+static int stm32_spi_set_cs(struct udevice *dev, unsigned int cs, bool enable)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(dev);
+
+ debug("%s: cs=%d enable=%d\n", __func__, cs, enable);
+
+ if (cs >= MAX_CS_COUNT)
+ return -ENODEV;
+
+ if (!dm_gpio_is_valid(&priv->cs_gpios[cs]))
+ return -EINVAL;
+
+ if (priv->cs_high)
+ enable = !enable;
+
+ return dm_gpio_set_value(&priv->cs_gpios[cs], enable ? 1 : 0);
+}
+
+static int stm32_spi_set_mode(struct udevice *bus, uint mode)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(bus);
+ u32 cfg2_clrb = 0, cfg2_setb = 0;
+
+ debug("%s: mode=%d\n", __func__, mode);
+
+ if (mode & SPI_CPOL)
+ cfg2_setb |= SPI_CFG2_CPOL;
+ else
+ cfg2_clrb |= SPI_CFG2_CPOL;
+
+ if (mode & SPI_CPHA)
+ cfg2_setb |= SPI_CFG2_CPHA;
+ else
+ cfg2_clrb |= SPI_CFG2_CPHA;
+
+ if (mode & SPI_LSB_FIRST)
+ cfg2_setb |= SPI_CFG2_LSBFRST;
+ else
+ cfg2_clrb |= SPI_CFG2_LSBFRST;
+
+ if (cfg2_clrb || cfg2_setb)
+ clrsetbits_le32(priv->base + STM32_SPI_CFG2,
+ cfg2_clrb, cfg2_setb);
+
+ if (mode & SPI_CS_HIGH)
+ priv->cs_high = true;
+ else
+ priv->cs_high = false;
+ return 0;
+}
+
+static int stm32_spi_set_fthlv(struct udevice *dev, u32 xfer_len)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(dev);
+ u32 fthlv, half_fifo;
+
+ /* data packet should not exceed 1/2 of fifo space */
+ half_fifo = (priv->fifo_size / 2);
+
+ /* data_packet should not exceed transfer length */
+ fthlv = (half_fifo > xfer_len) ? xfer_len : half_fifo;
+
+ /* align packet size with data registers access */
+ fthlv -= (fthlv % 4);
+
+ if (!fthlv)
+ fthlv = 1;
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_FTHLV,
+ (fthlv - 1) << SPI_CFG1_FTHLV_SHIFT);
+
+ return 0;
+}
+
+static int stm32_spi_set_speed(struct udevice *bus, uint hz)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(bus);
+ u32 div, mbrdiv;
+
+ debug("%s: hz=%d\n", __func__, hz);
+
+ if (priv->cur_hz == hz)
+ return 0;
+
+ div = DIV_ROUND_UP(priv->bus_clk_rate, hz);
+
+ if (div < STM32_MBR_DIV_MIN ||
+ div > STM32_MBR_DIV_MAX)
+ return -EINVAL;
+
+ /* Determine the first power of 2 greater than or equal to div */
+ if (div & (div - 1))
+ mbrdiv = fls(div);
+ else
+ mbrdiv = fls(div) - 1;
+
+ if ((mbrdiv - 1) < 0)
+ return -EINVAL;
+
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_MBR,
+ (mbrdiv - 1) << SPI_CFG1_MBR_SHIFT);
+
+ priv->cur_hz = hz;
+
+ return 0;
+}
+
+static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev_get_parent(slave);
+ struct dm_spi_slave_platdata *slave_plat;
+ struct stm32_spi_priv *priv = dev_get_priv(bus);
+ u32 sr;
+ u32 ifcr = 0;
+ u32 xferlen;
+ u32 mode;
+ int xfer_status = 0;
+
+ xferlen = bitlen / 8;
+
+ if (xferlen <= SPI_CR2_TSIZE)
+ writel(xferlen, priv->base + STM32_SPI_CR2);
+ else
+ return -EMSGSIZE;
+
+ priv->tx_buf = dout;
+ priv->rx_buf = din;
+ priv->tx_len = priv->tx_buf ? bitlen / 8 : 0;
+ priv->rx_len = priv->rx_buf ? bitlen / 8 : 0;
+
+ mode = SPI_FULL_DUPLEX;
+ if (!priv->tx_buf)
+ mode = SPI_SIMPLEX_RX;
+ else if (!priv->rx_buf)
+ mode = SPI_SIMPLEX_TX;
+
+ if (priv->cur_xferlen != xferlen || priv->cur_mode != mode) {
+ priv->cur_mode = mode;
+ priv->cur_xferlen = xferlen;
+
+ /* Disable the SPI hardware to unlock CFG1/CFG2 registers */
+ stm32_spi_disable(priv);
+
+ clrsetbits_le32(priv->base + STM32_SPI_CFG2, SPI_CFG2_COMM,
+ mode << SPI_CFG2_COMM_SHIFT);
+
+ stm32_spi_set_fthlv(bus, xferlen);
+
+ /* Enable the SPI hardware */
+ stm32_spi_enable(priv);
+ }
+
+ debug("%s: priv->tx_len=%d priv->rx_len=%d\n", __func__,
+ priv->tx_len, priv->rx_len);
+
+ slave_plat = dev_get_parent_platdata(slave);
+ if (flags & SPI_XFER_BEGIN)
+ stm32_spi_set_cs(bus, slave_plat->cs, false);
+
+ /* Be sure to have data in fifo before starting data transfer */
+ if (priv->tx_buf)
+ stm32_spi_write_txfifo(priv);
+
+ setbits_le32(priv->base + STM32_SPI_CR1, SPI_CR1_CSTART);
+
+ while (1) {
+ sr = readl(priv->base + STM32_SPI_SR);
+
+ if (sr & SPI_SR_OVR) {
+ dev_err(bus, "Overrun: RX data lost\n");
+ xfer_status = -EIO;
+ break;
+ }
+
+ if (sr & SPI_SR_SUSP) {
+ dev_warn(bus, "System too slow is limiting data throughput\n");
+
+ if (priv->rx_buf && priv->rx_len > 0)
+ stm32_spi_read_rxfifo(priv);
+
+ ifcr |= SPI_SR_SUSP;
+ }
+
+ if (sr & SPI_SR_TXTF)
+ ifcr |= SPI_SR_TXTF;
+
+ if (sr & SPI_SR_TXP)
+ if (priv->tx_buf && priv->tx_len > 0)
+ stm32_spi_write_txfifo(priv);
+
+ if (sr & SPI_SR_RXP)
+ if (priv->rx_buf && priv->rx_len > 0)
+ stm32_spi_read_rxfifo(priv);
+
+ if (sr & SPI_SR_EOT) {
+ if (priv->rx_buf && priv->rx_len > 0)
+ stm32_spi_read_rxfifo(priv);
+ break;
+ }
+
+ writel(ifcr, priv->base + STM32_SPI_IFCR);
+ }
+
+ /* clear status flags */
+ setbits_le32(priv->base + STM32_SPI_IFCR, SPI_IFCR_ALL);
+ stm32_spi_stopxfer(bus);
+
+ if (flags & SPI_XFER_END)
+ stm32_spi_set_cs(bus, slave_plat->cs, true);
+
+ return xfer_status;
+}
+
+static int stm32_spi_get_fifo_size(struct udevice *dev)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(dev);
+ u32 count = 0;
+
+ stm32_spi_enable(priv);
+
+ while (readl(priv->base + STM32_SPI_SR) & SPI_SR_TXP)
+ writeb(++count, priv->base + STM32_SPI_TXDR);
+
+ stm32_spi_disable(priv);
+
+ debug("%s %d x 8-bit fifo size\n", __func__, count);
+
+ return count;
+}
+
+static int stm32_spi_probe(struct udevice *dev)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(dev);
+ unsigned long clk_rate;
+ int ret;
+ int i;
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ /* enable clock */
+ ret = clk_get_by_index(dev, 0, &priv->clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_enable(&priv->clk);
+ if (ret < 0)
+ return ret;
+
+ clk_rate = clk_get_rate(&priv->clk);
+ if (!clk_rate) {
+ ret = -EINVAL;
+ goto clk_err;
+ }
+
+ priv->bus_clk_rate = clk_rate;
+
+ /* perform reset */
+ ret = reset_get_by_index(dev, 0, &priv->rst_ctl);
+ if (ret < 0)
+ goto clk_err;
+
+ reset_assert(&priv->rst_ctl);
+ udelay(2);
+ reset_deassert(&priv->rst_ctl);
+
+ ret = gpio_request_list_by_name(dev, "cs-gpios", priv->cs_gpios,
+ ARRAY_SIZE(priv->cs_gpios), 0);
+ if (ret < 0) {
+ pr_err("Can't get %s cs gpios: %d", dev->name, ret);
+ goto reset_err;
+ }
+
+ priv->fifo_size = stm32_spi_get_fifo_size(dev);
+
+ priv->cur_mode = SPI_FULL_DUPLEX;
+ priv->cur_xferlen = 0;
+ priv->cur_bpw = SPI_DEFAULT_WORDLEN;
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
+ priv->cur_bpw - 1);
+
+ for (i = 0; i < ARRAY_SIZE(priv->cs_gpios); i++) {
+ if (!dm_gpio_is_valid(&priv->cs_gpios[i]))
+ continue;
+
+ dm_gpio_set_dir_flags(&priv->cs_gpios[i],
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ }
+
+ /* Ensure I2SMOD bit is kept cleared */
+ clrbits_le32(priv->base + STM32_SPI_I2SCFGR, SPI_I2SCFGR_I2SMOD);
+
+ /*
+ * - SS input value high
+ * - transmitter half duplex direction
+ * - automatic communication suspend when RX-Fifo is full
+ */
+ setbits_le32(priv->base + STM32_SPI_CR1,
+ SPI_CR1_SSI | SPI_CR1_HDDIR | SPI_CR1_MASRX);
+
+ /*
+ * - Set the master mode (default Motorola mode)
+ * - Consider 1 master/n slaves configuration and
+ * SS input value is determined by the SSI bit
+ * - keep control of all associated GPIOs
+ */
+ setbits_le32(priv->base + STM32_SPI_CFG2,
+ SPI_CFG2_MASTER | SPI_CFG2_SSM | SPI_CFG2_AFCNTR);
+
+ return 0;
+
+reset_err:
+ reset_free(&priv->rst_ctl);
+
+clk_err:
+ clk_disable(&priv->clk);
+ clk_free(&priv->clk);
+
+ return ret;
+};
+
+static int stm32_spi_remove(struct udevice *dev)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ stm32_spi_stopxfer(dev);
+ stm32_spi_disable(priv);
+
+ ret = reset_assert(&priv->rst_ctl);
+ if (ret < 0)
+ return ret;
+
+ reset_free(&priv->rst_ctl);
+
+ ret = clk_disable(&priv->clk);
+ if (ret < 0)
+ return ret;
+
+ clk_free(&priv->clk);
+
+ return ret;
+};
+
+static const struct dm_spi_ops stm32_spi_ops = {
+ .claim_bus = stm32_spi_claim_bus,
+ .release_bus = stm32_spi_release_bus,
+ .set_mode = stm32_spi_set_mode,
+ .set_speed = stm32_spi_set_speed,
+ .xfer = stm32_spi_xfer,
+};
+
+static const struct udevice_id stm32_spi_ids[] = {
+ { .compatible = "st,stm32h7-spi", },
+ { }
+};
+
+U_BOOT_DRIVER(stm32_spi) = {
+ .name = "stm32_spi",
+ .id = UCLASS_SPI,
+ .of_match = stm32_spi_ids,
+ .ops = &stm32_spi_ops,
+ .priv_auto_alloc_size = sizeof(struct stm32_spi_priv),
+ .probe = stm32_spi_probe,
+ .remove = stm32_spi_remove,
+};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b01dbc446d..dbafb74a34 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -26,6 +26,13 @@ config BCM2835_WDT
This provides basic infrastructure to support BCM2835/2836 watchdog
hardware, with a max timeout of ~15secs.
+config IMX_WATCHDOG
+ bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP"
+ select HW_WATCHDOG
+ help
+ Select this to enable the IMX and LSCH2 of Layerscape watchdog
+ driver.
+
config OMAP_WATCHDOG
bool "TI OMAP watchdog driver"
depends on ARCH_OMAP2PLUS
@@ -59,14 +66,6 @@ config WDT
What exactly happens when the timer expires is up to a particular
device/driver.
-config WDT_SANDBOX
- bool "Enable Watchdog Timer support for Sandbox"
- depends on SANDBOX && WDT
- help
- Enable Watchdog Timer support in Sandbox. This is a dummy device that
- can be probed and supports all of the methods of WDT, but does not
- really do anything.
-
config WDT_ARMADA_37XX
bool "Marvell Armada 37xx watchdog timer support"
depends on WDT && ARMADA_3700
@@ -87,6 +86,13 @@ config WDT_ASPEED
It currently does not support Boot Flash Addressing Mode Detection or
Second Boot.
+config WDT_AT91
+ bool "AT91 watchdog timer support"
+ depends on WDT
+ help
+ Select this to enable Microchip watchdog timer, which can be found on
+ some AT91 devices.
+
config WDT_BCM6345
bool "BCM6345 watchdog timer support"
depends on WDT && (ARCH_BMIPS || ARCH_BCM6858 || ARCH_BCM63158)
@@ -95,21 +101,6 @@ config WDT_BCM6345
The watchdog timer is stopped when initialized.
It performs full SoC reset.
-config WDT_ORION
- bool "Orion watchdog timer support"
- depends on WDT
- select CLK
- help
- Select this to enable Orion watchdog timer, which can be found on some
- Marvell Armada chips.
-
-config WDT_SP805
- bool "SP805 watchdog timer support"
- depends on WDT
- help
- Select this to enable SP805 watchdog timer, which can be found on some
- nxp layerscape chips.
-
config WDT_CDNS
bool "Cadence watchdog timer support"
depends on WDT
@@ -118,6 +109,20 @@ config WDT_CDNS
Select this to enable Cadence watchdog timer, which can be found on some
Xilinx Microzed Platform.
+config WDT_MPC8xx
+ bool "MPC8xx watchdog timer support"
+ depends on WDT && MPC8xx
+ select CONFIG_MPC8xx_WATCHDOG
+ help
+ Select this to enable mpc8xx watchdog timer
+
+config WDT_MT7621
+ bool "MediaTek MT7621 watchdog timer support"
+ depends on WDT && SOC_MT7628
+ help
+ Select this to enable Ralink / Mediatek watchdog timer,
+ which can be found on some MediaTek chips.
+
config WDT_MTK
bool "MediaTek watchdog timer support"
depends on WDT && ARCH_MEDIATEK
@@ -126,39 +131,43 @@ config WDT_MTK
The watchdog timer is stopped when initialized.
It performs full SoC reset.
-config XILINX_TB_WATCHDOG
- bool "Xilinx Axi watchdog timer support"
+config WDT_ORION
+ bool "Orion watchdog timer support"
depends on WDT
- imply WATCHDOG
+ select CLK
help
- Select this to enable Xilinx Axi watchdog timer, which can be found on some
- Xilinx Microblaze Platforms.
+ Select this to enable Orion watchdog timer, which can be found on some
+ Marvell Armada chips.
-config IMX_WATCHDOG
- bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP"
- select HW_WATCHDOG
+config WDT_SANDBOX
+ bool "Enable Watchdog Timer support for Sandbox"
+ depends on SANDBOX && WDT
help
- Select this to enable the IMX and LSCH2 of Layerscape watchdog
- driver.
+ Enable Watchdog Timer support in Sandbox. This is a dummy device that
+ can be probed and supports all of the methods of WDT, but does not
+ really do anything.
-config WDT_AT91
- bool "AT91 watchdog timer support"
+config WDT_SP805
+ bool "SP805 watchdog timer support"
depends on WDT
help
- Select this to enable Microchip watchdog timer, which can be found on
- some AT91 devices.
+ Select this to enable SP805 watchdog timer, which can be found on some
+ nxp layerscape chips.
-config WDT_MT7621
- bool "MediaTek MT7621 watchdog timer support"
- depends on WDT && SOC_MT7628
+config WDT_STM32MP
+ bool "IWDG watchdog driver for STM32 MP's family"
+ depends on WDT
+ imply WATCHDOG
help
- Select this to enable Ralink / Mediatek watchdog timer,
- which can be found on some MediaTek chips.
+ Enable the STM32 watchdog (IWDG) driver. Enable support to
+ configure STM32's on-SoC watchdog.
-config WDT_MPC8xx
- bool "MPC8xx watchdog timer support"
- depends on WDT && MPC8xx
+config XILINX_TB_WATCHDOG
+ bool "Xilinx Axi watchdog timer support"
+ depends on WDT
+ imply WATCHDOG
help
- Select this to enable mpc8xx watchdog timer
+ Select this to enable Xilinx Axi watchdog timer, which can be found on some
+ Xilinx Microblaze Platforms.
endmenu
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 6f20e73810..e3f4fdb406 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
+obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
diff --git a/drivers/watchdog/stm32mp_wdt.c b/drivers/watchdog/stm32mp_wdt.c
new file mode 100644
index 0000000000..8093d0a9f4
--- /dev/null
+++ b/drivers/watchdog/stm32mp_wdt.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <syscon.h>
+#include <wdt.h>
+#include <asm/io.h>
+#include <linux/iopoll.h>
+
+/* IWDG registers */
+#define IWDG_KR 0x00 /* Key register */
+#define IWDG_PR 0x04 /* Prescaler Register */
+#define IWDG_RLR 0x08 /* ReLoad Register */
+#define IWDG_SR 0x0C /* Status Register */
+
+/* IWDG_KR register bit mask */
+#define KR_KEY_RELOAD 0xAAAA /* Reload counter enable */
+#define KR_KEY_ENABLE 0xCCCC /* Peripheral enable */
+#define KR_KEY_EWA 0x5555 /* Write access enable */
+
+/* IWDG_PR register bit values */
+#define PR_256 0x06 /* Prescaler set to 256 */
+
+/* IWDG_RLR register values */
+#define RLR_MAX 0xFFF /* Max value supported by reload register */
+
+/* IWDG_SR register bit values */
+#define SR_PVU BIT(0) /* Watchdog prescaler value update */
+#define SR_RVU BIT(1) /* Watchdog counter reload value update */
+
+struct stm32mp_wdt_priv {
+ fdt_addr_t base; /* registers addr in physical memory */
+ unsigned long wdt_clk_rate; /* Watchdog dedicated clock rate */
+};
+
+static int stm32mp_wdt_reset(struct udevice *dev)
+{
+ struct stm32mp_wdt_priv *priv = dev_get_priv(dev);
+
+ writel(KR_KEY_RELOAD, priv->base + IWDG_KR);
+
+ return 0;
+}
+
+static int stm32mp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
+{
+ struct stm32mp_wdt_priv *priv = dev_get_priv(dev);
+ int reload;
+ u32 val;
+ int ret;
+
+ /* Prescaler fixed to 256 */
+ reload = timeout_ms * priv->wdt_clk_rate / 256;
+ if (reload > RLR_MAX + 1)
+ /* Force to max watchdog counter reload value */
+ reload = RLR_MAX + 1;
+ else if (!reload)
+ /* Force to min watchdog counter reload value */
+ reload = priv->wdt_clk_rate / 256;
+
+ /* Set prescaler & reload registers */
+ writel(KR_KEY_EWA, priv->base + IWDG_KR);
+ writel(PR_256, priv->base + IWDG_PR);
+ writel(reload - 1, priv->base + IWDG_RLR);
+
+ /* Enable watchdog */
+ writel(KR_KEY_ENABLE, priv->base + IWDG_KR);
+
+ /* Wait for the registers to be updated */
+ ret = readl_poll_timeout(priv->base + IWDG_SR, val,
+ val & (SR_PVU | SR_RVU), CONFIG_SYS_HZ);
+
+ if (ret < 0) {
+ pr_err("Updating IWDG registers timeout");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int stm32mp_wdt_probe(struct udevice *dev)
+{
+ struct stm32mp_wdt_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int ret;
+
+ debug("IWDG init\n");
+
+ priv->base = devfdt_get_addr(dev);
+ if (priv->base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Enable clock */
+ ret = clk_get_by_name(dev, "pclk", &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+ /* Get LSI clock */
+ ret = clk_get_by_name(dev, "lsi", &clk);
+ if (ret)
+ return ret;
+
+ priv->wdt_clk_rate = clk_get_rate(&clk);
+
+ debug("IWDG init done\n");
+
+ return 0;
+}
+
+static const struct wdt_ops stm32mp_wdt_ops = {
+ .start = stm32mp_wdt_start,
+ .reset = stm32mp_wdt_reset,
+};
+
+static const struct udevice_id stm32mp_wdt_match[] = {
+ { .compatible = "st,stm32mp1-iwdg" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(stm32mp_wdt) = {
+ .name = "stm32mp-wdt",
+ .id = UCLASS_WDT,
+ .of_match = stm32mp_wdt_match,
+ .priv_auto_alloc_size = sizeof(struct stm32mp_wdt_priv),
+ .probe = stm32mp_wdt_probe,
+ .ops = &stm32mp_wdt_ops,
+};