diff options
author | Tom Rini <trini@konsulko.com> | 2021-07-07 01:10:10 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2021-07-07 01:10:10 +0300 |
commit | 5617efd2c882562b716a61bc0dc0edda46b045df (patch) | |
tree | ea55ae3b5f8b5ed7cc2b51e07c03c13cf8058147 /drivers | |
parent | b5f9d2f3aae7b208273546965be4ea5497930882 (diff) | |
parent | 725cf89512eba9a49d447f009e0b97fdf2ae5dd6 (diff) | |
download | u-boot-5617efd2c882562b716a61bc0dc0edda46b045df.tar.xz |
Merge branch '2021-07-06-platform-updates'
- mpc8379erdb DM_USB, DM_PCI and DM_ETH support.
- Drop PCI support from the integrator family of boards
- Add synquacer support
- Assorted lpc32xx updates and improvements
- snapdragon (and related) fixes, Broadcom iproc update
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/ahci-pci.c | 2 | ||||
-rw-r--r-- | drivers/i2c/Kconfig | 13 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/lpc32xx_i2c.c | 15 | ||||
-rw-r--r-- | drivers/i2c/synquacer_i2c.c | 338 | ||||
-rw-r--r-- | drivers/mmc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/f_sdh30.c | 81 | ||||
-rw-r--r-- | drivers/pci/Kconfig | 12 | ||||
-rw-r--r-- | drivers/pci/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/pci-uclass.c | 3 | ||||
-rw-r--r-- | drivers/pci/pcie_ecam_synquacer.c | 600 | ||||
-rw-r--r-- | drivers/serial/serial_msm.c | 4 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 8 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-synquacer.c | 491 |
16 files changed, 1574 insertions, 7 deletions
diff --git a/drivers/ata/ahci-pci.c b/drivers/ata/ahci-pci.c index 11ec98b56f..b1d231e0f9 100644 --- a/drivers/ata/ahci-pci.c +++ b/drivers/ata/ahci-pci.c @@ -5,6 +5,7 @@ #include <common.h> #include <ahci.h> +#include <scsi.h> #include <dm.h> #include <pci.h> @@ -28,6 +29,7 @@ static const struct udevice_id ahci_pci_ids[] = { U_BOOT_DRIVER(ahci_pci) = { .name = "ahci_pci", .id = UCLASS_AHCI, + .ops = &scsi_ops, .of_match = ahci_pci_ids, .bind = ahci_pci_bind, .probe = ahci_pci_probe, diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 57a4efb88e..41065dd502 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -169,6 +169,12 @@ config SYS_I2C_IMX_LPI2C help Add support for the NXP i.MX LPI2C driver. +config SYS_I2C_LPC32XX + bool "LPC32XX I2C driver" + depends on ARCH_LPC32XX + help + Enable support for the LPC32xx I2C driver. + config SYS_I2C_MESON bool "Amlogic Meson I2C driver" depends on DM_I2C && ARCH_MESON @@ -455,6 +461,13 @@ config SYS_I2C_STM32F7 _ Optional clock stretching _ Software reset +config SYS_I2C_SYNQUACER + bool "Socionext SynQuacer I2C controller" + depends on ARCH_SYNQUACER && DM_I2C + help + Support for Socionext Synquacer I2C controller. This I2C controller + will be used for RTC and LS-connector on DeveloperBox. + config SYS_I2C_TEGRA bool "NVIDIA Tegra internal I2C controller" depends on ARCH_TEGRA diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 8c9f1fcd8b..06a1150f03 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o +obj-$(CONFIG_SYS_I2C_SYNQUACER) += synquacer_i2c.o obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o diff --git a/drivers/i2c/lpc32xx_i2c.c b/drivers/i2c/lpc32xx_i2c.c index f89f7955e4..774129ad8e 100644 --- a/drivers/i2c/lpc32xx_i2c.c +++ b/drivers/i2c/lpc32xx_i2c.c @@ -38,7 +38,6 @@ /* Status register values */ #define LPC32XX_I2C_STAT_TFF 0x00000400 #define LPC32XX_I2C_STAT_RFE 0x00000200 -#define LPC32XX_I2C_STAT_DRMI 0x00000008 #define LPC32XX_I2C_STAT_NAI 0x00000004 #define LPC32XX_I2C_STAT_TDI 0x00000001 @@ -283,11 +282,7 @@ static int lpc32xx_i2c_probe(struct udevice *bus) { struct lpc32xx_i2c_dev *dev = dev_get_plat(bus); - /* - * FIXME: This is not permitted - * dev_seq(bus) = dev->index; - */ - + dev->base = dev_read_addr_ptr(bus); __i2c_init(dev->base, dev->speed, 0, dev->index); return 0; } @@ -353,9 +348,15 @@ static const struct dm_i2c_ops lpc32xx_i2c_ops = { .set_bus_speed = lpc32xx_i2c_set_bus_speed, }; +static const struct udevice_id lpc32xx_i2c_ids[] = { + { .compatible = "nxp,pnx-i2c" }, + { } +}; + U_BOOT_DRIVER(i2c_lpc32xx) = { - .id = UCLASS_I2C, .name = "i2c_lpc32xx", + .id = UCLASS_I2C, + .of_match = lpc32xx_i2c_ids, .probe = lpc32xx_i2c_probe, .ops = &lpc32xx_i2c_ops, }; diff --git a/drivers/i2c/synquacer_i2c.c b/drivers/i2c/synquacer_i2c.c new file mode 100644 index 0000000000..6672d9435e --- /dev/null +++ b/drivers/i2c/synquacer_i2c.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + */ + +#include <dm/device_compat.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/sizes.h> +#include <linux/types.h> +#include <dm.h> +#include <fdtdec.h> +#include <i2c.h> +#include <clk.h> + +#define REG_BSR 0x0 +#define REG_BCR 0x4 +#define REG_CCR 0x8 +#define REG_ADR 0xc +#define REG_DAR 0x10 +#define REG_CSR 0x14 +#define REG_FSR 0x18 +#define REG_BC2R 0x1c + +/* I2C register bit definitions */ +#define BSR_FBT BIT(0) // First Byte Transfer +#define BSR_GCA BIT(1) // General Call Address +#define BSR_AAS BIT(2) // Address as Slave +#define BSR_TRX BIT(3) // Transfer/Receive +#define BSR_LRB BIT(4) // Last Received Bit +#define BSR_AL BIT(5) // Arbitration Lost +#define BSR_RSC BIT(6) // Repeated Start Cond. +#define BSR_BB BIT(7) // Bus Busy + +#define BCR_INT BIT(0) // Interrupt +#define BCR_INTE BIT(1) // Interrupt Enable +#define BCR_GCAA BIT(2) // Gen. Call Access Ack. +#define BCR_ACK BIT(3) // Acknowledge +#define BCR_MSS BIT(4) // Master Slave Select +#define BCR_SCC BIT(5) // Start Condition Cont. +#define BCR_BEIE BIT(6) // Bus Error Int Enable +#define BCR_BER BIT(7) // Bus Error + +#define CCR_CS_MASK (0x1f) // CCR Clock Period Sel. +#define CCR_EN BIT(5) // Enable +#define CCR_FM BIT(6) // Speed Mode Select + +#define CSR_CS_MASK (0x3f) // CSR Clock Period Sel. + +#define BC2R_SCLL BIT(0) // SCL Low Drive +#define BC2R_SDAL BIT(1) // SDA Low Drive +#define BC2R_SCLS BIT(4) // SCL Status +#define BC2R_SDAS BIT(5) // SDA Status + +/* PCLK frequency */ +#define BUS_CLK_FR(rate) (((rate) / 20000000) + 1) + +#define I2C_CLK_DEF 62500000 + +/* STANDARD MODE frequency */ +#define CLK_MASTER_STD(rate) \ + DIV_ROUND_UP(DIV_ROUND_UP((rate), I2C_SPEED_STANDARD_RATE) - 2, 2) +/* FAST MODE frequency */ +#define CLK_MASTER_FAST(rate) \ + DIV_ROUND_UP((DIV_ROUND_UP((rate), I2C_SPEED_FAST_RATE) - 2) * 2, 3) + +/* (clkrate <= 18000000) */ +/* calculate the value of CS bits in CCR register on standard mode */ +#define CCR_CS_STD_MAX_18M(rate) \ + ((CLK_MASTER_STD(rate) - 65) \ + & CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on standard mode */ +#define CSR_CS_STD_MAX_18M(rate) 0x00 + +/* calculate the value of CS bits in CCR register on fast mode */ +#define CCR_CS_FAST_MAX_18M(rate) \ + ((CLK_MASTER_FAST(rate) - 1) \ + & CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on fast mode */ +#define CSR_CS_FAST_MAX_18M(rate) 0x00 + +/* (clkrate > 18000000) */ +/* calculate the value of CS bits in CCR register on standard mode */ +#define CCR_CS_STD_MIN_18M(rate) \ + ((CLK_MASTER_STD(rate) - 1) \ + & CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on standard mode */ +#define CSR_CS_STD_MIN_18M(rate) \ + (((CLK_MASTER_STD(rate) - 1) >> 5) \ + & CSR_CS_MASK) + +/* calculate the value of CS bits in CCR register on fast mode */ +#define CCR_CS_FAST_MIN_18M(rate) \ + ((CLK_MASTER_FAST(rate) - 1) \ + & CCR_CS_MASK) + +/* calculate the value of CS bits in CSR register on fast mode */ +#define CSR_CS_FAST_MIN_18M(rate) \ + (((CLK_MASTER_FAST(rate) - 1) >> 5) \ + & CSR_CS_MASK) + +/* min I2C clock frequency 14M */ +#define MIN_CLK_RATE (14 * 1000000) +/* max I2C clock frequency 200M */ +#define MAX_CLK_RATE (200 * 1000000) +/* I2C clock frequency 18M */ +#define CLK_RATE_18M (18 * 1000000) + +#define SPEED_FM 400 // Fast Mode +#define SPEED_SM 100 // Standard Mode + +DECLARE_GLOBAL_DATA_PTR; + +struct synquacer_i2c { + void __iomem *base; + unsigned long pclkrate; + unsigned long speed_khz; +}; + +static int wait_irq(struct udevice *dev) +{ + struct synquacer_i2c *i2c = dev_get_priv(dev); + int timeout = 500000; + + do { + if (readb(i2c->base + REG_BCR) & BCR_INT) + return 0; + } while (timeout--); + + pr_err("%s: timeout\n", __func__); + return -1; +} + +static int synquacer_i2c_xfer_start(struct synquacer_i2c *i2c, + int addr, int read) +{ + u8 bsr, bcr; + + writeb((addr << 1) | (read ? 1 : 0), i2c->base + REG_DAR); + + bsr = readb(i2c->base + REG_BSR); + bcr = readb(i2c->base + REG_BCR); + + if ((bsr & BSR_BB) && !(bcr & BCR_MSS)) + return -EBUSY; + + if (bsr & BSR_BB) { + writeb(bcr | BCR_SCC, i2c->base + REG_BCR); + } else { + if (bcr & BCR_MSS) + return -EAGAIN; + /* Start Condition + Enable Interrupts */ + writeb(bcr | BCR_MSS | BCR_INTE | BCR_BEIE, i2c->base + REG_BCR); + } + + udelay(100); + return 0; +} + +static int synquacer_i2c_xfer(struct udevice *bus, + struct i2c_msg *msg, int nmsgs) +{ + struct synquacer_i2c *i2c = dev_get_priv(bus); + u8 bsr, bcr; + int idx; + + for (; nmsgs > 0; nmsgs--, msg++) { + synquacer_i2c_xfer_start(i2c, msg->addr, msg->flags & I2C_M_RD); + if (wait_irq(bus)) + return -EREMOTEIO; + + bsr = readb(i2c->base + REG_BSR); + if (bsr & BSR_LRB) { + debug("%s: No ack received\n", __func__); + return -EREMOTEIO; + } + + idx = 0; + do { + bsr = readb(i2c->base + REG_BSR); + bcr = readb(i2c->base + REG_BCR); + if (bcr & BCR_BER) { + debug("%s: Bus error detected\n", __func__); + return -EREMOTEIO; + } + if ((bsr & BSR_AL) || !(bcr & BCR_MSS)) { + debug("%s: Arbitration lost\n", __func__); + return -EREMOTEIO; + } + + if (msg->flags & I2C_M_RD) { + bcr = BCR_MSS | BCR_INTE | BCR_BEIE; + if (idx < msg->len - 1) + bcr |= BCR_ACK; + writeb(bcr, i2c->base + REG_BCR); + if (wait_irq(bus)) + return -EREMOTEIO; + bsr = readb(i2c->base + REG_BSR); + if (!(bsr & BSR_FBT)) + msg->buf[idx++] = readb(i2c->base + REG_DAR); + } else { + writeb(msg->buf[idx++], i2c->base + REG_DAR); + bcr = BCR_MSS | BCR_INTE | BCR_BEIE; + writeb(bcr, i2c->base + REG_BCR); + if (wait_irq(bus)) + return -EREMOTEIO; + bsr = readb(i2c->base + REG_BSR); + if (bsr & BSR_LRB) { + debug("%s: no ack\n", __func__); + return -EREMOTEIO; + } + } + } while (idx < msg->len); + } + + /* Force bus state to idle, terminating any ongoing transfer */ + writeb(0, i2c->base + REG_BCR); + udelay(100); + + return 0; +} + +static void synquacer_i2c_hw_reset(struct synquacer_i2c *i2c) +{ + /* Disable clock */ + writeb(0, i2c->base + REG_CCR); + writeb(0, i2c->base + REG_CSR); + + /* Set own Address */ + writeb(0, i2c->base + REG_ADR); + + /* Set PCLK frequency */ + writeb(BUS_CLK_FR(i2c->pclkrate), i2c->base + REG_FSR); + + /* clear IRQ (INT=0, BER=0), Interrupt Disable */ + writeb(0, i2c->base + REG_BCR); + writeb(0, i2c->base + REG_BC2R); +} + +static int synquacer_i2c_get_bus_speed(struct udevice *bus) +{ + struct synquacer_i2c *i2c = dev_get_priv(bus); + + return i2c->speed_khz * 1000; +} + +static int synquacer_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct synquacer_i2c *i2c = dev_get_priv(bus); + u32 rt = i2c->pclkrate; + u8 ccr_cs, csr_cs; + + /* Set PCLK frequency */ + writeb(BUS_CLK_FR(i2c->pclkrate), i2c->base + REG_FSR); + + if (speed >= SPEED_FM * 1000) { + i2c->speed_khz = SPEED_FM; + if (i2c->pclkrate <= CLK_RATE_18M) { + ccr_cs = CCR_CS_FAST_MAX_18M(rt); + csr_cs = CSR_CS_FAST_MAX_18M(rt); + } else { + ccr_cs = CCR_CS_FAST_MIN_18M(rt); + csr_cs = CSR_CS_FAST_MIN_18M(rt); + } + + /* Set Clock and enable, Set fast mode */ + writeb(ccr_cs | CCR_FM | CCR_EN, i2c->base + REG_CCR); + writeb(csr_cs, i2c->base + REG_CSR); + } else { + i2c->speed_khz = SPEED_SM; + if (i2c->pclkrate <= CLK_RATE_18M) { + ccr_cs = CCR_CS_STD_MAX_18M(rt); + csr_cs = CSR_CS_STD_MAX_18M(rt); + } else { + ccr_cs = CCR_CS_STD_MIN_18M(rt); + csr_cs = CSR_CS_STD_MIN_18M(rt); + } + + /* Set Clock and enable, Set standard mode */ + writeb(ccr_cs | CCR_EN, i2c->base + REG_CCR); + writeb(csr_cs, i2c->base + REG_CSR); + } + + return 0; +} + +static int synquacer_i2c_of_to_plat(struct udevice *bus) +{ + struct synquacer_i2c *priv = dev_get_priv(bus); + struct clk ck; + int ret; + + ret = clk_get_by_index(bus, 0, &ck); + if (ret < 0) { + priv->pclkrate = I2C_CLK_DEF; + } else { + clk_enable(&ck); + priv->pclkrate = clk_get_rate(&ck); + } + + return 0; +} + +static int synquacer_i2c_probe(struct udevice *bus) +{ + struct synquacer_i2c *i2c = dev_get_priv(bus); + + i2c->base = dev_read_addr_ptr(bus); + synquacer_i2c_hw_reset(i2c); + synquacer_i2c_set_bus_speed(bus, 400000); /* set default speed */ + return 0; +} + +static const struct dm_i2c_ops synquacer_i2c_ops = { + .xfer = synquacer_i2c_xfer, + .set_bus_speed = synquacer_i2c_set_bus_speed, + .get_bus_speed = synquacer_i2c_get_bus_speed, +}; + +static const struct udevice_id synquacer_i2c_ids[] = { + { + .compatible = "socionext,synquacer-i2c", + }, + { } +}; + +U_BOOT_DRIVER(sni_synquacer_i2c) = { + .name = "sni_synquacer_i2c", + .id = UCLASS_I2C, + .of_match = synquacer_i2c_ids, + .of_to_plat = synquacer_i2c_of_to_plat, + .probe = synquacer_i2c_probe, + .priv_auto = sizeof(struct synquacer_i2c), + .ops = &synquacer_i2c_ops, +}; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index d0176175e8..717ce5a62f 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -561,6 +561,16 @@ config MMC_SDHCI_IPROC If unsure, say N. +config MMC_SDHCI_F_SDH30 + bool "SDHCI support for Fujitsu Semiconductor F_SDH30" + depends on BLK && DM_MMC + depends on MMC_SDHCI + help + This selects the Secure Digital Host Controller Interface (SDHCI) + Needed by some Fujitsu SoC for MMC / SD / SDIO support. + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_KONA bool "SDHCI support on Broadcom KONA platform" depends on MMC_SDHCI diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index d96ac90719..dde6cd563f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_MMC_UNIPHIER) += tmio-common.o uniphier-sd.o obj-$(CONFIG_RENESAS_SDHI) += tmio-common.o renesas-sdhi.o obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o obj-$(CONFIG_MMC_MTK) += mtk-sd.o +obj-$(CONFIG_MMC_SDHCI_F_SDH30) += f_sdh30.o diff --git a/drivers/mmc/f_sdh30.c b/drivers/mmc/f_sdh30.c new file mode 100644 index 0000000000..3a85d9e348 --- /dev/null +++ b/drivers/mmc/f_sdh30.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Socionext F_SDH30 eMMC driver + * Copyright 2021 Linaro Ltd. + * Copyright 2021 Socionext, Inc. + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <malloc.h> +#include <sdhci.h> + +struct f_sdh30_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static int f_sdh30_sdhci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct f_sdh30_plat *plat = dev_get_plat(dev); + struct sdhci_host *host = dev_get_priv(dev); + int ret; + + ret = mmc_of_parse(dev, &plat->cfg); + if (ret) + return ret; + + host->mmc = &plat->mmc; + host->mmc->dev = dev; + host->mmc->priv = host; + + ret = sdhci_setup_cfg(&plat->cfg, host, 200000000, 400000); + if (ret) + return ret; + + upriv->mmc = host->mmc; + + mmc_set_clock(host->mmc, host->mmc->cfg->f_min, MMC_CLK_ENABLE); + + return sdhci_probe(dev); +} + +static int f_sdh30_of_to_plat(struct udevice *dev) +{ + struct sdhci_host *host = dev_get_priv(dev); + + host->name = strdup(dev->name); + host->ioaddr = dev_read_addr_ptr(dev); + host->bus_width = dev_read_u32_default(dev, "bus-width", 4); + host->index = dev_read_u32_default(dev, "index", 0); + + return 0; +} + +static int f_sdh30_bind(struct udevice *dev) +{ + struct f_sdh30_plat *plat = dev_get_plat(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id f_sdh30_mmc_ids[] = { + { .compatible = "fujitsu,mb86s70-sdhci-3.0" }, + { } +}; + +U_BOOT_DRIVER(f_sdh30_drv) = { + .name = "f_sdh30_sdhci", + .id = UCLASS_MMC, + .of_match = f_sdh30_mmc_ids, + .of_to_plat = f_sdh30_of_to_plat, + .ops = &sdhci_ops, + .bind = f_sdh30_bind, + .probe = f_sdh30_sdhci_probe, + .priv_auto = sizeof(struct sdhci_host), + .plat_auto = sizeof(struct f_sdh30_plat), +}; diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b2b7b253f8..782179eb0f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -81,6 +81,18 @@ config PCIE_ECAM_GENERIC Say Y here if you want to enable support for generic ECAM-based PCIe host controllers, such as the one emulated by QEMU. +config PCIE_ECAM_SYNQUACER + bool "SynQuacer ECAM-based PCI host controller support" + default n + depends on DM_PCI + select PCI_INIT_R + select PCI_REGION_MULTI_ENTRY + help + Say Y here if you want to enable support for Socionext + SynQuacer SoC's ECAM-based PCIe host controllers. + Note that this must be configured when boot because Linux driver + expects the PCIe RC has been configured in the bootloader. + config PCI_PHYTIUM bool "Phytium PCIe support" depends on DM_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c742bb2c94..6568dc9a08 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -16,6 +16,7 @@ endif obj-$(CONFIG_PCI) += pci_auto_common.o pci_common.o obj-$(CONFIG_PCIE_ECAM_GENERIC) += pcie_ecam_generic.o +obj-$(CONFIG_PCIE_ECAM_SYNQUACER) += pcie_ecam_synquacer.o obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 22a033e632..afe4f1974a 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -550,6 +550,9 @@ int pci_auto_config_devices(struct udevice *bus) max_bus = ret; sub_bus = max(sub_bus, max_bus); + if (dev_get_parent(dev) == bus) + continue; + pplat = dev_get_parent_plat(dev); if (pplat->class == (PCI_CLASS_DISPLAY_VGA << 8)) set_vga_bridge_bits(dev); diff --git a/drivers/pci/pcie_ecam_synquacer.c b/drivers/pci/pcie_ecam_synquacer.c new file mode 100644 index 0000000000..c6e7c59f8a --- /dev/null +++ b/drivers/pci/pcie_ecam_synquacer.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SynQuacer PCIE host driver + * + * Based on drivers/pci/pcie_ecam_generic.c + * + * Copyright (C) 2016 Imagination Technologies + * Copyright (C) 2021 Linaro Ltd. + */ + +#include <common.h> +#include <dm.h> +#include <pci.h> +#include <log.h> + +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/delay.h> + +/* iATU registers */ +#define IATU_VIEWPORT_OFF 0x900 +#define IATU_VIEWPORT_INBOUND BIT(31) +#define IATU_VIEWPORT_OUTBOUND 0 +#define IATU_VIEWPORT_REGION_INDEX(idx) ((idx) & 7) + +#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0 0x904 +#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM 0x0 +#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_IO 0x2 +#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG0 0x4 +#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG1 0x5 +#define IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TH BIT(12) + +#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0 0x908 +#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_REGION_EN BIT(31) +#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_CFG_SHIFT_MODE BIT(28) +#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_32BIT 0xF +#define IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_64BIT 0xFF + +#define IATU_LWR_BASE_ADDR_OFF_OUTBOUND_0 0x90C +#define IATU_UPPER_BASE_ADDR_OFF_OUTBOUND_0 0x910 +#define IATU_LIMIT_ADDR_OFF_OUTBOUND_0 0x914 +#define IATU_LWR_TARGET_ADDR_OFF_OUTBOUND_0 0x918 +#define IATU_UPPER_TARGET_ADDR_OFF_OUTBOUND_0 0x91C + +/* Clock and resets */ +#define CORE_CONTROL 0x000 +#define APP_LTSSM_ENABLE BIT(4) +#define DEVICE_TYPE (BIT(3) | BIT(2) | BIT(1) | BIT(0)) + +#define AXI_CLK_STOP 0x004 +#define DBI_ACLK_STOP BIT(8) +#define SLV_ACLK_STOP BIT(4) +#define MSTR_ACLK_STOP BIT(0) +#define DBI_CSYSREQ_REG BIT(9) +#define SLV_CSYSREQ_REG BIT(5) +#define MSTR_CSYSREQ_REG BIT(1) + +#define RESET_CONTROL_1 0x00C +#define PERST_N_O_REG BIT(5) +#define PERST_N_I_REG BIT(4) +#define BUTTON_RST_N_REG BIT(1) +#define PWUP_RST_N_REG BIT(0) + +#define RESET_CONTROL_2 0x010 + +#define RESET_SELECT_1 0x014 +#define SQU_RST_SEL BIT(29) +#define PHY_RST_SEL BIT(28) +#define PWR_RST_SEL BIT(24) +#define STI_RST_SEL BIT(20) +#define N_STI_RST_SEL BIT(16) +#define CORE_RST_SEL BIT(12) +#define PERST_SEL BIT(4) +#define BUTTON_RST_SEL BIT(1) +#define PWUP_RST_SEL BIT(0) + +#define RESET_SELECT_2 0x018 +#define DBI_ARST_SEL BIT(8) +#define SLV_ARST_SEL BIT(4) +#define MSTR_ARST_SEL BIT(0) + +#define EM_CONTROL 0x030 +#define PRE_DET_STT_REG BIT(4) + +#define EM_SELECT 0x034 +#define PRE_DET_STT_SEL BIT(4) + +#define PM_CONTROL_2 0x050 +#define SYS_AUX_PWR_DET BIT(8) + +#define PHY_CONFIG_COM_6 0x114 +#define PIPE_PORT_SEL GENMASK(1, 0) + +#define LINK_MONITOR 0x210 +#define SMLH_LINK_UP BIT(0) + +#define LINK_CAPABILITIES_REG 0x07C +#define PCIE_CAP_MAX_LINK_WIDTH GENMASK(7, 4) +#define PCIE_CAP_MAX_LINK_SPEED GENMASK(3, 0) + +#define LINK_CONTROL_LINK_STATUS_REG 0x080 +#define PCIE_CAP_NEGO_LINK_WIDTH GENMASK(23, 20) +#define PCIE_CAP_LINK_SPEED GENMASK(19, 16) + +#define TYPE1_CLASS_CODE_REV_ID_REG 0x008 +#define BASE_CLASS_CODE 0xFF000000 +#define BASE_CLASS_CODE_VALUE 0x06 +#define SUBCLASS_CODE 0x00FF0000 +#define SUBCLASS_CODE_VALUE 0x04 +#define PROGRAM_INTERFACE 0x0000FF00 +#define PROGRAM_INTERFACE_VALUE 0x00 + +#define GEN2_CONTROL_OFF 0x80c +#define DIRECT_SPEED_CHANGE BIT(17) + +#define MISC_CONTROL_1_OFF 0x8BC +#define DBI_RO_WR_EN BIT(0) + +static void or_writel(void *base, u32 offs, u32 val) +{ + writel(readl(base + offs) | val, base + offs); +} + +static void masked_writel(void *base, u32 offs, u32 mask, u32 val) +{ + u32 data; + int shift = ffs(mask); /* Note that ffs() returns 1 for 0x1 */ + + if (val && shift > 1) + val <<= shift - 1; + + if (mask != ~0) + data = (readl(base + offs) & ~mask) | val; + else + data = val; + + writel(data, base + offs); +} + +static u32 masked_readl(void *base, u32 offs, u32 mask) +{ + u32 data; + int shift = ffs(mask); /* Note that ffs() returns 1 for 0x1 */ + + data = readl(base + offs); + + if (mask != ~0) + data &= mask; + if (shift > 1) + data >>= shift - 1; + + return data; +} + +/* + * Since SynQuacer's PCIe RC is expected to be initialized in the + * firmware (including U-Boot), devicetree doesn't have control + * blocks. + * + * Thus, this will initialize the PCIe RC with fixed addresses. + */ + +#define SYNQUACER_PCI_SEG0_CONFIG_BASE 0x60000000 +#define SYNQUACER_PCI_SEG0_CONFIG_SIZE 0x07f00000 +#define SYNQUACER_PCI_SEG0_DBI_BASE 0x583d0000 +#define SYNQUACER_PCI_SEG0_EXS_BASE 0x58390000 + +#define SYNQUACER_PCI_SEG1_CONFIG_BASE 0x70000000 +#define SYNQUACER_PCI_SEG1_CONFIG_SIZE 0x07f00000 +#define SYNQUACER_PCI_SEG1_DBI_BASE 0x583c0000 +#define SYNQUACER_PCI_SEG1_EXS_BASE 0x58380000 + +#define SIZE_16KB 0x00004000 +#define SIZE_64KB 0x00010000 +#define SIZE_1MB 0x00100000 + +#define SYNQUACER_PCI_DBI_SIZE SIZE_16KB +#define SYNQUACER_PCI_EXS_SIZE SIZE_64KB + +#define NUM_SQ_PCI_RC 2 + +static const struct synquacer_pcie_base { + phys_addr_t cfg_base; + phys_addr_t dbi_base; + phys_addr_t exs_base; +} synquacer_pci_bases[NUM_SQ_PCI_RC] = { + { + .cfg_base = SYNQUACER_PCI_SEG0_CONFIG_BASE, + .dbi_base = SYNQUACER_PCI_SEG0_DBI_BASE, + .exs_base = SYNQUACER_PCI_SEG0_EXS_BASE, + }, { + .cfg_base = SYNQUACER_PCI_SEG1_CONFIG_BASE, + .dbi_base = SYNQUACER_PCI_SEG1_DBI_BASE, + .exs_base = SYNQUACER_PCI_SEG1_EXS_BASE, + }, +}; + +/** + * struct synquacer_ecam_pcie - synquacer_ecam PCIe controller state + * @cfg_base: The base address of memory mapped configuration space + */ +struct synquacer_ecam_pcie { + void *cfg_base; + pci_size_t size; + void *dbi_base; + void *exs_base; + int first_busno; + + struct pci_region mem; + struct pci_region io; + struct pci_region mem64; +}; + +DECLARE_GLOBAL_DATA_PTR; + +/** + * pci_synquacer_ecam_conf_address() - Calculate the address of a config access + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @paddress: Pointer to the pointer to write the calculates address to + * + * Calculates the address that should be accessed to perform a PCIe + * configuration space access for a given device identified by the PCIe + * controller device @pcie and the bus, device & function numbers in @bdf. If + * access to the device is not valid then the function will return an error + * code. Otherwise the address to access will be written to the pointer pointed + * to by @paddress. + */ +static int pci_synquacer_ecam_conf_address(const struct udevice *bus, + pci_dev_t bdf, uint offset, + void **paddress) +{ + struct synquacer_ecam_pcie *pcie = dev_get_priv(bus); + void *addr; + + addr = pcie->cfg_base; + addr += (PCI_BUS(bdf) - pcie->first_busno) << 20; + addr += PCI_DEV(bdf) << 15; + addr += PCI_FUNC(bdf) << 12; + addr += offset; + *paddress = addr; + + return 0; +} + +static bool pci_synquacer_ecam_addr_valid(const struct udevice *bus, + pci_dev_t bdf) +{ + struct synquacer_ecam_pcie *pcie = dev_get_priv(bus); + int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16); + + /* + * The Synopsys DesignWare PCIe controller in ECAM mode will not filter + * type 0 config TLPs sent to devices 1 and up on its downstream port, + * resulting in devices appearing multiple times on bus 0 unless we + * filter out those accesses here. + */ + if (PCI_BUS(bdf) == pcie->first_busno && PCI_DEV(bdf) > 0) + return false; + + return (PCI_BUS(bdf) >= pcie->first_busno && + PCI_BUS(bdf) < pcie->first_busno + num_buses); +} + +/** + * pci_synquacer_ecam_read_config() - Read from configuration space + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @valuep: A pointer at which to store the read value + * @size: Indicates the size of access to perform + * + * Read a value of size @size from offset @offset within the configuration + * space of the device identified by the bus, device & function numbers in @bdf + * on the PCI bus @bus. + */ +static int pci_synquacer_ecam_read_config(const struct udevice *bus, + pci_dev_t bdf, uint offset, + ulong *valuep, enum pci_size_t size) +{ + if (!pci_synquacer_ecam_addr_valid(bus, bdf)) { + *valuep = pci_get_ff(size); + return 0; + } + + return pci_generic_mmap_read_config(bus, pci_synquacer_ecam_conf_address, + bdf, offset, valuep, size); +} + +/** + * pci_synquacer_ecam_write_config() - Write to configuration space + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @value: The value to write + * @size: Indicates the size of access to perform + * + * Write the value @value of size @size from offset @offset within the + * configuration space of the device identified by the bus, device & function + * numbers in @bdf on the PCI bus @bus. + */ +static int pci_synquacer_ecam_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + if (!pci_synquacer_ecam_addr_valid(bus, bdf)) + return 0; + + return pci_generic_mmap_write_config(bus, pci_synquacer_ecam_conf_address, + bdf, offset, value, size); +} + +/** + * pci_synquacer_ecam_of_to_plat() - Translate from DT to device state + * @dev: A pointer to the device being operated on + * + * Translate relevant data from the device tree pertaining to device @dev into + * state that the driver will later make use of. This state is stored in the + * device's private data structure. + * + * Return: 0 on success, else -EINVAL + */ +static int pci_synquacer_ecam_of_to_plat(struct udevice *dev) +{ + struct synquacer_ecam_pcie *pcie = dev_get_priv(dev); + struct fdt_resource reg_res; + int i, err; + + debug("%s: called for %s\n", __func__, dev->name); + + err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", + 0, ®_res); + if (err < 0) { + pr_err("\"reg\" resource not found\n"); + return err; + } + + /* Find the correct pair of the DBI/EXS base address */ + for (i = 0; i < NUM_SQ_PCI_RC; i++) { + if (synquacer_pci_bases[i].cfg_base == reg_res.start) + break; + } + if (i == NUM_SQ_PCI_RC) { + pr_err("Unknown ECAM base address %lx.\n", + (unsigned long)reg_res.start); + return -ENOENT; + } + pcie->dbi_base = map_physmem(synquacer_pci_bases[i].dbi_base, + SYNQUACER_PCI_DBI_SIZE, MAP_NOCACHE); + if (!pcie->dbi_base) { + pr_err("Failed to map DBI for %s\n", dev->name); + return -ENOMEM; + } + + pcie->exs_base = map_physmem(synquacer_pci_bases[i].exs_base, + SYNQUACER_PCI_EXS_SIZE, MAP_NOCACHE); + if (!pcie->exs_base) { + pr_err("Failed to map EXS for %s\n", dev->name); + return -ENOMEM; + } + + pcie->size = fdt_resource_size(®_res); + pcie->cfg_base = map_physmem(reg_res.start, pcie->size, MAP_NOCACHE); + if (!pcie->cfg_base) { + pr_err("Failed to map config space for %s\n", dev->name); + return -ENOMEM; + } + debug("mappings DBI: %p EXS: %p CFG: %p\n", pcie->dbi_base, pcie->exs_base, pcie->cfg_base); + + return 0; +} + +static void pci_synquacer_pre_init(struct synquacer_ecam_pcie *pcie) +{ + void *base = pcie->exs_base; + + masked_writel(base, EM_SELECT, PRE_DET_STT_SEL, 0); + masked_writel(base, EM_CONTROL, PRE_DET_STT_REG, 0); + masked_writel(base, EM_CONTROL, PRE_DET_STT_REG, 1); + + /* 1: Assert all PHY / LINK resets */ + masked_writel(base, RESET_SELECT_1, PERST_SEL, 0); + masked_writel(base, RESET_CONTROL_1, PERST_N_I_REG, 0); + masked_writel(base, RESET_CONTROL_1, PERST_N_O_REG, 0); + + /* Device Reset(PERST#) is effective afrer Set device_type (RC) */ + masked_writel(base, RESET_SELECT_1, PWUP_RST_SEL, 0); + masked_writel(base, RESET_CONTROL_1, PWUP_RST_N_REG, 0); + masked_writel(base, RESET_SELECT_1, BUTTON_RST_SEL, 0); + masked_writel(base, RESET_CONTROL_1, BUTTON_RST_N_REG, 0); + masked_writel(base, RESET_SELECT_1, PWR_RST_SEL, 1); + masked_writel(base, RESET_SELECT_2, MSTR_ARST_SEL, 1); + masked_writel(base, RESET_SELECT_2, SLV_ARST_SEL, 1); + masked_writel(base, RESET_SELECT_2, DBI_ARST_SEL, 1); + masked_writel(base, RESET_SELECT_1, CORE_RST_SEL, 1); + masked_writel(base, RESET_SELECT_1, STI_RST_SEL, 1); + masked_writel(base, RESET_SELECT_1, N_STI_RST_SEL, 1); + masked_writel(base, RESET_SELECT_1, SQU_RST_SEL, 1); + masked_writel(base, RESET_SELECT_1, PHY_RST_SEL, 1); + + /* 2: Set P<n>_app_ltssm_enable='0' for reprogramming before linkup. */ + masked_writel(base, CORE_CONTROL, APP_LTSSM_ENABLE, 0); + + /* 3: Set device_type (RC) */ + masked_writel(base, CORE_CONTROL, DEVICE_TYPE, 4); +} + +static void pci_synquacer_dbi_init(void *dbi_base) +{ + masked_writel(dbi_base, MISC_CONTROL_1_OFF, DBI_RO_WR_EN, 1); + /* 4 Lanes */ + masked_writel(dbi_base, LINK_CAPABILITIES_REG, + PCIE_CAP_MAX_LINK_WIDTH, 4); + /* Gen 2 */ + masked_writel(dbi_base, LINK_CAPABILITIES_REG, + PCIE_CAP_MAX_LINK_SPEED, 2); + + masked_writel(dbi_base, TYPE1_CLASS_CODE_REV_ID_REG, + BASE_CLASS_CODE, BASE_CLASS_CODE_VALUE); + masked_writel(dbi_base, TYPE1_CLASS_CODE_REV_ID_REG, + SUBCLASS_CODE, SUBCLASS_CODE_VALUE); + masked_writel(dbi_base, TYPE1_CLASS_CODE_REV_ID_REG, + PROGRAM_INTERFACE, PROGRAM_INTERFACE_VALUE); + + masked_writel(dbi_base, MISC_CONTROL_1_OFF, DBI_RO_WR_EN, 0); +} + +static void pcie_sq_prog_outbound_atu(void *dbi_base, int index, + u64 cpu_base, u64 pci_base, u64 size, + u32 type, u32 flags) +{ + debug("%s: %p, %d, %llx, %llx, %llx, %x, %x\n", __func__, + dbi_base, index, cpu_base, pci_base, size, type, flags); + + writel(IATU_VIEWPORT_OUTBOUND | IATU_VIEWPORT_REGION_INDEX(index), + dbi_base + IATU_VIEWPORT_OFF); + + writel((u32)(cpu_base & 0xffffffff), + dbi_base + IATU_LWR_BASE_ADDR_OFF_OUTBOUND_0); + writel((u32)(cpu_base >> 32), + dbi_base + IATU_UPPER_BASE_ADDR_OFF_OUTBOUND_0); + writel((u32)(cpu_base + size - 1), + dbi_base + IATU_LIMIT_ADDR_OFF_OUTBOUND_0); + + writel((u32)(pci_base & 0xffffffff), + dbi_base + IATU_LWR_TARGET_ADDR_OFF_OUTBOUND_0); + writel((u32)(pci_base >> 32), + dbi_base + IATU_UPPER_TARGET_ADDR_OFF_OUTBOUND_0); + + writel(type, dbi_base + IATU_REGION_CTRL_1_OFF_OUTBOUND_0); + writel(IATU_REGION_CTRL_2_OFF_OUTBOUND_0_REGION_EN | flags, + dbi_base + IATU_REGION_CTRL_2_OFF_OUTBOUND_0); +} + +static void pci_synquacer_post_init(struct synquacer_ecam_pcie *pcie) +{ + void *base = pcie->exs_base; + + /* + * 4: Set Bifurcation 1=disable 4=able + * 5: Supply Reference (It has executed) + * 6: Wait for 10usec (Reference Clocks is stable) + * 7: De assert PERST# + */ + masked_writel(base, RESET_CONTROL_1, PERST_N_I_REG, 1); + masked_writel(base, RESET_CONTROL_1, PERST_N_O_REG, 1); + + /* 8: Assert SYS_AUX_PWR_DET */ + masked_writel(base, PM_CONTROL_2, SYS_AUX_PWR_DET, 1); + + /* 9: Supply following clocks */ + masked_writel(base, AXI_CLK_STOP, MSTR_CSYSREQ_REG, 1); + masked_writel(base, AXI_CLK_STOP, MSTR_ACLK_STOP, 0); + masked_writel(base, AXI_CLK_STOP, SLV_CSYSREQ_REG, 1); + masked_writel(base, AXI_CLK_STOP, SLV_ACLK_STOP, 0); + masked_writel(base, AXI_CLK_STOP, DBI_CSYSREQ_REG, 1); + masked_writel(base, AXI_CLK_STOP, DBI_ACLK_STOP, 0); + + /* + * 10: De assert PHY reset + * 11: De assert LINK's PMC reset + */ + masked_writel(base, RESET_CONTROL_1, PWUP_RST_N_REG, 1); + masked_writel(base, RESET_CONTROL_1, BUTTON_RST_N_REG, 1); + + /* 12: PHY auto + * 13: Wrapper auto + * 14-17: PHY auto + * 18: Wrapper auto + * 19: Update registers through DBI AXI Slave interface + */ + pci_synquacer_dbi_init(pcie->dbi_base); + + or_writel(pcie->dbi_base, PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Force link speed change to Gen2 at link up */ + or_writel(pcie->dbi_base, GEN2_CONTROL_OFF, DIRECT_SPEED_CHANGE); + + /* Region 0: MMIO32 range */ + pcie_sq_prog_outbound_atu(pcie->dbi_base, 0, + pcie->mem.phys_start, + pcie->mem.bus_start, + pcie->mem.size, + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM | + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TH, + IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_32BIT); + + /* Region 1: Type 0 config space */ + pcie_sq_prog_outbound_atu(pcie->dbi_base, 1, + (u64)pcie->cfg_base, + 0, + SIZE_64KB, + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG0, + IATU_REGION_CTRL_2_OFF_OUTBOUND_0_CFG_SHIFT_MODE); + + /* Region 2: Type 1 config space */ + pcie_sq_prog_outbound_atu(pcie->dbi_base, 2, + (u64)pcie->cfg_base + SIZE_64KB, + 0, + (u64)pcie->io.phys_start - (u64)pcie->cfg_base - SIZE_64KB, + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_CFG1, + IATU_REGION_CTRL_2_OFF_OUTBOUND_0_CFG_SHIFT_MODE); + + /* Region 3: port I/O range */ + pcie_sq_prog_outbound_atu(pcie->dbi_base, 3, + pcie->io.phys_start, + pcie->io.bus_start, + pcie->io.size, + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_IO, + 0); + + /* Region 4: MMIO64 range */ + pcie_sq_prog_outbound_atu(pcie->dbi_base, 4, + pcie->mem64.phys_start, + pcie->mem64.bus_start, + pcie->mem64.size, + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TYPE_MEM | + IATU_REGION_CTRL_1_OFF_OUTBOUND_0_TH, + IATU_REGION_CTRL_2_OFF_OUTBOUND_0_MSG_CODE_32BIT); + + /* enable link */ + if (masked_readl(base, CORE_CONTROL, APP_LTSSM_ENABLE) == 0) + masked_writel(base, CORE_CONTROL, APP_LTSSM_ENABLE, 1); +} + +static int pci_synquacer_ecam_probe(struct udevice *dev) +{ + struct synquacer_ecam_pcie *pcie = dev_get_priv(dev); + struct udevice *ctlr = pci_get_controller(dev); + struct pci_controller *hose = dev_get_uclass_priv(ctlr); + + debug("Probe synquacer pcie for bus %d\n", dev_seq(dev)); + pcie->first_busno = dev_seq(dev); + + /* Store the IO and MEM windows settings for configuring ATU */ + pcie->io.phys_start = hose->regions[0].phys_start; /* IO base */ + pcie->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */ + pcie->io.size = hose->regions[0].size; /* IO size */ + + pcie->mem.phys_start = hose->regions[1].phys_start; /* MEM base */ + pcie->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */ + pcie->mem.size = hose->regions[1].size; /* MEM size */ + + pcie->mem64.phys_start = hose->regions[2].phys_start; /* MEM64 base */ + pcie->mem64.bus_start = hose->regions[2].bus_start; /* MEM64_bus_addr */ + pcie->mem64.size = hose->regions[2].size; /* MEM64 size */ + + pci_synquacer_pre_init(pcie); + + mdelay(150); + + pci_synquacer_post_init(pcie); + + /* It takes a while to stabilize the PCIe bus for scanning */ + mdelay(100); + + return 0; +} + +static const struct dm_pci_ops pci_synquacer_ecam_ops = { + .read_config = pci_synquacer_ecam_read_config, + .write_config = pci_synquacer_ecam_write_config, +}; + +static const struct udevice_id pci_synquacer_ecam_ids[] = { + { .compatible = "socionext,synquacer-pcie-ecam" }, + { } +}; + +U_BOOT_DRIVER(pci_synquacer_ecam) = { + .name = "pci_synquacer_ecam", + .id = UCLASS_PCI, + .of_match = pci_synquacer_ecam_ids, + .ops = &pci_synquacer_ecam_ops, + .probe = pci_synquacer_ecam_probe, + .of_to_plat = pci_synquacer_ecam_of_to_plat, + .priv_auto = sizeof(struct synquacer_ecam_pcie), +}; diff --git a/drivers/serial/serial_msm.c b/drivers/serial/serial_msm.c index d8c6c2f6b5..d8dd5c1104 100644 --- a/drivers/serial/serial_msm.c +++ b/drivers/serial/serial_msm.c @@ -23,6 +23,7 @@ /* Serial registers - this driver works in uartdm mode*/ #define UARTDM_DMRX 0x34 /* Max RX transfer length */ +#define UARTDM_DMEN 0x3C /* DMA/data-packing mode */ #define UARTDM_NCF_TX 0x40 /* Number of chars to TX */ #define UARTDM_RXFS 0x50 /* RX channel status register */ @@ -197,6 +198,9 @@ static void uart_dm_init(struct msm_serial_data *priv) writel(MSM_BOOT_UART_DM_8_N_1_MODE, priv->base + UARTDM_MR2); writel(MSM_BOOT_UART_DM_CMD_RESET_RX, priv->base + UARTDM_CR); writel(MSM_BOOT_UART_DM_CMD_RESET_TX, priv->base + UARTDM_CR); + + /* Make sure BAM/single character mode is disabled */ + writel(0x0, priv->base + UARTDM_DMEN); } static int msm_serial_probe(struct udevice *dev) { diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e317d8a2c6..5c2a60a214 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -488,4 +488,12 @@ config MXC_SPI Enable the MXC SPI controller driver. This driver can be used on various i.MX SoCs such as i.MX31/35/51/6/7. +config SYNQUACER_SPI + bool "Socionext SynQuacer HS-SPI driver" + depends on ARCH_SYNQUACER + help + Enable the Socionext HS-SPI driver for SynQuacer. This driver can + be used to access the SPI interface and SPI NOR flash on platforms + embedding this HS-SPI IP core. + endif # menu "SPI Support" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3dc83089b8..f70851e4bc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o obj-$(CONFIG_FSL_ESPI) += fsl_espi.o obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o +obj-$(CONFIG_SYNQUACER_SPI) += spi-synquacer.o obj-$(CONFIG_ICH_SPI) += ich.o obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c new file mode 100644 index 0000000000..ce558c4bc0 --- /dev/null +++ b/drivers/spi/spi-synquacer.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * spi-synquacer.c - Socionext Synquacer SPI driver + * Copyright 2021 Linaro Ltd. + * Copyright 2021 Socionext, Inc. + */ + +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <log.h> +#include <time.h> +#include <dm/device_compat.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <spi.h> +#include <wait_bit.h> + +#define MCTRL 0x0 +#define MEN 0 +#define CSEN 1 +#define IPCLK 3 +#define MES 4 +#define SYNCON 5 + +#define PCC0 0x4 +#define PCC(n) (PCC0 + (n) * 4) +#define RTM 3 +#define ACES 2 +#define SAFESYNC 16 +#define CPHA 0 +#define CPOL 1 +#define SSPOL 4 +#define SDIR 7 +#define SS2CD 5 +#define SENDIAN 8 +#define CDRS_SHIFT 9 +#define CDRS_MASK 0x7f + +#define TXF 0x14 +#define TXE 0x18 +#define TXC 0x1c +#define RXF 0x20 +#define RXE 0x24 +#define RXC 0x28 +#define TFLETE 4 +#define RFMTE 5 + +#define FAULTF 0x2c +#define FAULTC 0x30 + +#define DMCFG 0x34 +#define SSDC 1 +#define MSTARTEN 2 + +#define DMSTART 0x38 +#define TRIGGER 0 +#define DMSTOP 8 +#define CS_MASK 3 +#define CS_SHIFT 16 +#define DATA_TXRX 0 +#define DATA_RX 1 +#define DATA_TX 2 +#define DATA_MASK 3 +#define DATA_SHIFT 26 +#define BUS_WIDTH 24 + +#define DMBCC 0x3c +#define DMSTATUS 0x40 +#define RX_DATA_MASK 0x1f +#define RX_DATA_SHIFT 8 +#define TX_DATA_MASK 0x1f +#define TX_DATA_SHIFT 16 + +#define TXBITCNT 0x44 + +#define FIFOCFG 0x4c +#define BPW_MASK 0x3 +#define BPW_SHIFT 8 +#define RX_FLUSH 11 +#define TX_FLUSH 12 +#define RX_TRSHLD_MASK 0xf +#define RX_TRSHLD_SHIFT 0 +#define TX_TRSHLD_MASK 0xf +#define TX_TRSHLD_SHIFT 4 + +#define TXFIFO 0x50 +#define RXFIFO 0x90 +#define MID 0xfc + +#define FIFO_DEPTH 16 +#define TX_TRSHLD 4 +#define RX_TRSHLD (FIFO_DEPTH - TX_TRSHLD) + +#define TXBIT 1 +#define RXBIT 2 + +DECLARE_GLOBAL_DATA_PTR; + +struct synquacer_spi_plat { + void __iomem *base; + bool aces, rtm; +}; + +struct synquacer_spi_priv { + void __iomem *base; + bool aces, rtm; + int speed, cs, mode, rwflag; + void *rx_buf; + const void *tx_buf; + unsigned int tx_words, rx_words; +}; + +static void read_fifo(struct synquacer_spi_priv *priv) +{ + u32 len = readl(priv->base + DMSTATUS); + u8 *buf = priv->rx_buf; + int i; + + len = (len >> RX_DATA_SHIFT) & RX_DATA_MASK; + len = min_t(unsigned int, len, priv->rx_words); + + for (i = 0; i < len; i++) + *buf++ = readb(priv->base + RXFIFO); + + priv->rx_buf = buf; + priv->rx_words -= len; +} + +static void write_fifo(struct synquacer_spi_priv *priv) +{ + u32 len = readl(priv->base + DMSTATUS); + const u8 *buf = priv->tx_buf; + int i; + + len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK; + len = min_t(unsigned int, FIFO_DEPTH - len, priv->tx_words); + + for (i = 0; i < len; i++) + writeb(*buf++, priv->base + TXFIFO); + + priv->tx_buf = buf; + priv->tx_words -= len; +} + +static void synquacer_cs_set(struct synquacer_spi_priv *priv, bool active) +{ + u32 val; + + val = readl(priv->base + DMSTART); + val &= ~(CS_MASK << CS_SHIFT); + val |= priv->cs << CS_SHIFT; + + if (active) { + writel(val, priv->base + DMSTART); + + val = readl(priv->base + DMSTART); + val &= ~BIT(DMSTOP); + writel(val, priv->base + DMSTART); + } else { + val |= BIT(DMSTOP); + writel(val, priv->base + DMSTART); + + if (priv->rx_buf) { + u32 buf[16]; + + priv->rx_buf = buf; + priv->rx_words = 16; + read_fifo(priv); + } + } +} + +static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx) +{ + struct udevice *bus = dev->parent; + struct synquacer_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); + u32 val, div, bus_width; + int rwflag; + + rwflag = (rx ? 1 : 0) | (tx ? 2 : 0); + + /* if nothing to do */ + if (slave_plat->mode == priv->mode && + rwflag == priv->rwflag && + slave_plat->cs == priv->cs && + slave_plat->max_hz == priv->speed) + return; + + priv->rwflag = rwflag; + priv->cs = slave_plat->cs; + priv->mode = slave_plat->mode; + priv->speed = slave_plat->max_hz; + + if (priv->mode & SPI_TX_BYTE) + bus_width = 1; + else if (priv->mode & SPI_TX_DUAL) + bus_width = 2; + else if (priv->mode & SPI_TX_QUAD) + bus_width = 4; + else if (priv->mode & SPI_TX_OCTAL) + bus_width = 8; + + div = DIV_ROUND_UP(125000000, priv->speed); + + val = readl(priv->base + PCC(priv->cs)); + val &= ~BIT(RTM); + val &= ~BIT(ACES); + val &= ~BIT(SAFESYNC); + if ((priv->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3) + val |= BIT(SAFESYNC); + if ((priv->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6) + val |= BIT(SAFESYNC); + + if (priv->mode & SPI_CPHA) + val |= BIT(CPHA); + else + val &= ~BIT(CPHA); + + if (priv->mode & SPI_CPOL) + val |= BIT(CPOL); + else + val &= ~BIT(CPOL); + + if (priv->mode & SPI_CS_HIGH) + val |= BIT(SSPOL); + else + val &= ~BIT(SSPOL); + + if (priv->mode & SPI_LSB_FIRST) + val |= BIT(SDIR); + else + val &= ~BIT(SDIR); + + if (priv->aces) + val |= BIT(ACES); + + if (priv->rtm) + val |= BIT(RTM); + + val |= (3 << SS2CD); + val |= BIT(SENDIAN); + + val &= ~(CDRS_MASK << CDRS_SHIFT); + val |= ((div >> 1) << CDRS_SHIFT); + + writel(val, priv->base + PCC(priv->cs)); + + val = readl(priv->base + FIFOCFG); + val &= ~(BPW_MASK << BPW_SHIFT); + val |= (0 << BPW_SHIFT); + writel(val, priv->base + FIFOCFG); + + val = readl(priv->base + DMSTART); + val &= ~(DATA_MASK << DATA_SHIFT); + + if (tx && rx) + val |= (DATA_TXRX << DATA_SHIFT); + else if (rx) + val |= (DATA_RX << DATA_SHIFT); + else + val |= (DATA_TX << DATA_SHIFT); + + val &= ~(3 << BUS_WIDTH); + val |= ((bus_width >> 1) << BUS_WIDTH); + writel(val, priv->base + DMSTART); +} + +static int synquacer_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *tx_buf, void *rx_buf, + unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct synquacer_spi_priv *priv = dev_get_priv(bus); + u32 val, words, busy; + + val = readl(priv->base + FIFOCFG); + val |= (1 << RX_FLUSH); + val |= (1 << TX_FLUSH); + writel(val, priv->base + FIFOCFG); + + synquacer_spi_config(dev, rx_buf, tx_buf); + + priv->tx_buf = tx_buf; + priv->rx_buf = rx_buf; + + words = bitlen / 8; + + if (tx_buf) { + busy |= BIT(TXBIT); + priv->tx_words = words; + } else { + busy &= ~BIT(TXBIT); + priv->tx_words = 0; + } + + if (rx_buf) { + busy |= BIT(RXBIT); + priv->rx_words = words; + } else { + busy &= ~BIT(RXBIT); + priv->rx_words = 0; + } + + if (flags & SPI_XFER_BEGIN) + synquacer_cs_set(priv, true); + + if (tx_buf) + write_fifo(priv); + + if (rx_buf) { + val = readl(priv->base + FIFOCFG); + val &= ~(RX_TRSHLD_MASK << RX_TRSHLD_SHIFT); + val |= ((priv->rx_words > FIFO_DEPTH ? + RX_TRSHLD : priv->rx_words) << RX_TRSHLD_SHIFT); + writel(val, priv->base + FIFOCFG); + } + + writel(~0, priv->base + TXC); + writel(~0, priv->base + RXC); + + /* Trigger */ + val = readl(priv->base + DMSTART); + val |= BIT(TRIGGER); + writel(val, priv->base + DMSTART); + + while (busy & (BIT(RXBIT) | BIT(TXBIT))) { + if (priv->rx_words) + read_fifo(priv); + else + busy &= ~BIT(RXBIT); + + if (priv->tx_words) { + write_fifo(priv); + } else { + u32 len; + + do { /* wait for shifter to empty out */ + cpu_relax(); + len = readl(priv->base + DMSTATUS); + len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK; + } while (tx_buf && len); + busy &= ~BIT(TXBIT); + } + } + + if (flags & SPI_XFER_END) + synquacer_cs_set(priv, false); + + return 0; +} + +static int synquacer_spi_set_speed(struct udevice *bus, uint speed) +{ + return 0; +} + +static int synquacer_spi_set_mode(struct udevice *bus, uint mode) +{ + return 0; +} + +static int synquacer_spi_claim_bus(struct udevice *dev) +{ + return 0; +} + +static int synquacer_spi_release_bus(struct udevice *dev) +{ + return 0; +} + +static void synquacer_spi_disable_module(struct synquacer_spi_priv *priv) +{ + writel(0, priv->base + MCTRL); + while (readl(priv->base + MCTRL) & BIT(MES)) + cpu_relax(); +} + +static void synquacer_spi_init(struct synquacer_spi_priv *priv) +{ + u32 val; + + synquacer_spi_disable_module(priv); + + writel(0, priv->base + TXE); + writel(0, priv->base + RXE); + val = readl(priv->base + TXF); + writel(val, priv->base + TXC); + val = readl(priv->base + RXF); + writel(val, priv->base + RXC); + val = readl(priv->base + FAULTF); + writel(val, priv->base + FAULTC); + + val = readl(priv->base + DMCFG); + val &= ~BIT(SSDC); + val &= ~BIT(MSTARTEN); + writel(val, priv->base + DMCFG); + + /* Enable module with direct mode */ + val = readl(priv->base + MCTRL); + val &= ~BIT(IPCLK); + val &= ~BIT(CSEN); + val |= BIT(MEN); + val |= BIT(SYNCON); + writel(val, priv->base + MCTRL); +} + +static void synquacer_spi_exit(struct synquacer_spi_priv *priv) +{ + u32 val; + + synquacer_spi_disable_module(priv); + + /* Enable module with command sequence mode */ + val = readl(priv->base + MCTRL); + val &= ~BIT(IPCLK); + val |= BIT(CSEN); + val |= BIT(MEN); + val |= BIT(SYNCON); + writel(val, priv->base + MCTRL); + + while (!(readl(priv->base + MCTRL) & BIT(MES))) + cpu_relax(); +} + +static int synquacer_spi_probe(struct udevice *bus) +{ + struct synquacer_spi_plat *plat = dev_get_plat(bus); + struct synquacer_spi_priv *priv = dev_get_priv(bus); + + priv->base = plat->base; + priv->aces = plat->aces; + priv->rtm = plat->rtm; + + synquacer_spi_init(priv); + return 0; +} + +static int synquacer_spi_remove(struct udevice *bus) +{ + struct synquacer_spi_priv *priv = dev_get_priv(bus); + + synquacer_spi_exit(priv); + return 0; +} + +static int synquacer_spi_of_to_plat(struct udevice *bus) +{ + struct synquacer_spi_plat *plat = dev_get_plat(bus); + struct clk clk; + + plat->base = dev_read_addr_ptr(bus); + + plat->aces = dev_read_bool(bus, "socionext,set-aces"); + plat->rtm = dev_read_bool(bus, "socionext,use-rtm"); + + clk_get_by_name(bus, "iHCLK", &clk); + clk_enable(&clk); + + return 0; +} + +static const struct dm_spi_ops synquacer_spi_ops = { + .claim_bus = synquacer_spi_claim_bus, + .release_bus = synquacer_spi_release_bus, + .xfer = synquacer_spi_xfer, + .set_speed = synquacer_spi_set_speed, + .set_mode = synquacer_spi_set_mode, +}; + +static const struct udevice_id synquacer_spi_ids[] = { + { .compatible = "socionext,synquacer-spi" }, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(synquacer_spi) = { + .name = "synquacer_spi", + .id = UCLASS_SPI, + .of_match = synquacer_spi_ids, + .ops = &synquacer_spi_ops, + .of_to_plat = synquacer_spi_of_to_plat, + .plat_auto = sizeof(struct synquacer_spi_plat), + .priv_auto = sizeof(struct synquacer_spi_priv), + .probe = synquacer_spi_probe, + .flags = DM_FLAG_OS_PREPARE, + .remove = synquacer_spi_remove, +}; |