From ad77009d22866a708b25e596a8b59e207157af71 Mon Sep 17 00:00:00 2001 From: Lukas Funke Date: Fri, 28 Apr 2023 14:38:47 +0200 Subject: spi: pl022: Align compatible property with device tree binding Align the compatible property with the kernel device tree binding [1] by removing the '-spi' suffix. [1] https://www.kernel.org/doc/Documentation/devicetree/bindings/spi/spi-pl022.yaml Signed-off-by: Lukas Funke Signed-off-by: Stefan Herbrechtsmeier Reviewed-by: Jagan Teki --- drivers/spi/pl022_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c index 828eab3d342..2986c4eb5ac 100644 --- a/drivers/spi/pl022_spi.c +++ b/drivers/spi/pl022_spi.c @@ -307,7 +307,7 @@ static int pl022_spi_of_to_plat(struct udevice *bus) } static const struct udevice_id pl022_spi_ids[] = { - { .compatible = "arm,pl022-spi" }, + { .compatible = "arm,pl022" }, { } }; #endif -- cgit v1.2.3 From 47c32734a67e0493bd4d14c57a520d247fec6cf5 Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Fri, 28 Apr 2023 14:38:48 +0200 Subject: spi: pl022: Rename flush into pl022_spi_flush Rename the flush function into pl022_spi_flush to avoid conflicting types with previous declaration of the function in stdio.h header. Signed-off-by: Stefan Herbrechtsmeier Reviewed-by: Jagan Teki --- drivers/spi/pl022_spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c index 2986c4eb5ac..a6a0c552aa0 100644 --- a/drivers/spi/pl022_spi.c +++ b/drivers/spi/pl022_spi.c @@ -107,7 +107,7 @@ static int pl022_spi_probe(struct udevice *bus) return 0; } -static void flush(struct pl022_spi_slave *ps) +static void pl022_spi_flush(struct pl022_spi_slave *ps) { do { while (readw(ps->base + SSP_SR) & SSP_SR_MASK_RNE) @@ -126,7 +126,7 @@ static int pl022_spi_claim_bus(struct udevice *dev) reg |= SSP_CR1_MASK_SSE; writew(reg, ps->base + SSP_CR1); - flush(ps); + pl022_spi_flush(ps); return 0; } @@ -137,7 +137,7 @@ static int pl022_spi_release_bus(struct udevice *dev) struct pl022_spi_slave *ps = dev_get_priv(bus); u16 reg; - flush(ps); + pl022_spi_flush(ps); /* Disable the SPI hardware */ reg = readw(ps->base + SSP_CR1); -- cgit v1.2.3 From 03612861a722ebd478c63bf055783fe8c3fcb3bd Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Fri, 28 Apr 2023 14:38:49 +0200 Subject: spi: pl022: Remove platform data header Remove the platform data header because its content is only used by the driver. Signed-off-by: Stefan Herbrechtsmeier Reviewed-by: Jagan Teki --- drivers/spi/pl022_spi.c | 8 +++++++- include/dm/platform_data/spi_pl022.h | 21 --------------------- 2 files changed, 7 insertions(+), 22 deletions(-) delete mode 100644 include/dm/platform_data/spi_pl022.h diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c index a6a0c552aa0..9e4caac16fa 100644 --- a/drivers/spi/pl022_spi.c +++ b/drivers/spi/pl022_spi.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -66,6 +66,12 @@ #define SSP_SR_MASK_RFF (0x1 << 3) /* Receive FIFO full */ #define SSP_SR_MASK_BSY (0x1 << 4) /* Busy Flag */ +struct pl022_spi_pdata { + fdt_addr_t addr; + fdt_size_t size; + unsigned int freq; +}; + struct pl022_spi_slave { void *base; unsigned int freq; diff --git a/include/dm/platform_data/spi_pl022.h b/include/dm/platform_data/spi_pl022.h deleted file mode 100644 index 7f74b3cbc5c..00000000000 --- a/include/dm/platform_data/spi_pl022.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2018 - * Quentin Schulz, Bootlin, quentin.schulz@bootlin.com - * - * Structure for use with U_BOOT_DRVINFO for pl022 SPI devices or to use - * in of_to_plat. - */ - -#ifndef __spi_pl022_h -#define __spi_pl022_h - -#include - -struct pl022_spi_pdata { - fdt_addr_t addr; - fdt_size_t size; - unsigned int freq; -}; - -#endif /* __spi_pl022_h */ -- cgit v1.2.3 From 47d9ae54a103980576d2267699d609f431981446 Mon Sep 17 00:00:00 2001 From: Lukas Funke Date: Fri, 28 Apr 2023 14:38:50 +0200 Subject: spi: pl022: Add chip-select gpio support Add support for an optional external chip-select gpio. Signed-off-by: Lukas Funke Signed-off-by: Stefan Herbrechtsmeier Reviewed-by: Jagan Teki --- drivers/spi/pl022_spi.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c index 9e4caac16fa..fc7388b379d 100644 --- a/drivers/spi/pl022_spi.c +++ b/drivers/spi/pl022_spi.c @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include #define SSP_CR0 0x000 @@ -70,6 +72,9 @@ struct pl022_spi_pdata { fdt_addr_t addr; fdt_size_t size; unsigned int freq; +#if CONFIG_IS_ENABLED(DM_GPIO) + struct gpio_desc cs_gpio; +#endif }; struct pl022_spi_slave { @@ -153,6 +158,17 @@ static int pl022_spi_release_bus(struct udevice *dev) return 0; } +static void pl022_spi_set_cs(struct udevice *dev, bool on) +{ +#if CONFIG_IS_ENABLED(DM_GPIO) + struct udevice *bus = dev->parent; + struct pl022_spi_pdata *plat = dev_get_plat(bus); + + if (dm_gpio_is_valid(&plat->cs_gpio)) + dm_gpio_set_value(&plat->cs_gpio, on ? 1 : 0); +#endif +} + static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { @@ -165,7 +181,7 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen, if (bitlen == 0) /* Finish any previously submitted transfers */ - return 0; + goto done; /* * TODO: The controller can do non-multiple-of-8 bit @@ -178,9 +194,13 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen, if (bitlen % 8) { /* Errors always terminate an ongoing transfer */ flags |= SPI_XFER_END; - return -1; + ret = -1; + goto done; } + if (flags & SPI_XFER_BEGIN) + pl022_spi_set_cs(dev, true); + len = bitlen / 8; while (len_tx < len) { @@ -207,6 +227,10 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen, } } +done: + if (flags & SPI_XFER_END) + pl022_spi_set_cs(dev, false); + return ret; } @@ -309,6 +333,13 @@ static int pl022_spi_of_to_plat(struct udevice *bus) plat->freq = clk_get_rate(&clkdev); +#if CONFIG_IS_ENABLED(DM_GPIO) + ret = gpio_request_by_name(bus, "cs-gpios", 0, &plat->cs_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret < 0 && ret != -ENOENT) + return ret; +#endif + return 0; } -- cgit v1.2.3 From 937b49e90abe8dcb095d324dc4d48ae1542b563d Mon Sep 17 00:00:00 2001 From: William Zhang Date: Wed, 7 Jun 2023 16:37:01 -0700 Subject: spi: bcm63xx-hsspi: Make driver depend on BCMBCA arch ARCH_BCMBCA was introduced to cover individual Broadcom broadband SoC for common features and IP blocks. Use this config instead of each chip config as the Kconfig dependency for Broadcom HSSPI driver. Signed-off-by: William Zhang Reviewed-by: Jagan Teki --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 453a5983b2a..7e71c5ae870 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -101,9 +101,9 @@ config ATMEL_SPI config BCM63XX_HSSPI bool "BCM63XX HSSPI driver" - depends on (ARCH_BMIPS || BCM6856 || BCM6858 || BCM63158) + depends on (ARCH_BMIPS || ARCH_BCMBCA) help - Enable the BCM6328 HSSPI driver. This driver can be used to + Enable the BCM63XX HSSPI driver. This driver can be used to access the SPI NOR flash on platforms embedding this Broadcom SPI core. -- cgit v1.2.3 From 27c4d550aaf45a9ffdd6ffe00aee854867943f29 Mon Sep 17 00:00:00 2001 From: William Zhang Date: Wed, 7 Jun 2023 16:37:02 -0700 Subject: spi: bcm63xx-hsspi: Fix multi-bit mode setting Currently the driver always sets the controller to dual data bit mode for both tx and rx data in the profile mode control register even for single data bit transfer. Luckily the opcode is set correctly according to SPI transfer data bit width so it does not actually cause issues. This change fixes the problem by setting tx and rx data bit mode field correctly according to the actual SPI transfer tx and rx data bit width. Fixes: 29cc4368ad4b ("dm: spi: add BCM63xx HSSPI driver") Port from linux patch: Link: https://lore.kernel.org/r/20230209200246.141520-11-william.zhang@broadcom.com Signed-off-by: William Zhang Reviewed-by: Jagan Teki --- drivers/spi/bcm63xx_hsspi.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c index 4d714adc4af..ea34da2a316 100644 --- a/drivers/spi/bcm63xx_hsspi.c +++ b/drivers/spi/bcm63xx_hsspi.c @@ -221,7 +221,7 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, size_t data_bytes = bitlen / 8; size_t step_size = HSSPI_FIFO_SIZE; uint16_t opcode = 0; - uint32_t val; + uint32_t val = SPI_PFL_MODE_FILL_MASK; const uint8_t *tx = dout; uint8_t *rx = din; @@ -240,14 +240,17 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, step_size -= HSSPI_FIFO_OP_SIZE; /* dual mode */ - if ((opcode == HSSPI_FIFO_OP_CODE_R && plat->mode == SPI_RX_DUAL) || - (opcode == HSSPI_FIFO_OP_CODE_W && plat->mode == SPI_TX_DUAL)) + if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) || + (opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) { opcode |= HSSPI_FIFO_OP_MBIT_MASK; - /* profile mode */ - val = SPI_PFL_MODE_FILL_MASK | - SPI_PFL_MODE_MDRDSZ_MASK | - SPI_PFL_MODE_MDWRSZ_MASK; + /* profile mode */ + if (plat->mode & SPI_RX_DUAL) + val |= SPI_PFL_MODE_MDRDSZ_MASK; + if (plat->mode & SPI_TX_DUAL) + val |= SPI_PFL_MODE_MDWRSZ_MASK; + } + if (plat->mode & SPI_3WIRE) val |= SPI_PFL_MODE_3WIRE_MASK; writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs)); -- cgit v1.2.3 From c430e697e70f44a7ae348eb8c493f15284fceb12 Mon Sep 17 00:00:00 2001 From: William Zhang Date: Wed, 7 Jun 2023 16:37:03 -0700 Subject: spi: bcm63xx-hsspi: Add new compatible string support New compatible string brcm,bcmbca-hsspi-v1.0 is introduced based on dts document brcm,bcm63xx-hsspi.yaml. Add it to the driver to support this new binding. Port from linux patch: Link: https://lore.kernel.org/r/20230207065826.285013-6-william.zhang@broadcom.com Signed-off-by: William Zhang Reviewed-by: Jagan Teki --- drivers/spi/bcm63xx_hsspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c index ea34da2a316..0d12c345b1d 100644 --- a/drivers/spi/bcm63xx_hsspi.c +++ b/drivers/spi/bcm63xx_hsspi.c @@ -313,6 +313,7 @@ static const struct dm_spi_ops bcm63xx_hsspi_ops = { static const struct udevice_id bcm63xx_hsspi_ids[] = { { .compatible = "brcm,bcm6328-hsspi", }, + { .compatible = "brcm,bcmbca-hsspi-v1.0", }, { /* sentinel */ } }; -- cgit v1.2.3 From 0e144ec38cbb42e4d2f390b8347848fcebcfd974 Mon Sep 17 00:00:00 2001 From: William Zhang Date: Wed, 7 Jun 2023 16:37:04 -0700 Subject: spi: bcm63xx-hsspi: Add prepend mode support Due to the controller limitation to keep the chip select low during the bus idle time between the transfer, a dummy cs workaround was used when this driver was first upstreamed to the u-boot based on linux kernel driver. It basically picks the dummy cs as !actual_cs so typically dummy cs is 1 when most of the case only cs 0 is used in the board design. Then invert the polarity of both cs and tell the controller to start the transfers using dummy cs. Assuming both cs are active low before the inversion, effectively this keeps dummy cs high and actual cs low during the transfer and workaround the issue. This workaround requires that dummy cs 1 pin to is set to SPI chip selection function in the pinmux when the transfer clock is above 25MHz. The old chips likely have default pinmux set to chip select on the dummy cs pin so it works but this is not case for the new Broadband BCA chips and this workaround stop working. This is specifically an issue to support SPI NAND and SPI NOR flash because these flash devices can typically run at or above 100MHz. This patch utilizes the prepend feature of the controller to combine the multiple transfers in the same message to a single transfer when possible. This way there is no need to keep clock low between transfers and solve the issue without any pinmux requirement. Multiple transfers within a SPI message may be combined into one transfer if the following are all true: * One or more half duplex write transfer in single bit mode * Optional full duplex read/write at the end * No delay and cs_change between transfers Most of the SPI device meets this requirements such as SPI NOR, SPI NAND flash, Broadcom SPI voice card and etc. So this change switches to the prepend mode as the default mode. For any SPI message that does not meet the above requirement, we switch to original dummy cs mode but limit the clock rate to the safe 25MHz. Port from linux patch: Link: https://lore.kernel.org/r/20230209200246.141520-12-william.zhang@broadcom.com Signed-off-by: William Zhang Acked-by: Jagan Teki --- drivers/spi/bcm63xx_hsspi.c | 259 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 242 insertions(+), 17 deletions(-) diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c index 0d12c345b1d..a24bb430cbb 100644 --- a/drivers/spi/bcm63xx_hsspi.c +++ b/drivers/spi/bcm63xx_hsspi.c @@ -20,7 +20,13 @@ #define HSSPI_PP 0 -#define SPI_MAX_SYNC_CLOCK 30000000 +/* + * The maximum frequency for SPI synchronous mode is 30MHz for some chips and + * 25MHz for some others. This depends on the chip layout and SPI signals + * distance to the pad. We use the lower of these values to cover all relevant + * chips. + */ +#define SPI_MAX_SYNC_CLOCK 25000000 /* SPI Control register */ #define SPI_CTL_REG 0x000 @@ -72,12 +78,16 @@ #define SPI_PFL_MODE_REG(x) (0x100 + (0x20 * (x)) + 0x08) #define SPI_PFL_MODE_FILL_SHIFT 0 #define SPI_PFL_MODE_FILL_MASK (0xff << SPI_PFL_MODE_FILL_SHIFT) +#define SPI_PFL_MODE_MDRDST_SHIFT 8 +#define SPI_PFL_MODE_MDWRST_SHIFT 12 #define SPI_PFL_MODE_MDRDSZ_SHIFT 16 #define SPI_PFL_MODE_MDRDSZ_MASK (1 << SPI_PFL_MODE_MDRDSZ_SHIFT) #define SPI_PFL_MODE_MDWRSZ_SHIFT 18 #define SPI_PFL_MODE_MDWRSZ_MASK (1 << SPI_PFL_MODE_MDWRSZ_SHIFT) #define SPI_PFL_MODE_3WIRE_SHIFT 20 #define SPI_PFL_MODE_3WIRE_MASK (1 << SPI_PFL_MODE_3WIRE_SHIFT) +#define SPI_PFL_MODE_PREPCNT_SHIFT 24 +#define SPI_PFL_MODE_PREPCNT_MASK (4 << SPI_PFL_MODE_PREPCNT_SHIFT) /* SPI Ping-Pong FIFO registers */ #define HSSPI_FIFO_SIZE 0x200 @@ -96,12 +106,21 @@ #define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT) #define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT) +#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE) +#define HSSPI_MAX_PREPEND_SIZE 15 + +#define HSSPI_XFER_MODE_PREPEND 0 +#define HSSPI_XFER_MODE_DUMMYCS 1 + struct bcm63xx_hsspi_priv { void __iomem *regs; ulong clk_rate; uint8_t num_cs; uint8_t cs_pols; uint speed; + uint xfer_mode; + uint32_t prepend_cnt; + uint8_t prepend_buf[HSSPI_MAX_PREPEND_SIZE]; }; static int bcm63xx_hsspi_cs_info(struct udevice *bus, uint cs, @@ -143,9 +162,16 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, struct dm_spi_slave_plat *plat) { uint32_t clr, set; + uint speed = priv->speed; + + if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS && + speed > SPI_MAX_SYNC_CLOCK) { + speed = SPI_MAX_SYNC_CLOCK; + debug("Force to dummy cs mode. Reduce the speed to %dHz\n", speed); + } /* profile clock */ - set = DIV_ROUND_UP(priv->clk_rate, priv->speed); + set = DIV_ROUND_UP(priv->clk_rate, speed); set = DIV_ROUND_UP(2048, set); set &= SPI_PFL_CLK_FREQ_MASK; set |= SPI_PFL_CLK_RSTLOOP_MASK; @@ -164,7 +190,7 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, set |= SPI_PFL_SIG_LATCHRIS_MASK; /* async clk */ - if (priv->speed > SPI_MAX_SYNC_CLOCK) + if (speed > SPI_MAX_SYNC_CLOCK) set |= SPI_PFL_SIG_ASYNCIN_MASK; clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set); @@ -173,17 +199,24 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, set = 0; clr = 0; - /* invert cs polarity */ - if (priv->cs_pols & BIT(plat->cs)) - clr |= BIT(plat->cs); - else - set |= BIT(plat->cs); - - /* invert dummy cs polarity */ - if (priv->cs_pols & BIT(!plat->cs)) - clr |= BIT(!plat->cs); - else - set |= BIT(!plat->cs); + if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) { + if (priv->cs_pols & BIT(plat->cs)) + set |= BIT(plat->cs); + else + clr |= BIT(plat->cs); + } else { + /* invert cs polarity */ + if (priv->cs_pols & BIT(plat->cs)) + clr |= BIT(plat->cs); + else + set |= BIT(plat->cs); + + /* invert dummy cs polarity */ + if (priv->cs_pols & BIT(!plat->cs)) + clr |= BIT(!plat->cs); + else + set |= BIT(!plat->cs); + } clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set); } @@ -212,13 +245,18 @@ static void bcm63xx_hsspi_deactivate_cs(struct bcm63xx_hsspi_priv *priv) * all the time. This hack is also used in the upstream linux driver and * allows keeping CS active between transfers even if the HW doesn't give * this possibility. + * + * This workaround only works when the dummy CS (usually CS1 when the actual + * CS is 0) pinmuxed to SPI chip select function if SPI clock is faster than + * SPI_MAX_SYNC_CLOCK. In old broadcom chip, CS1 pin is default to chip select + * function. But this is not the case for new chips. To make this function + * always work, it should be called with maximum clock of SPI_MAX_SYNC_CLOCK. */ -static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) +static int bcm63xx_hsspi_xfer_dummy_cs(struct udevice *dev, unsigned int data_bytes, + const void *dout, void *din, unsigned long flags) { struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent); struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); - size_t data_bytes = bitlen / 8; size_t step_size = HSSPI_FIFO_SIZE; uint16_t opcode = 0; uint32_t val = SPI_PFL_MODE_FILL_MASK; @@ -304,6 +342,182 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, return 0; } +static int bcm63xx_prepare_prepend_transfer(struct bcm63xx_hsspi_priv *priv, + unsigned int data_bytes, const void *dout, void *din, + unsigned long flags) +{ + /* + * only support multiple half duplex write transfer + optional + * full duplex read/write at the end. + */ + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->prepend_cnt = 0; + } + + if (din) { + /* buffering reads not possible for prepend mode */ + if (!(flags & SPI_XFER_END)) { + debug("unable to buffer reads\n"); + return HSSPI_XFER_MODE_DUMMYCS; + } + + /* check rx size */ + if (data_bytes > HSSPI_MAX_DATA_SIZE) { + debug("max rx bytes exceeded\n"); + return HSSPI_XFER_MODE_DUMMYCS; + } + } + + if (dout) { + /* check tx size */ + if (flags & SPI_XFER_END) { + if (priv->prepend_cnt + data_bytes > HSSPI_MAX_DATA_SIZE) { + debug("max tx bytes exceeded\n"); + return HSSPI_XFER_MODE_DUMMYCS; + } + } else { + if (priv->prepend_cnt + data_bytes > HSSPI_MAX_PREPEND_SIZE) { + debug("max prepend bytes exceeded\n"); + return HSSPI_XFER_MODE_DUMMYCS; + } + + /* + * buffer transfer data in the prepend buf in case we have to fall + * back to dummy cs mode. + */ + memcpy(&priv->prepend_buf[priv->prepend_cnt], dout, data_bytes); + priv->prepend_cnt += data_bytes; + } + } + + return HSSPI_XFER_MODE_PREPEND; +} + +static int bcm63xx_hsspi_xfer_prepend(struct udevice *dev, unsigned int data_bytes, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent); + struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); + uint16_t opcode = 0; + uint32_t val, offset; + int ret; + + if (flags & SPI_XFER_END) { + offset = HSSPI_FIFO_BASE + HSSPI_FIFO_OP_SIZE; + if (priv->prepend_cnt) { + /* copy prepend data */ + memcpy_toio(priv->regs + offset, + priv->prepend_buf, priv->prepend_cnt); + } + + if (dout && data_bytes) { + /* copy tx data */ + offset += priv->prepend_cnt; + memcpy_toio(priv->regs + offset, dout, data_bytes); + } + + bcm63xx_hsspi_activate_cs(priv, plat); + if (dout && !din) { + /* all half-duplex write. merge to single write */ + data_bytes += priv->prepend_cnt; + opcode = HSSPI_FIFO_OP_CODE_W; + priv->prepend_cnt = 0; + } else if (!dout && din) { + /* half-duplex read with prepend write */ + opcode = HSSPI_FIFO_OP_CODE_R; + } else { + /* full duplex read/write */ + opcode = HSSPI_FIFO_OP_READ_WRITE; + } + + /* profile mode */ + val = SPI_PFL_MODE_FILL_MASK; + if (plat->mode & SPI_3WIRE) + val |= SPI_PFL_MODE_3WIRE_MASK; + + /* dual mode */ + if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) || + (opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) { + opcode |= HSSPI_FIFO_OP_MBIT_MASK; + + if (plat->mode & SPI_RX_DUAL) { + val |= SPI_PFL_MODE_MDRDSZ_MASK; + val |= priv->prepend_cnt << SPI_PFL_MODE_MDRDST_SHIFT; + } + if (plat->mode & SPI_TX_DUAL) { + val |= SPI_PFL_MODE_MDWRSZ_MASK; + val |= priv->prepend_cnt << SPI_PFL_MODE_MDWRST_SHIFT; + } + } + val |= (priv->prepend_cnt << SPI_PFL_MODE_PREPCNT_SHIFT); + writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs)); + + /* set fifo operation */ + val = opcode | (data_bytes & HSSPI_FIFO_OP_BYTES_MASK); + writew(cpu_to_be16(val), + priv->regs + HSSPI_FIFO_OP_REG); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_PFL_SHIFT) & + SPI_CMD_PFL_MASK; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & + SPI_CMD_SLAVE_MASK; + writel(val, priv->regs + SPI_CMD_REG); + + /* wait for completion */ + ret = wait_for_bit_32(priv->regs + SPI_STAT_REG, + SPI_STAT_SRCBUSY_MASK, false, + 1000, false); + if (ret) { + bcm63xx_hsspi_deactivate_cs(priv); + printf("spi polling timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->regs + HSSPI_FIFO_BASE, + data_bytes); + bcm63xx_hsspi_deactivate_cs(priv); + } + + return 0; +} + +static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent); + int ret; + u32 data_bytes = bitlen >> 3; + + if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) { + priv->xfer_mode = + bcm63xx_prepare_prepend_transfer(priv, data_bytes, dout, din, flags); + } + + /* if not prependable, fall back to dummy cs mode with safe clock */ + if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS) { + /* For pending prepend data from previous transfers, send it first */ + if (priv->prepend_cnt) { + bcm63xx_hsspi_xfer_dummy_cs(dev, priv->prepend_cnt, + priv->prepend_buf, NULL, + (flags & ~SPI_XFER_END) | SPI_XFER_BEGIN); + priv->prepend_cnt = 0; + } + ret = bcm63xx_hsspi_xfer_dummy_cs(dev, data_bytes, dout, din, flags); + } else { + ret = bcm63xx_hsspi_xfer_prepend(dev, data_bytes, dout, din, flags); + } + + if (flags & SPI_XFER_END) + priv->xfer_mode = HSSPI_XFER_MODE_PREPEND; + + return ret; +} + static const struct dm_spi_ops bcm63xx_hsspi_ops = { .cs_info = bcm63xx_hsspi_cs_info, .set_mode = bcm63xx_hsspi_set_mode, @@ -321,6 +535,7 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev) { struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent); struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); + struct spi_slave *slave = dev_get_parent_priv(dev); /* check cs */ if (plat->cs >= priv->num_cs) { @@ -334,6 +549,13 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev) else priv->cs_pols &= ~BIT(plat->cs); + /* + * set the max read/write size to make sure each xfer are within the + * prepend limit + */ + slave->max_read_size = HSSPI_MAX_DATA_SIZE; + slave->max_write_size = HSSPI_MAX_DATA_SIZE; + return 0; } @@ -395,6 +617,9 @@ static int bcm63xx_hsspi_probe(struct udevice *dev) priv->cs_pols = readl(priv->regs + SPI_CTL_REG) & SPI_CTL_CS_POL_MASK; + /* default in prepend mode */ + priv->xfer_mode = HSSPI_XFER_MODE_PREPEND; + return 0; } -- cgit v1.2.3 From 55c0144bd30dbbb9a3b1cb6987040022756368f2 Mon Sep 17 00:00:00 2001 From: William Zhang Date: Wed, 7 Jun 2023 16:37:05 -0700 Subject: spi: bcmbca-hsspi: Add driver for newer HSSPI controller The newer BCMBCA SoCs such as BCM6756, BCM4912 and BCM6855 include an updated SPI controller that add the capability to allow the driver to control chip select explicitly. Driver can control and keep cs low between the transfers natively. Hence the dummy cs workaround or prepend mode found in the bcm63xx-hsspi driver are no longer needed and this new driver is much cleaner. Port from linux patch: Link: https://lore.kernel.org/r/20230209200246.141520-15-william.zhang@broadcom.com Signed-off-by: William Zhang Reviewed-by: Jagan Teki --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/bcmbca_hsspi.c | 414 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 424 insertions(+) create mode 100644 drivers/spi/bcmbca_hsspi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7e71c5ae870..854b8b88daf 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -107,6 +107,15 @@ config BCM63XX_HSSPI access the SPI NOR flash on platforms embedding this Broadcom SPI core. +config BCMBCA_HSSPI + bool "BCMBCA HSSPI driver" + depends on ARCH_BCMBCA && HAVE_SPI_CS_CTRL + help + This enables support for the High Speed SPI controller present on + newer Broadcom BCMBCA SoCs. These SoCs include an updated SPI controller + that adds the capability to allow the driver to control chip select + explicitly. + config BCM63XX_SPI bool "BCM6348 SPI driver" depends on ARCH_BMIPS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 95dba9ac455..c27b3327c33 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o +obj-$(CONFIG_BCMBCA_HSSPI) += bcmbca_hsspi.o obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o obj-$(CONFIG_CF_SPI) += cf_spi.o diff --git a/drivers/spi/bcmbca_hsspi.c b/drivers/spi/bcmbca_hsspi.c new file mode 100644 index 00000000000..fbe315a7d45 --- /dev/null +++ b/drivers/spi/bcmbca_hsspi.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas + * + * Derived from linux/drivers/spi/spi-bcm63xx-hsspi.c: + * Copyright (C) 2000-2010 Broadcom Corporation + * Copyright (C) 2012-2013 Jonas Gorski + * Copyright (C) 2021 Broadcom Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HSSPI_PP 0 + +#define SPI_MAX_SYNC_CLOCK 30000000 + +/* SPI Control register */ +#define SPI_CTL_REG 0x000 +#define SPI_CTL_CS_POL_SHIFT 0 +#define SPI_CTL_CS_POL_MASK (0xff << SPI_CTL_CS_POL_SHIFT) +#define SPI_CTL_CLK_GATE_SHIFT 16 +#define SPI_CTL_CLK_GATE_MASK BIT(SPI_CTL_CLK_GATE_SHIFT) +#define SPI_CTL_CLK_POL_SHIFT 17 +#define SPI_CTL_CLK_POL_MASK BIT(SPI_CTL_CLK_POL_SHIFT) + +/* SPI Interrupts registers */ +#define SPI_IR_STAT_REG 0x008 +#define SPI_IR_ST_MASK_REG 0x00c +#define SPI_IR_MASK_REG 0x010 + +#define SPI_IR_CLEAR_ALL 0xff001f1f + +/* SPI Ping-Pong Command registers */ +#define SPI_CMD_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x00) +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START BIT(SPI_CMD_OP_SHIFT) +#define SPI_CMD_PFL_SHIFT 8 +#define SPI_CMD_PFL_MASK (0x7 << SPI_CMD_PFL_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 12 +#define SPI_CMD_SLAVE_MASK (0x7 << SPI_CMD_SLAVE_SHIFT) + +/* SPI Ping-Pong Status registers */ +#define SPI_STAT_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x04) +#define SPI_STAT_SRCBUSY_SHIFT 1 +#define SPI_STAT_SRCBUSY_MASK BIT(SPI_STAT_SRCBUSY_SHIFT) + +/* SPI Profile Clock registers */ +#define SPI_PFL_CLK_REG(x) (0x100 + (0x20 * (x)) + 0x00) +#define SPI_PFL_CLK_FREQ_SHIFT 0 +#define SPI_PFL_CLK_FREQ_MASK (0x3fff << SPI_PFL_CLK_FREQ_SHIFT) +#define SPI_PFL_CLK_RSTLOOP_SHIFT 15 +#define SPI_PFL_CLK_RSTLOOP_MASK BIT(SPI_PFL_CLK_RSTLOOP_SHIFT) + +/* SPI Profile Signal registers */ +#define SPI_PFL_SIG_REG(x) (0x100 + (0x20 * (x)) + 0x04) +#define SPI_PFL_SIG_LATCHRIS_SHIFT 12 +#define SPI_PFL_SIG_LATCHRIS_MASK BIT(SPI_PFL_SIG_LATCHRIS_SHIFT) +#define SPI_PFL_SIG_LAUNCHRIS_SHIFT 13 +#define SPI_PFL_SIG_LAUNCHRIS_MASK BIT(SPI_PFL_SIG_LAUNCHRIS_SHIFT) +#define SPI_PFL_SIG_ASYNCIN_SHIFT 16 +#define SPI_PFL_SIG_ASYNCIN_MASK BIT(SPI_PFL_SIG_ASYNCIN_SHIFT) + +/* SPI Profile Mode registers */ +#define SPI_PFL_MODE_REG(x) (0x100 + (0x20 * (x)) + 0x08) +#define SPI_PFL_MODE_FILL_SHIFT 0 +#define SPI_PFL_MODE_FILL_MASK (0xff << SPI_PFL_MODE_FILL_SHIFT) +#define SPI_PFL_MODE_MDRDSZ_SHIFT 16 +#define SPI_PFL_MODE_MDRDSZ_MASK BIT(SPI_PFL_MODE_MDRDSZ_SHIFT) +#define SPI_PFL_MODE_MDWRSZ_SHIFT 18 +#define SPI_PFL_MODE_MDWRSZ_MASK BIT(SPI_PFL_MODE_MDWRSZ_SHIFT) +#define SPI_PFL_MODE_3WIRE_SHIFT 20 +#define SPI_PFL_MODE_3WIRE_MASK BIT(SPI_PFL_MODE_3WIRE_SHIFT) + +/* SPI Ping-Pong FIFO registers */ +#define HSSPI_FIFO_SIZE 0x200 +#define HSSPI_FIFO_BASE (0x200 + \ + (HSSPI_FIFO_SIZE * HSSPI_PP)) + +/* SPI Ping-Pong FIFO OP register */ +#define HSSPI_FIFO_OP_SIZE 0x2 +#define HSSPI_FIFO_OP_REG (HSSPI_FIFO_BASE + 0x00) +#define HSSPI_FIFO_OP_BYTES_SHIFT 0 +#define HSSPI_FIFO_OP_BYTES_MASK (0x3ff << HSSPI_FIFO_OP_BYTES_SHIFT) +#define HSSPI_FIFO_OP_MBIT_SHIFT 11 +#define HSSPI_FIFO_OP_MBIT_MASK BIT(HSSPI_FIFO_OP_MBIT_SHIFT) +#define HSSPI_FIFO_OP_CODE_SHIFT 13 +#define HSSPI_FIFO_OP_READ_WRITE (1 << HSSPI_FIFO_OP_CODE_SHIFT) +#define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT) +#define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT) + +#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE) + +#define SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT 0 +#define SPIM_CTRL_CS_OVERRIDE_SEL_MASK 0xff +#define SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT 8 +#define SPIM_CTRL_CS_OVERRIDE_VAL_MASK 0xff + +struct bcmbca_hsspi_priv { + void __iomem *regs; + void __iomem *spim_ctrl; + u32 clk_rate; + u8 num_cs; + u8 cs_pols; + u32 speed; +}; + +static int bcmbca_hsspi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcmbca_hsspi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + dev_err(bus, "no cs %u\n", cs); + return -EINVAL; + } + + return 0; +} + +static int bcmbca_hsspi_set_mode(struct udevice *bus, uint mode) +{ + struct bcmbca_hsspi_priv *priv = dev_get_priv(bus); + + /* clock polarity */ + if (mode & SPI_CPOL) + setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); + else + clrbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); + + return 0; +} + +static int bcmbca_hsspi_set_speed(struct udevice *bus, uint speed) +{ + struct bcmbca_hsspi_priv *priv = dev_get_priv(bus); + + priv->speed = speed; + + return 0; +} + +static void bcmbca_hsspi_setup_clock(struct bcmbca_hsspi_priv *priv, + struct dm_spi_slave_plat *plat) +{ + u32 clr, set; + + /* profile clock */ + set = DIV_ROUND_UP(priv->clk_rate, priv->speed); + set = DIV_ROUND_UP(2048, set); + set &= SPI_PFL_CLK_FREQ_MASK; + set |= SPI_PFL_CLK_RSTLOOP_MASK; + writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs)); + + /* profile signal */ + set = 0; + clr = SPI_PFL_SIG_LAUNCHRIS_MASK | + SPI_PFL_SIG_LATCHRIS_MASK | + SPI_PFL_SIG_ASYNCIN_MASK; + + /* latch/launch config */ + if (plat->mode & SPI_CPHA) + set |= SPI_PFL_SIG_LAUNCHRIS_MASK; + else + set |= SPI_PFL_SIG_LATCHRIS_MASK; + + /* async clk */ + if (priv->speed > SPI_MAX_SYNC_CLOCK) + set |= SPI_PFL_SIG_ASYNCIN_MASK; + + clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set); + + /* global control */ + set = 0; + clr = 0; + + if (priv->cs_pols & BIT(plat->cs)) + set |= BIT(plat->cs); + else + clr |= BIT(plat->cs); + + clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set); +} + +static void bcmbca_hsspi_activate_cs(struct bcmbca_hsspi_priv *priv, + struct dm_spi_slave_plat *plat) +{ + u32 val; + + /* set the override bit */ + val = readl(priv->spim_ctrl); + val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT); + writel(val, priv->spim_ctrl); +} + +static void bcmbca_hsspi_deactivate_cs(struct bcmbca_hsspi_priv *priv, + struct dm_spi_slave_plat *plat) +{ + u32 val; + + /* clear the cs override bit */ + val = readl(priv->spim_ctrl); + val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT); + writel(val, priv->spim_ctrl); +} + +static int bcmbca_hsspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcmbca_hsspi_priv *priv = dev_get_priv(dev->parent); + struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); + size_t data_bytes = bitlen / 8; + size_t step_size = HSSPI_FIFO_SIZE; + u16 opcode = 0; + u32 val = SPI_PFL_MODE_FILL_MASK; + const u8 *tx = dout; + u8 *rx = din; + u32 cs_act = 0; + + if (flags & SPI_XFER_BEGIN) + bcmbca_hsspi_setup_clock(priv, plat); + + /* fifo operation */ + if (tx && rx) + opcode = HSSPI_FIFO_OP_READ_WRITE; + else if (rx) + opcode = HSSPI_FIFO_OP_CODE_R; + else if (tx) + opcode = HSSPI_FIFO_OP_CODE_W; + + if (opcode != HSSPI_FIFO_OP_CODE_R) + step_size -= HSSPI_FIFO_OP_SIZE; + + /* dual mode */ + if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) || + (opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) { + opcode |= HSSPI_FIFO_OP_MBIT_MASK; + + /* profile mode */ + if (plat->mode & SPI_RX_DUAL) + val |= SPI_PFL_MODE_MDRDSZ_MASK; + if (plat->mode & SPI_TX_DUAL) + val |= SPI_PFL_MODE_MDWRSZ_MASK; + } + + if (plat->mode & SPI_3WIRE) + val |= SPI_PFL_MODE_3WIRE_MASK; + writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs)); + + /* transfer loop */ + while (data_bytes > 0) { + size_t curr_step = min(step_size, data_bytes); + int ret; + + /* copy tx data */ + if (tx) { + memcpy_toio(priv->regs + HSSPI_FIFO_BASE + + HSSPI_FIFO_OP_SIZE, tx, curr_step); + tx += curr_step; + } + + /* set fifo operation */ + writew(cpu_to_be16(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK)), + priv->regs + HSSPI_FIFO_OP_REG); + + /* make sure we keep cs active until spi transfer is done */ + if (!cs_act) { + bcmbca_hsspi_activate_cs(priv, plat); + cs_act = 1; + } + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_PFL_SHIFT) & + SPI_CMD_PFL_MASK; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & + SPI_CMD_SLAVE_MASK; + writel(val, priv->regs + SPI_CMD_REG); + + /* wait for completion */ + ret = wait_for_bit_32(priv->regs + SPI_STAT_REG, + SPI_STAT_SRCBUSY_MASK, false, + 1000, false); + if (ret) { + bcmbca_hsspi_deactivate_cs(priv, plat); + dev_err(dev, "interrupt timeout\n"); + return ret; + } + + data_bytes -= curr_step; + if ((flags & SPI_XFER_END) && !data_bytes) + bcmbca_hsspi_deactivate_cs(priv, plat); + + /* copy rx data */ + if (rx) { + memcpy_fromio(rx, priv->regs + HSSPI_FIFO_BASE, + curr_step); + rx += curr_step; + } + } + + return 0; +} + +static const struct dm_spi_ops bcmbca_hsspi_ops = { + .cs_info = bcmbca_hsspi_cs_info, + .set_mode = bcmbca_hsspi_set_mode, + .set_speed = bcmbca_hsspi_set_speed, + .xfer = bcmbca_hsspi_xfer, +}; + +static const struct udevice_id bcmbca_hsspi_ids[] = { + { .compatible = "brcm,bcmbca-hsspi-v1.1", }, + { /* sentinel */ } +}; + +static int bcmbca_hsspi_child_pre_probe(struct udevice *dev) +{ + struct bcmbca_hsspi_priv *priv = dev_get_priv(dev->parent); + struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); + u32 val; + + /* check cs */ + if (plat->cs >= priv->num_cs) { + dev_err(dev, "no cs %u\n", plat->cs); + return -EINVAL; + } + + /* cs polarity */ + if (plat->mode & SPI_CS_HIGH) + priv->cs_pols |= BIT(plat->cs); + else + priv->cs_pols &= ~BIT(plat->cs); + + /* set the polarity to spim cs register */ + val = readl(priv->spim_ctrl); + val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT); + if (priv->cs_pols & BIT(plat->cs)) + val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT); + writel(val, priv->spim_ctrl); + + return 0; +} + +static int bcmbca_hsspi_probe(struct udevice *dev) +{ + struct bcmbca_hsspi_priv *priv = dev_get_priv(dev); + struct clk clk; + int ret; + + priv->regs = dev_remap_addr_name(dev, "hsspi"); + if (!priv->regs) + return -EINVAL; + + priv->spim_ctrl = dev_remap_addr_name(dev, "spim-ctrl"); + if (!priv->spim_ctrl) { + dev_err(dev, "misc spim ctrl register not defined in dts!\n"); + return -EINVAL; + } + + priv->num_cs = dev_read_u32_default(dev, "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_name(dev, "hsspi", &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0 && ret != -ENOSYS) + return ret; + + clk_free(&clk); + + /* get clock rate */ + ret = clk_get_by_name(dev, "pll", &clk); + if (ret < 0 && ret != -ENOSYS) + return ret; + + priv->clk_rate = clk_get_rate(&clk); + + clk_free(&clk); + + /* initialize hardware */ + writel(0, priv->regs + SPI_IR_MASK_REG); + + /* clear pending interrupts */ + writel(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG); + + /* enable clk gate */ + setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK); + + /* read default cs polarities */ + priv->cs_pols = readl(priv->regs + SPI_CTL_REG) & + SPI_CTL_CS_POL_MASK; + + dev_info(dev, "Broadcom BCMBCA HS SPI bus driver\n"); + return 0; +} + +U_BOOT_DRIVER(bcmbca_hsspi) = { + .name = "bcmbca_hsspi", + .id = UCLASS_SPI, + .of_match = bcmbca_hsspi_ids, + .ops = &bcmbca_hsspi_ops, + .priv_auto = sizeof(struct bcmbca_hsspi_priv), + .child_pre_probe = bcmbca_hsspi_child_pre_probe, + .probe = bcmbca_hsspi_probe, +}; -- cgit v1.2.3 From 1d70101aa2a489529847231c741dff4f235cfeaa Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Wed, 24 May 2023 16:32:46 +0900 Subject: spi: synquacer: remove SPI_TX_BYTE handling Current code expects that SPI_TX_BYTE is single bit mode but it is wrong. It indicates byte program mode, not single bit mode. If SPI_TX_DUAL, SPI_TX_QUAD and SPI_TX_OCTAL bits are not set, the default transfer bus width is single bit. Signed-off-by: Masahisa Kojima Reviewed-by: Ilias Apalodimas Reviewed-by: Jagan Teki --- drivers/spi/spi-synquacer.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index 0f5d0a30c39..553f9687e3b 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -186,7 +186,7 @@ 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 = 1; + u32 val, div, bus_width; int rwflag; rwflag = (rx ? 1 : 0) | (tx ? 2 : 0); @@ -203,16 +203,14 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx) 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) + 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; else - log_warning("SPI mode not configured, setting to byte mode\n"); + bus_width = 1; /* default is single bit mode */ div = DIV_ROUND_UP(125000000, priv->speed); -- cgit v1.2.3 From 486f4d5a5379b2b857854810a6af686175df4965 Mon Sep 17 00:00:00 2001 From: William Zhang Date: Wed, 7 Jun 2023 16:37:06 -0700 Subject: dt-bindings: spi: Add bcm63xx-hsspi controller support Bring the device tree binding document from Linux to u-boot Port from linux patches: Link: https://lore.kernel.org/r/20230207065826.285013-2-william.zhang@broadcom.com Link: https://lore.kernel.org/r/20230207065826.285013-3-william.zhang@broadcom.com Signed-off-by: William Zhang Reviewed-by: Jagan Teki --- .../spi/brcm,bcm63xx-hsspi.yaml | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml diff --git a/doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml b/doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml new file mode 100644 index 00000000000..6554978583f --- /dev/null +++ b/doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/brcm,bcm63xx-hsspi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom Broadband SoC High Speed SPI controller + +maintainers: + - William Zhang + - Kursad Oney + - Jonas Gorski + +description: | + Broadcom Broadband SoC supports High Speed SPI master controller since the + early MIPS based chips such as BCM6328 and BCM63268. This initial rev 1.0 + controller was carried over to recent ARM based chips, such as BCM63138, + BCM4908 and BCM6858. The old MIPS based chip should continue to use the + brcm,bcm6328-hsspi compatible string. The recent ARM based chip is required to + use the brcm,bcmbca-hsspi-v1.0 as part of its compatible string list as + defined below to match the specific chip along with ip revision info. + + This rev 1.0 controller has a limitation that can not keep the chip select line + active between the SPI transfers within the same SPI message. This can + terminate the transaction to some SPI devices prematurely. The issue can be + worked around by either the controller's prepend mode or using the dummy chip + select workaround. Driver automatically picks the suitable mode based on + transfer type so it is transparent to the user. + + The newer SoCs such as BCM6756, BCM4912 and BCM6855 include an updated SPI + controller rev 1.1 that add the capability to allow the driver to control chip + select explicitly. This solves the issue in the old controller. + +properties: + compatible: + oneOf: + - const: brcm,bcm6328-hsspi + - items: + - enum: + - brcm,bcm47622-hsspi + - brcm,bcm4908-hsspi + - brcm,bcm63138-hsspi + - brcm,bcm63146-hsspi + - brcm,bcm63148-hsspi + - brcm,bcm63158-hsspi + - brcm,bcm63178-hsspi + - brcm,bcm6846-hsspi + - brcm,bcm6856-hsspi + - brcm,bcm6858-hsspi + - brcm,bcm6878-hsspi + - const: brcm,bcmbca-hsspi-v1.0 + - items: + - enum: + - brcm,bcm4912-hsspi + - brcm,bcm6756-hsspi + - brcm,bcm6813-hsspi + - brcm,bcm6855-hsspi + - const: brcm,bcmbca-hsspi-v1.1 + + reg: + items: + - description: main registers + - description: miscellaneous control registers + minItems: 1 + + reg-names: + items: + - const: hsspi + - const: spim-ctrl + minItems: 1 + + clocks: + items: + - description: SPI master reference clock + - description: SPI master pll clock + + clock-names: + items: + - const: hsspi + - const: pll + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +allOf: + - $ref: spi-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - brcm,bcm6328-hsspi + - brcm,bcmbca-hsspi-v1.0 + then: + properties: + reg: + maxItems: 1 + reg-names: + maxItems: 1 + else: + properties: + reg: + minItems: 2 + maxItems: 2 + reg-names: + minItems: 2 + maxItems: 2 + required: + - reg-names + +unevaluatedProperties: false + +examples: + - | + #include + spi@ff801000 { + compatible = "brcm,bcm6756-hsspi", "brcm,bcmbca-hsspi-v1.1"; + reg = <0xff801000 0x1000>, + <0xff802610 0x4>; + reg-names = "hsspi", "spim-ctrl"; + interrupts = ; + clocks = <&hsspi>, <&hsspi_pll>; + clock-names = "hsspi", "pll"; + num-cs = <8>; + #address-cells = <1>; + #size-cells = <0>; + }; -- cgit v1.2.3 From 2e9fe73a883ae3ff4692714d8bbccae0f4f3ba4e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 18 May 2023 19:22:40 -0300 Subject: spi: soft_spi: Support the recommended soft spi properties According to Documentation/devicetree/bindings/spi/spi-gpio.yaml from Linux, the recommended spio-gpio properties are: sck-gpios, miso-gpios and mosi-gpios. gpio-sck, gpio-mosi and gpio-miso are considered deprecated. Currently, U-Boot only supports the deprecated properties. Allow the soft_spi driver to support both the new and old properties. Signed-off-by: Fabio Estevam Reviewed-by: Jagan Teki --- drivers/spi/soft_spi.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index f3602a25ba3..0fa14339bdc 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -248,19 +248,32 @@ static int soft_spi_probe(struct udevice *dev) cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; - if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs, - GPIOD_IS_OUT | cs_flags) || - gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk, - GPIOD_IS_OUT | clk_flags)) + ret = gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs, + GPIOD_IS_OUT | cs_flags); + if (ret) + return -EINVAL; + + ret = gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk, + GPIOD_IS_OUT | clk_flags); + if (ret) + ret = gpio_request_by_name(dev, "sck-gpios", 0, &plat->sclk, + GPIOD_IS_OUT | clk_flags); + if (ret) return -EINVAL; ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) + ret = gpio_request_by_name(dev, "mosi-gpios", 0, &plat->mosi, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); if (ret) plat->flags |= SPI_MASTER_NO_TX; ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso, GPIOD_IS_IN); + if (ret) + ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso, + GPIOD_IS_IN); if (ret) plat->flags |= SPI_MASTER_NO_RX; -- cgit v1.2.3 From a9ab9f7c376a3d03002e3e0f7cff268d2de08f86 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 18 May 2023 19:22:41 -0300 Subject: doc: bindings: soft-spi: Remove the usage of deprecated properties According to Documentation/devicetree/bindings/spi/spi-gpio.yaml from Linux, the recommended spio-gpio properties are: sck-gpios, miso-gpios and mosi-gpios. gpio-sck, gpio-mosi and gpio-miso are considered deprecated. Update the bindings to suggest the recommeded properties. Signed-off-by: Fabio Estevam Reviewed-by: Jagan Teki --- doc/device-tree-bindings/spi/soft-spi.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/device-tree-bindings/spi/soft-spi.txt b/doc/device-tree-bindings/spi/soft-spi.txt index dfb50664732..bdf7e86befb 100644 --- a/doc/device-tree-bindings/spi/soft-spi.txt +++ b/doc/device-tree-bindings/spi/soft-spi.txt @@ -9,10 +9,10 @@ The soft SPI node requires the following properties: Mandatory properties: compatible: "spi-gpio" cs-gpios: GPIOs to use for SPI chip select (output) -gpio-sck: GPIO to use for SPI clock (output) +sck-gpios: GPIO to use for SPI clock (output) And at least one of: -gpio-mosi: GPIO to use for SPI MOSI line (output) -gpio-miso: GPIO to use for SPI MISO line (input) +mosi-gpios: GPIO to use for SPI MOSI line (output) +miso-gpios: GPIO to use for SPI MISO line (input) Optional propertie: spi-delay-us: Number of microseconds of delay between each CS transition @@ -27,9 +27,9 @@ Example: soft-spi { compatible = "spi-gpio"; cs-gpios = <&gpio 235 0>; /* Y43 */ - gpio-sck = <&gpio 225 0>; /* Y31 */ - gpio-mosi = <&gpio 227 0>; /* Y33 */ - gpio-miso = <&gpio 224 0>; /* Y30 */ + sck-gpios = <&gpio 225 0>; /* Y31 */ + mosi-gpios = <&gpio 227 0>; /* Y33 */ + miso-gpios = <&gpio 224 0>; /* Y30 */ spi-delay-us = <1>; #address-cells = <1>; #size-cells = <0>; -- cgit v1.2.3 From 3a4da23c8e31d7adc09eccf7e9ab5ad1204e1a1d Mon Sep 17 00:00:00 2001 From: Bruce Suen Date: Mon, 19 Jun 2023 06:28:58 -0400 Subject: mtd: spi-nor-ids: change full company name of XTX XTX changed full company name from "XTX Technology (Shenzhen) Limited to "XTX Technology Limited" since 2020,So remove "(Shenzhen)". Signed-off-by: Bruce Suen Reviewed-by: Jagan Teki --- drivers/mtd/spi/spi-nor-ids.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index 3f8b7967893..a00e4e50a4b 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -528,7 +528,7 @@ const struct flash_info spi_nor_ids[] = { { INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, #endif #ifdef CONFIG_SPI_FLASH_XTX - /* XTX Technology (Shenzhen) Limited */ + /* XTX Technology Limited */ { INFO("xt25f128b", 0x0b4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, #endif { }, -- cgit v1.2.3 From 41b5c79ea067d28ea27c0c48d4f7c4b89d76ea4f Mon Sep 17 00:00:00 2001 From: Bruce Suen Date: Thu, 13 Jul 2023 14:12:41 +0530 Subject: mtd: spi-nor-ids: add xtxtech part# add following XTX part numbers to the list: xt25f08: 3V QSPI, 8Mbit xt25f16: 3V QSPI, 16Mbit xt25f32: 3V QSPI, 32Mbit xt25f64: 3V QSPI, 64Mbit xt25f128: 3V QSPI, 128Mbit xt25f256: 3V QSPI, 256Mbit xt25q08: 1.8V QSPI, 8Mbit xt25q16: 1.8V QSPI, 16Mbit xt25q32: 1.8V QSPI, 32Mbit xt25q64: 1.8V QSPI, 64Mbit xt25q128: 1.8V QSPI, 128Mbit xt25q256: 1.8V QSPI, 256Mbit xt25q512: 1.8V QSPI, 512Mbit xt25q01g: 1.8V QSPI, 1Gbit xt25w512: wide voltage, QSPI, 512Mbit xt25w01g: wide voltage, QSPI, 1Gbit remove xt25f128b and add xt25f128,because xt25f128b andxt25f128f share same jdec id,we use xt25f128 instead. Signed-off-by: Bruce Suen [jagan: re-edited the entire patch] Signed-off-by: Jagan Teki --- drivers/mtd/spi/spi-nor-ids.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index a00e4e50a4b..ecb9ded5956 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -529,7 +529,41 @@ const struct flash_info spi_nor_ids[] = { #endif #ifdef CONFIG_SPI_FLASH_XTX /* XTX Technology Limited */ - { INFO("xt25f128b", 0x0b4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + /* adding these 3V QSPI flash parts */ + { INFO("xt25f08", 0x0b4014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25f16", 0x0b4015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25f32", 0x0b4016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25f64", 0x0b4017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25f128", 0x0b4018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25f256", 0x0b4019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + /* adding these 1.8V QSPI flash parts */ + { INFO("xt25q08", 0x0b6014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25q16", 0x0b6015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25q32", 0x0b6016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25q64", 0x0b6017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25q128", 0x0b6018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("xt25q256", 0x0b6019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("xt25q512", 0x0b601A, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("xt25q01g", 0x0b601B, 0, 64 * 1024, 2048, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + /* adding these wide voltage QSPI flash parts */ + { INFO("xt25w512", 0x0b651A, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("xt25w01g", 0x0b651B, 0, 64 * 1024, 2048, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, #endif { }, }; -- cgit v1.2.3 From 9b768bf0e312170076823e9e4c8094f3a9351cc6 Mon Sep 17 00:00:00 2001 From: Jim Liu Date: Tue, 4 Jul 2023 16:01:56 +0800 Subject: spi: npcm_pspi: use ACTIVE_LOW flag for cs gpio and set default max_hz If cs gpio is requested with ACTIVE_HIGH flag, it will be pulled low(i.e. active). This is not what we expected. Signed-off-by: Jim Liu Reviewed-by: Jagan Teki --- drivers/spi/npcm_pspi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/spi/npcm_pspi.c b/drivers/spi/npcm_pspi.c index bd9ac654113..37bab709672 100644 --- a/drivers/spi/npcm_pspi.c +++ b/drivers/spi/npcm_pspi.c @@ -40,7 +40,7 @@ static inline void spi_cs_activate(struct udevice *dev) struct udevice *bus = dev->parent; struct npcm_pspi_priv *priv = dev_get_priv(bus); - dm_gpio_set_value(&priv->cs_gpio, 0); + dm_gpio_set_value(&priv->cs_gpio, 1); } static inline void spi_cs_deactivate(struct udevice *dev) @@ -48,7 +48,7 @@ static inline void spi_cs_deactivate(struct udevice *dev) struct udevice *bus = dev->parent; struct npcm_pspi_priv *priv = dev_get_priv(bus); - dm_gpio_set_value(&priv->cs_gpio, 1); + dm_gpio_set_value(&priv->cs_gpio, 0); } static inline void npcm_pspi_enable(struct npcm_pspi_priv *priv) @@ -122,6 +122,9 @@ static int npcm_pspi_xfer(struct udevice *dev, unsigned int bitlen, if (flags & SPI_XFER_END) spi_cs_deactivate(dev); + debug("npcm_pspi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n", + dev->parent->name, dev->name, *(uint *)tx, *(uint *)rx, bitlen); + npcm_pspi_disable(priv); return ret; @@ -183,6 +186,7 @@ static int npcm_pspi_set_mode(struct udevice *bus, uint mode) val |= pspi_mode; writew(val, priv->base + PSPI_CTL1); + debug("%s: mode=%u\n", __func__, mode); return 0; } @@ -197,9 +201,9 @@ static int npcm_pspi_probe(struct udevice *bus) return ret; priv->base = dev_read_addr_ptr(bus); - priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0); + priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 1000000); gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0, - &priv->cs_gpio, GPIOD_IS_OUT); + &priv->cs_gpio, GPIOD_IS_OUT| GPIOD_ACTIVE_LOW); return 0; } -- cgit v1.2.3 From 4a31e145217cecc3d421f96eafcd2cfd9c670929 Mon Sep 17 00:00:00 2001 From: Venkatesh Yadav Abbarapu Date: Mon, 26 Jun 2023 09:02:37 +0530 Subject: mtd: spi-nor: Add support for w25q256jwm Add support for Winbond 256M-bit flash w25q256jwm. Performed basic erase/write/readback operations on ZynqMP zc1751+dc1 board. Signed-off-by: Venkatesh Yadav Abbarapu Reviewed-by: Jagan Teki --- drivers/mtd/spi/spi-nor-ids.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index ecb9ded5956..45872159842 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -446,6 +446,11 @@ const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + INFO("w25q256jwm", 0xef8019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { INFO("w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { INFO("w25q64dw", 0xef6017, 0, 64 * 1024, 128, -- cgit v1.2.3