[U-Boot] [PATCH 00/10] mips: bmips: add SPI support

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
Álvaro Fernández Rojas (10): drivers: spi: allow limiting reads drivers: spi: add config to consider command bytes when writting to flash dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/mips/dts/brcm,bcm3380.dtsi | 13 ++ arch/mips/dts/brcm,bcm63268.dtsi | 13 ++ arch/mips/dts/brcm,bcm6338.dtsi | 13 ++ arch/mips/dts/brcm,bcm6348.dtsi | 13 ++ arch/mips/dts/brcm,bcm6358.dtsi | 13 ++ arch/mips/dts/netgear,cg3100d.dts | 13 ++ arch/mips/dts/sagem,f@st1704.dts | 13 ++ configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/mtd/spi/spi_flash.c | 5 +- drivers/spi/Kconfig | 26 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++ include/spi.h | 11 +- 14 files changed, 552 insertions(+), 2 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index ab7910b..e44c10f 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -487,6 +487,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index deb65ef..75a994c 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map; u8 option;

On 18 May 2017 at 13:29, Álvaro Fernández Rojas noltari@gmail.com wrote:
For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- drivers/mtd/spi/spi_flash.c | 2 +- drivers/spi/Kconfig | 3 +++ include/spi.h | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..748cc32 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi_max_write(spi, sizeof(cmd)));
spi_flash_addr(write_addr, cmd);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f3f7dbe..a00d401 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -13,6 +13,9 @@ config DM_SPI typically use driver-private data instead of extending the spi_slave structure.
+config SPI_MAX_WRITE_CMD_BYTES + bool "Include command bytes when determining max write size" + if DM_SPI
config ALTERA_SPI diff --git a/include/spi.h b/include/spi.h index 75a994c..310fa4d 100644 --- a/include/spi.h +++ b/include/spi.h @@ -63,6 +63,12 @@ struct dm_spi_slave_platdata {
#endif /* CONFIG_DM_SPI */
+#ifdef CONFIG_SPI_MAX_WRITE_CMD_BYTES +#define spi_max_write(spi, len) (spi->max_write_size - len) +#else +#define spi_max_write(spi, len) (spi->max_write_size) +#endif + /** * struct spi_slave - Representation of a SPI slave * @@ -89,7 +95,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

Hi Alvaro,
On 18 May 2017 at 13:29, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/mtd/spi/spi_flash.c | 2 +- drivers/spi/Kconfig | 3 +++ include/spi.h | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..748cc32 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len,
(size_t)spi->max_write_size);
spi_max_write(spi, sizeof(cmd))); spi_flash_addr(write_addr, cmd);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f3f7dbe..a00d401 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -13,6 +13,9 @@ config DM_SPI typically use driver-private data instead of extending the spi_slave structure.
+config SPI_MAX_WRITE_CMD_BYTES
bool "Include command bytes when determining max write size"
Do you really need this, or can you just always do this? If you need it, please add detailed help.
if DM_SPI
config ALTERA_SPI diff --git a/include/spi.h b/include/spi.h index 75a994c..310fa4d 100644 --- a/include/spi.h +++ b/include/spi.h @@ -63,6 +63,12 @@ struct dm_spi_slave_platdata {
#endif /* CONFIG_DM_SPI */
+#ifdef CONFIG_SPI_MAX_WRITE_CMD_BYTES +#define spi_max_write(spi, len) (spi->max_write_size - len) +#else +#define spi_max_write(spi, len) (spi->max_write_size) +#endif
/**
- struct spi_slave - Representation of a SPI slave
@@ -89,7 +95,7 @@ struct dm_spi_slave_platdata {
- @max_read_size: If non-zero, the maximum number of bytes which can
be read at once.
- @max_write_size: If non-zero, the maximum number of bytes which can
be written at once, excluding command bytes.
*/
be written at once.
- @memory_map: Address of read-only SPI flash access.
- @flags: Indication of SPI flags.
-- 2.1.4
Regards, Simon

Hi Simon,
El 20/05/2017 a las 4:29, Simon Glass escribió:
Hi Alvaro,
On 18 May 2017 at 13:29, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/mtd/spi/spi_flash.c | 2 +- drivers/spi/Kconfig | 3 +++ include/spi.h | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..748cc32 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len,
(size_t)spi->max_write_size);
spi_max_write(spi, sizeof(cmd))); spi_flash_addr(write_addr, cmd);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f3f7dbe..a00d401 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -13,6 +13,9 @@ config DM_SPI typically use driver-private data instead of extending the spi_slave structure.
+config SPI_MAX_WRITE_CMD_BYTES
bool "Include command bytes when determining max write size"
Do you really need this, or can you just always do this? If you need it, please add detailed help.
Actually we don't need this at all and we could do it always from a BCM63xx point of view, but there are drivers that are hacking this at a lower level, like the ich one: https://github.com/Noltari/u-boot/blob/master/drivers/spi/ich.c#L355
In my opinion this is wrong, but I added that new kconfig because I didn't want to break other SPI drivers...
Maybe I should reverse it and make this the default, adding a kconfig option option which doesn't include command bytes for those drivers (fsl_qspi also uses max_write_size, but it's quite complex and didn't check if it would break it...).
if DM_SPI
config ALTERA_SPI diff --git a/include/spi.h b/include/spi.h index 75a994c..310fa4d 100644 --- a/include/spi.h +++ b/include/spi.h @@ -63,6 +63,12 @@ struct dm_spi_slave_platdata {
#endif /* CONFIG_DM_SPI */
+#ifdef CONFIG_SPI_MAX_WRITE_CMD_BYTES +#define spi_max_write(spi, len) (spi->max_write_size - len) +#else +#define spi_max_write(spi, len) (spi->max_write_size) +#endif
/**
- struct spi_slave - Representation of a SPI slave
@@ -89,7 +95,7 @@ struct dm_spi_slave_platdata {
- @max_read_size: If non-zero, the maximum number of bytes which can
be read at once.
- @max_write_size: If non-zero, the maximum number of bytes which can
be written at once, excluding command bytes.
*/
be written at once.
- @memory_map: Address of read-only SPI flash access.
- @flags: Indication of SPI flags.
-- 2.1.4
Regards, Simon
Regards, Álvaro.

On Sat, May 20, 2017 at 1:36 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Simon,
El 20/05/2017 a las 4:29, Simon Glass escribió:
Hi Alvaro,
On 18 May 2017 at 13:29, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/mtd/spi/spi_flash.c | 2 +- drivers/spi/Kconfig | 3 +++ include/spi.h | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-)
Is this patch not part of your v2 BCM-SPI changes?
thanks!

Hi Jagan,
El 30/05/2017 a las 7:04, Jagan Teki escribió:
On Sat, May 20, 2017 at 1:36 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Simon,
El 20/05/2017 a las 4:29, Simon Glass escribió:
Hi Alvaro,
On 18 May 2017 at 13:29, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/mtd/spi/spi_flash.c | 2 +- drivers/spi/Kconfig | 3 +++ include/spi.h | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-)
Is this patch not part of your v2 BCM-SPI changes?
Nope, I removed it and decided to always consider command bytes as suggested by Simon: https://lists.denx.de/pipermail/u-boot/2017-May/292445.html
thanks!
Than, Álvaro.

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a00d401..d458dd7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -43,6 +43,29 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice + prompt "BCM63xx SPI driver" + depends on ARCH_BMIPS + optional + +config BCM6338_SPI + bool "BCM6338 SPI driver" + select SPI_MAX_WRITE_CMD_BYTES + help + Enable the BCM6338 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + +config BCM6358_SPI + bool "BCM6358 SPI driver" + select SPI_MAX_WRITE_CMD_BYTES + help + Enable the BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + +endchoice + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BCM6338_SPI) + +# define SPI_DT_ID "brcm,bcm6338-spi" + +/* SPI Command register */ +# define SPI_CMD_REG 0x00 + +/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04 + +/* SPI Clock register */ +# define SPI_CLK_REG 0x06 + +/* SPI Fill register */ +# define SPI_FILL_REG 0x07 + +/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT) + +# define bcm63xx_spi_wctl(v,a) writeb_be(v, a); + +/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f + +/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f + +#elif defined(CONFIG_BCM6358_SPI) + +# define SPI_DT_ID "brcm,bcm6358-spi" + +/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT) + +# define bcm63xx_spi_wctl(v,a) writew_be(v, a); + +/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e + +/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220 + +/* SPI Command register */ +# define SPI_CMD_REG 0x700 + +/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704 + +/* SPI Clock register */ +# define SPI_CLK_REG 0x706 + +/* SPI Fill register */ +# define SPI_FILL_REG 0x707 + +#endif + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +struct bcm63xx_spi_priv { + void __iomem *regs; + uint8_t num_cs; + size_t tx_bytes; +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + error("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->regs + SPI_CLK_REG, + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->regs + SPI_IR_MASK_REG); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + error("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > SPI_RX_DATA_SIZE) { + error("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) { + error("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val = 0; + uint8_t irq; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_HD_W; + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val |= (data_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_FD_RW; + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val |= (data_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_HD_R; + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + error("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + val |= SPI_CMD_3WIRE_MASK; + writew_be(val, priv->regs + SPI_CMD_REG); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG); + + do { + /* read interupts */ + irq = readb_be(priv->regs + SPI_IR_STAT_REG); + + /* transfer completed */ + if (irq & SPI_IR_DONE_MASK) + break; + } while (1); + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG, + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { .compatible = SPI_DT_ID, }, + { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + error("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = SPI_RX_DATA_SIZE; + slave->max_write_size = SPI_TX_DATA_SIZE; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + clk_enable(&clk); + clk_free(&clk); + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + reset_deassert(&rst_ctl); + reset_free(&rst_ctl); + + /* initialize hardware */ + writeb_be(0, priv->regs + SPI_IR_MASK_REG); + + /* set fill register */ + writeb_be(0xff, priv->regs + SPI_FILL_REG); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

On 18 May 2017 at 13:29, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
Reviewed-by: Simon Glass sjg@chromium.org

On Fri, May 19, 2017 at 12:59 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a00d401..d458dd7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -43,6 +43,29 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice
prompt "BCM63xx SPI driver"
depends on ARCH_BMIPS
optional
+config BCM6338_SPI
bool "BCM6338 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6338 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+config BCM6358_SPI
bool "BCM6358 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+endchoice
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+#if defined(CONFIG_BCM6338_SPI)
+# define SPI_DT_ID "brcm,bcm6338-spi"
+/* SPI Command register */ +# define SPI_CMD_REG 0x00
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04
+/* SPI Clock register */ +# define SPI_CLK_REG 0x06
+/* SPI Fill register */ +# define SPI_FILL_REG 0x07
+/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writeb_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f
+#elif defined(CONFIG_BCM6358_SPI)
+# define SPI_DT_ID "brcm,bcm6358-spi"
+/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writew_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220
+/* SPI Command register */ +# define SPI_CMD_REG 0x700
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704
+/* SPI Clock register */ +# define SPI_CLK_REG 0x706
+/* SPI Fill register */ +# define SPI_FILL_REG 0x707
+#endif
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+struct bcm63xx_spi_priv {
void __iomem *regs;
uint8_t num_cs;
size_t tx_bytes;
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (mode & SPI_LSB_FIRST)
setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->regs + SPI_CLK_REG,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->regs + SPI_IR_MASK_REG);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > SPI_RX_DATA_SIZE) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val = 0;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_W;
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_FD_RW;
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_R;
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->regs + SPI_CMD_REG);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG);
do {
/* read interupts */
irq = readb_be(priv->regs + SPI_IR_STAT_REG);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct udevice_id bcm63xx_spi_ids[] = {
{ .compatible = SPI_DT_ID, },
{ /* sentinel */ }
Try to add .data with reg_space of respective controller, this will eventually reduce to driver configs and make it CONFIG_BCM63XX and reduce unneeded#ifdef.
thanks!

Hi Jagan,
El 7/6/17 a las 9:35, Jagan Teki escribió:
On Fri, May 19, 2017 at 12:59 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a00d401..d458dd7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -43,6 +43,29 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice
prompt "BCM63xx SPI driver"
depends on ARCH_BMIPS
optional
+config BCM6338_SPI
bool "BCM6338 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6338 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+config BCM6358_SPI
bool "BCM6358 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+endchoice
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+#if defined(CONFIG_BCM6338_SPI)
+# define SPI_DT_ID "brcm,bcm6338-spi"
+/* SPI Command register */ +# define SPI_CMD_REG 0x00
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04
+/* SPI Clock register */ +# define SPI_CLK_REG 0x06
+/* SPI Fill register */ +# define SPI_FILL_REG 0x07
+/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writeb_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f
+#elif defined(CONFIG_BCM6358_SPI)
+# define SPI_DT_ID "brcm,bcm6358-spi"
+/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writew_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220
+/* SPI Command register */ +# define SPI_CMD_REG 0x700
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704
+/* SPI Clock register */ +# define SPI_CLK_REG 0x706
+/* SPI Fill register */ +# define SPI_FILL_REG 0x707
+#endif
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+struct bcm63xx_spi_priv {
void __iomem *regs;
uint8_t num_cs;
size_t tx_bytes;
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (mode & SPI_LSB_FIRST)
setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->regs + SPI_CLK_REG,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->regs + SPI_IR_MASK_REG);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > SPI_RX_DATA_SIZE) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val = 0;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_W;
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_FD_RW;
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_R;
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->regs + SPI_CMD_REG);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG);
do {
/* read interupts */
irq = readb_be(priv->regs + SPI_IR_STAT_REG);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct udevice_id bcm63xx_spi_ids[] = {
{ .compatible = SPI_DT_ID, },
{ /* sentinel */ }
Try to add .data with reg_space of respective controller, this will eventually reduce to driver configs and make it CONFIG_BCM63XX and reduce unneeded#ifdef.
This implies adding considerable bloat to the driver... Is it imperative for the driver to be accepted?
thanks!
Thanks.

On Wed, Jun 7, 2017 at 9:05 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 7/6/17 a las 9:35, Jagan Teki escribió:
On Fri, May 19, 2017 at 12:59 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a00d401..d458dd7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -43,6 +43,29 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice
prompt "BCM63xx SPI driver"
depends on ARCH_BMIPS
optional
+config BCM6338_SPI
bool "BCM6338 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6338 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+config BCM6358_SPI
bool "BCM6358 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+endchoice
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+#if defined(CONFIG_BCM6338_SPI)
+# define SPI_DT_ID "brcm,bcm6338-spi"
+/* SPI Command register */ +# define SPI_CMD_REG 0x00
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04
+/* SPI Clock register */ +# define SPI_CLK_REG 0x06
+/* SPI Fill register */ +# define SPI_FILL_REG 0x07
+/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writeb_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f
+#elif defined(CONFIG_BCM6358_SPI)
+# define SPI_DT_ID "brcm,bcm6358-spi"
+/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writew_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220
+/* SPI Command register */ +# define SPI_CMD_REG 0x700
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704
+/* SPI Clock register */ +# define SPI_CLK_REG 0x706
+/* SPI Fill register */ +# define SPI_FILL_REG 0x707
+#endif
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+struct bcm63xx_spi_priv {
void __iomem *regs;
uint8_t num_cs;
size_t tx_bytes;
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (mode & SPI_LSB_FIRST)
setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->regs + SPI_CLK_REG,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->regs + SPI_IR_MASK_REG);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > SPI_RX_DATA_SIZE) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val = 0;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_W;
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_FD_RW;
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_R;
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->regs + SPI_CMD_REG);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG);
do {
/* read interupts */
irq = readb_be(priv->regs + SPI_IR_STAT_REG);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct udevice_id bcm63xx_spi_ids[] = {
{ .compatible = SPI_DT_ID, },
{ /* sentinel */ }
Try to add .data with reg_space of respective controller, this will eventually reduce to driver configs and make it CONFIG_BCM63XX and reduce unneeded#ifdef.
This implies adding considerable bloat to the driver... Is it imperative for the driver to be accepted?
Yes, as per as recent U-boot developement we're trying to reduce defconfig code as possible and even this method can improve code quality.
thanks!

Hi Jagan,
El 07/06/2017 a las 19:29, Jagan Teki escribió:
On Wed, Jun 7, 2017 at 9:05 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 7/6/17 a las 9:35, Jagan Teki escribió:
On Fri, May 19, 2017 at 12:59 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a00d401..d458dd7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -43,6 +43,29 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice
prompt "BCM63xx SPI driver"
depends on ARCH_BMIPS
optional
+config BCM6338_SPI
bool "BCM6338 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6338 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+config BCM6358_SPI
bool "BCM6358 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+endchoice
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+#if defined(CONFIG_BCM6338_SPI)
+# define SPI_DT_ID "brcm,bcm6338-spi"
+/* SPI Command register */ +# define SPI_CMD_REG 0x00
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04
+/* SPI Clock register */ +# define SPI_CLK_REG 0x06
+/* SPI Fill register */ +# define SPI_FILL_REG 0x07
+/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writeb_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f
+#elif defined(CONFIG_BCM6358_SPI)
+# define SPI_DT_ID "brcm,bcm6358-spi"
+/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writew_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220
+/* SPI Command register */ +# define SPI_CMD_REG 0x700
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704
+/* SPI Clock register */ +# define SPI_CLK_REG 0x706
+/* SPI Fill register */ +# define SPI_FILL_REG 0x707
+#endif
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+struct bcm63xx_spi_priv {
void __iomem *regs;
uint8_t num_cs;
size_t tx_bytes;
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (mode & SPI_LSB_FIRST)
setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->regs + SPI_CLK_REG,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->regs + SPI_IR_MASK_REG);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > SPI_RX_DATA_SIZE) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val = 0;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_W;
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_FD_RW;
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_R;
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->regs + SPI_CMD_REG);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG);
do {
/* read interupts */
irq = readb_be(priv->regs + SPI_IR_STAT_REG);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct udevice_id bcm63xx_spi_ids[] = {
{ .compatible = SPI_DT_ID, },
{ /* sentinel */ }
Try to add .data with reg_space of respective controller, this will eventually reduce to driver configs and make it CONFIG_BCM63XX and reduce unneeded#ifdef.
This implies adding considerable bloat to the driver... Is it imperative for the driver to be accepted?
Yes, as per as recent U-boot developement we're trying to reduce defconfig code as possible and even this method can improve code quality.
I don't think that method applies here, since u-boot is usally built for each SoC and supporting two cores in the same build makes no sense to me. These should be done in two different drivers but I merged them into a single one to improve maintainability. BTW, two u-boot developers already reviewed this patch and didn't complain about it...
thanks!
Thanks.

On Thu, Jun 8, 2017 at 12:05 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 07/06/2017 a las 19:29, Jagan Teki escribió:
On Wed, Jun 7, 2017 at 9:05 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 7/6/17 a las 9:35, Jagan Teki escribió:
On Fri, May 19, 2017 at 12:59 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a00d401..d458dd7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -43,6 +43,29 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice
prompt "BCM63xx SPI driver"
depends on ARCH_BMIPS
optional
+config BCM6338_SPI
bool "BCM6338 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6338 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+config BCM6358_SPI
bool "BCM6358 SPI driver"
select SPI_MAX_WRITE_CMD_BYTES
help
Enable the BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
+endchoice
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+#if defined(CONFIG_BCM6338_SPI)
+# define SPI_DT_ID "brcm,bcm6338-spi"
+/* SPI Command register */ +# define SPI_CMD_REG 0x00
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04
+/* SPI Clock register */ +# define SPI_CLK_REG 0x06
+/* SPI Fill register */ +# define SPI_FILL_REG 0x07
+/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writeb_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f
+#elif defined(CONFIG_BCM6358_SPI)
+# define SPI_DT_ID "brcm,bcm6358-spi"
+/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT)
+# define bcm63xx_spi_wctl(v,a) writew_be(v, a);
+/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e
+/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220
+/* SPI Command register */ +# define SPI_CMD_REG 0x700
+/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704
+/* SPI Clock register */ +# define SPI_CLK_REG 0x706
+/* SPI Fill register */ +# define SPI_FILL_REG 0x707
+#endif
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+struct bcm63xx_spi_priv {
void __iomem *regs;
uint8_t num_cs;
size_t tx_bytes;
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (mode & SPI_LSB_FIRST)
setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->regs + SPI_CLK_REG,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->regs + SPI_IR_MASK_REG);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > SPI_RX_DATA_SIZE) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val = 0;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_W;
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_FD_RW;
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val |= (data_bytes << SPI_CTL_BYTES_SHIFT);
val |= SPI_CTL_TYPE_HD_R;
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->regs + SPI_CMD_REG);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG);
do {
/* read interupts */
irq = readb_be(priv->regs + SPI_IR_STAT_REG);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct udevice_id bcm63xx_spi_ids[] = {
{ .compatible = SPI_DT_ID, },
{ /* sentinel */ }
Try to add .data with reg_space of respective controller, this will eventually reduce to driver configs and make it CONFIG_BCM63XX and reduce unneeded#ifdef.
This implies adding considerable bloat to the driver... Is it imperative for the driver to be accepted?
Yes, as per as recent U-boot developement we're trying to reduce defconfig code as possible and even this method can improve code quality.
I don't think that method applies here, since u-boot is usally built for each SoC and supporting two cores in the same build makes no sense to me. These should be done in two different drivers but I merged them into a single one to improve maintainability.
I don't like the approch of adding ifdef with two controller reg_space, we have .data that has been used in many dm-driver, let's have a try.
BTW, two u-boot developers already reviewed this patch and didn't complain about it...
Wrong statement, it is community project every-body comment need to be considrable.
thanks!

Hi,
On 12 June 2017 at 00:02, Jagan Teki jagannadh.teki@gmail.com wrote:
On Thu, Jun 8, 2017 at 12:05 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 07/06/2017 a las 19:29, Jagan Teki escribió:
On Wed, Jun 7, 2017 at 9:05 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 7/6/17 a las 9:35, Jagan Teki escribió:
On Fri, May 19, 2017 at 12:59 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
drivers/spi/Kconfig | 23 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
[...]
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct udevice_id bcm63xx_spi_ids[] = {
{ .compatible = SPI_DT_ID, },
{ /* sentinel */ }
Try to add .data with reg_space of respective controller, this will eventually reduce to driver configs and make it CONFIG_BCM63XX and reduce unneeded#ifdef.
This implies adding considerable bloat to the driver... Is it imperative for the driver to be accepted?
Yes, as per as recent U-boot developement we're trying to reduce defconfig code as possible and even this method can improve code quality.
I don't think that method applies here, since u-boot is usally built for each SoC and supporting two cores in the same build makes no sense to me. These should be done in two different drivers but I merged them into a single one to improve maintainability.
I don't like the approch of adding ifdef with two controller reg_space, we have .data that has been used in many dm-driver, let's have a try.
I do agree that #ifdefs in the driver are not desirable and it is better to use the device tree .data field to select the correct feature. Hopefully this involves not too much refactoring. As an example of this see ich.c which handles a few different Intel SPI controllers. It's a complicated driver because of the way the hardware works but it does show how to do this sort of thing at run-time.
BTW, two u-boot developers already reviewed this patch and didn't complain about it...
Wrong statement, it is community project every-body comment need to be considrable.
thanks!
Jagan Teki Free Software Engineer | www.openedev.com U-Boot, Linux | Upstream Maintainer Hyderabad, India.
Regards, Simon

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/brcm,bcm6338.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..614792e 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -109,6 +109,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6338-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/brcm,bcm6348.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643..841f883 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -118,6 +118,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6338-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/brcm,bcm6358.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf8..11f990d 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -142,6 +142,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/brcm,bcm3380.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index e351d58..3c19b90 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -142,6 +142,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/brcm,bcm63268.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96b..8beeeef 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -136,6 +136,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/sagem,f@st1704.dts | 13 +++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 21 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5..f23cd02 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -14,6 +14,7 @@
aliases { serial0 = &uart0; + spi0 = &spi; };
chosen { @@ -44,6 +45,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index 8e89c15..d20f95c 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -1,5 +1,6 @@ CONFIG_ARCH_BMIPS=y CONFIG_BAUDRATE=115200 +CONFIG_BCM6338_SPI=y CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="sagem,f@st1704" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_GPIO=y @@ -45,6 +50,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM6338=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- arch/mips/dts/netgear,cg3100d.dts | 13 +++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 21 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7..8112c3d 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -14,6 +14,7 @@
aliases { serial0 = &uart0; + spi0 = &spi; };
chosen { @@ -90,6 +91,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index a6eff10..da2426b 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -3,6 +3,7 @@ CONFIG_BAUDRATE=115200 CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM6358_SPI=y CONFIG_BMIPS_BOOT_RAM=y CONFIG_BOARD_NETGEAR_CG3100D=y # CONFIG_CMD_BOOTD is not set @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="netgear,cg3100d" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_BCM6328=y @@ -47,6 +52,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM3380=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (10): drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/mips/dts/brcm,bcm3380.dtsi | 17 ++ arch/mips/dts/brcm,bcm63268.dtsi | 17 ++ arch/mips/dts/brcm,bcm6338.dtsi | 17 ++ arch/mips/dts/brcm,bcm6348.dtsi | 17 ++ arch/mips/dts/brcm,bcm6358.dtsi | 17 ++ arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/mtd/spi/spi_flash.c | 5 +- drivers/spi/Kconfig | 21 ++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++ include/spi.h | 5 +- 14 files changed, 559 insertions(+), 2 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index ab7910b..e44c10f 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -487,6 +487,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index deb65ef..75a994c 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map; u8 option;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..5b8cbc9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - len);
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index 75a994c..f418ea1 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

On 22 May 2017 at 12:21, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

On Mon, May 22, 2017 at 11:51 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..5b8cbc9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len,
(size_t)spi->max_write_size);
spi->max_write_size - len);
This can be squashed with 01/10 with proper commit message?
thanks!

Hi Jagan,
El 7/6/17 a las 9:30, Jagan Teki escribió:
On Mon, May 22, 2017 at 11:51 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..5b8cbc9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len,
(size_t)spi->max_write_size);
spi->max_write_size - len);
This can be squashed with 01/10 with proper commit message?
I think it's better if we keep it as two patches, because they are different features...
thanks!
Thanks.

On Wed, Jun 7, 2017 at 9:03 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 7/6/17 a las 9:30, Jagan Teki escribió:
On Mon, May 22, 2017 at 11:51 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..5b8cbc9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len,
(size_t)spi->max_write_size);
spi->max_write_size - len);
This can be squashed with 01/10 with proper commit message?
I think it's better if we keep it as two patches, because they are different features...
Fine, but adding immediate change followed by previous change deletion doesn't look feature that exist.
thanks!

Hi Jagan,
El 07/06/2017 a las 19:28, Jagan Teki escribió:
On Wed, Jun 7, 2017 at 9:03 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 7/6/17 a las 9:30, Jagan Teki escribió:
On Mon, May 22, 2017 at 11:51 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e44c10f..5b8cbc9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len,
(size_t)spi->max_write_size);
spi->max_write_size - len);
This can be squashed with 01/10 with proper commit message?
I think it's better if we keep it as two patches, because they are different features...
Fine, but adding immediate change followed by previous change deletion doesn't look feature that exist.
Nope, there's no previous change deletion. First patch adds support for limited reads and second patch fixes limited writes...
thanks!
Thanks

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- v2: no changes
drivers/spi/Kconfig | 21 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bef864f..e452223 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,27 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice + prompt "BCM63xx SPI driver" + depends on ARCH_BMIPS + optional + +config BCM6338_SPI + bool "BCM6338 SPI driver" + help + Enable the BCM6338 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + +config BCM6358_SPI + bool "BCM6358 SPI driver" + help + Enable the BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + +endchoice + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..c9ba648 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6338_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..6e64607 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BCM6338_SPI) + +# define SPI_DT_ID "brcm,bcm6338-spi" + +/* SPI Command register */ +# define SPI_CMD_REG 0x00 + +/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04 + +/* SPI Clock register */ +# define SPI_CLK_REG 0x06 + +/* SPI Fill register */ +# define SPI_FILL_REG 0x07 + +/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT) + +# define bcm63xx_spi_wctl(v,a) writeb_be(v, a); + +/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f + +/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f + +#elif defined(CONFIG_BCM6358_SPI) + +# define SPI_DT_ID "brcm,bcm6358-spi" + +/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT) + +# define bcm63xx_spi_wctl(v,a) writew_be(v, a); + +/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e + +/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220 + +/* SPI Command register */ +# define SPI_CMD_REG 0x700 + +/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704 + +/* SPI Clock register */ +# define SPI_CLK_REG 0x706 + +/* SPI Fill register */ +# define SPI_FILL_REG 0x707 + +#endif + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +struct bcm63xx_spi_priv { + void __iomem *regs; + uint8_t num_cs; + size_t tx_bytes; +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + error("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->regs + SPI_CLK_REG, + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->regs + SPI_IR_MASK_REG); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + error("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > SPI_RX_DATA_SIZE) { + error("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) { + error("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val = 0; + uint8_t irq; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_HD_W; + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val |= (data_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_FD_RW; + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val |= (data_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_HD_R; + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + error("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + val |= SPI_CMD_3WIRE_MASK; + writew_be(val, priv->regs + SPI_CMD_REG); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG); + + do { + /* read interupts */ + irq = readb_be(priv->regs + SPI_IR_STAT_REG); + + /* transfer completed */ + if (irq & SPI_IR_DONE_MASK) + break; + } while (1); + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG, + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { .compatible = SPI_DT_ID, }, + { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + error("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = SPI_RX_DATA_SIZE; + slave->max_write_size = SPI_TX_DATA_SIZE; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + clk_enable(&clk); + clk_free(&clk); + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + reset_deassert(&rst_ctl); + reset_free(&rst_ctl); + + /* initialize hardware */ + writeb_be(0, priv->regs + SPI_IR_MASK_REG); + + /* set fill register */ + writeb_be(0xff, priv->regs + SPI_FILL_REG); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..98bda9c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6338-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643..2347b71 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6338-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf8..1662783 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index e351d58..31c4ec4 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96b..6e3d9c3 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5..dd0e5b8 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index 8e89c15..d20f95c 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -1,5 +1,6 @@ CONFIG_ARCH_BMIPS=y CONFIG_BAUDRATE=115200 +CONFIG_BCM6338_SPI=y CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="sagem,f@st1704" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_GPIO=y @@ -45,6 +50,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM6338=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7..5f85c73 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index a6eff10..da2426b 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -3,6 +3,7 @@ CONFIG_BAUDRATE=115200 CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM6358_SPI=y CONFIG_BMIPS_BOOT_RAM=y CONFIG_BOARD_NETGEAR_CG3100D=y # CONFIG_CMD_BOOTD is not set @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="netgear,cg3100d" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_BCM6328=y @@ -47,6 +52,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM3380=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (10): drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/mips/dts/brcm,bcm3380.dtsi | 17 ++ arch/mips/dts/brcm,bcm63268.dtsi | 17 ++ arch/mips/dts/brcm,bcm6338.dtsi | 17 ++ arch/mips/dts/brcm,bcm6348.dtsi | 17 ++ arch/mips/dts/brcm,bcm6358.dtsi | 17 ++ arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/mtd/spi/spi_flash.c | 5 +- drivers/spi/Kconfig | 21 ++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++ include/spi.h | 5 +- 14 files changed, 559 insertions(+), 2 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 0034a28..5ee33d8 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -487,6 +487,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 8c4b882..d0fa537 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map; u8 option;

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 5ee33d8..1e194ce 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index d0fa537..c4e1da6 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 21 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bef864f..0b408db 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,27 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+choice + prompt "BCM63xx SPI driver" + depends on ARCH_BMIPS + optional + +config BCM6348_SPI + bool "BCM6348 SPI driver" + help + Enable the BCM6348 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + +config BCM6358_SPI + bool "BCM6358 SPI driver" + help + Enable the BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding this Broadcom + SPI core. + +endchoice + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..2795a34 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM6348_SPI)$(CONFIG_BCM6358_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..d3aad70 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BCM6348_SPI) + +# define SPI_DT_ID "brcm,bcm6348-spi" + +/* SPI Command register */ +# define SPI_CMD_REG 0x00 + +/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x02 +# define SPI_IR_MASK_REG 0x04 + +/* SPI Clock register */ +# define SPI_CLK_REG 0x06 + +/* SPI Fill register */ +# define SPI_FILL_REG 0x07 + +/* SPI Control register (8 bit) */ +# define SPI_CTL_REG 0x40 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3f << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 6 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT) + +# define bcm63xx_spi_wctl(v,a) writeb_be(v, a); + +/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x41 +# define SPI_TX_DATA_SIZE 0x3f + +/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x80 +# define SPI_RX_DATA_SIZE 0x3f + +#elif defined(CONFIG_BCM6358_SPI) + +# define SPI_DT_ID "brcm,bcm6358-spi" + +/* SPI Control register (16 bit) */ +# define SPI_CTL_REG 0x000 +# define SPI_CTL_BYTES_SHIFT 0 +# define SPI_CTL_BYTES_MASK (0x3ff << SPI_CTL_BYTES_SHIFT) +# define SPI_CTL_TYPE_SHIFT 14 +# define SPI_CTL_TYPE_FD_RW (0 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_W (1 << SPI_CTL_TYPE_SHIFT) +# define SPI_CTL_TYPE_HD_R (2 << SPI_CTL_TYPE_SHIFT) + +# define bcm63xx_spi_wctl(v,a) writew_be(v, a); + +/* SPI TX Data registers */ +# define SPI_TX_DATA_REG 0x002 +# define SPI_TX_DATA_SIZE 0x21e + +/* SPI RX Data registers */ +# define SPI_RX_DATA_REG 0x400 +# define SPI_RX_DATA_SIZE 0x220 + +/* SPI Command register */ +# define SPI_CMD_REG 0x700 + +/* SPI Interrupt registers */ +# define SPI_IR_STAT_REG 0x702 +# define SPI_IR_MASK_REG 0x704 + +/* SPI Clock register */ +# define SPI_CLK_REG 0x706 + +/* SPI Fill register */ +# define SPI_FILL_REG 0x707 + +#endif + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +struct bcm63xx_spi_priv { + void __iomem *regs; + uint8_t num_cs; + size_t tx_bytes; +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + error("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->regs + SPI_CLK_REG, SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->regs + SPI_CLK_REG, + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->regs + SPI_IR_MASK_REG); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + error("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > SPI_RX_DATA_SIZE) { + error("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > SPI_TX_DATA_SIZE) { + error("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->regs + SPI_TX_DATA_REG + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val = 0; + uint8_t irq; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val |= (priv->tx_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_HD_W; + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val |= (data_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_FD_RW; + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val |= (data_bytes << SPI_CTL_BYTES_SHIFT); + val |= SPI_CTL_TYPE_HD_R; + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + error("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + bcm63xx_spi_wctl(val, priv->regs + SPI_CTL_REG); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->regs + SPI_IR_STAT_REG); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + val |= SPI_CMD_3WIRE_MASK; + writew_be(val, priv->regs + SPI_CMD_REG); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->regs + SPI_IR_MASK_REG); + + do { + /* read interupts */ + irq = readb_be(priv->regs + SPI_IR_STAT_REG); + + /* transfer completed */ + if (irq & SPI_IR_DONE_MASK) + break; + } while (1); + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->regs + SPI_RX_DATA_REG, + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { .compatible = SPI_DT_ID, }, + { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + error("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = SPI_RX_DATA_SIZE; + slave->max_write_size = SPI_TX_DATA_SIZE; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + clk_enable(&clk); + clk_free(&clk); + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + reset_deassert(&rst_ctl); + reset_free(&rst_ctl); + + /* initialize hardware */ + writeb_be(0, priv->regs + SPI_IR_MASK_REG); + + /* set fill register */ + writeb_be(0xff, priv->regs + SPI_FILL_REG); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 21 +++ drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..0cab44c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)

On Sat, Jun 3, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..0cab44c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
aliases {
spi0 = &spi;
};
cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>;
@@ -109,6 +113,19 @@ status = "disabled"; };
spi: spi@fffe0c00 {
compatible = "brcm,bcm6348-spi";
reg = <0xfffe0c00 0xc0>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&periph_clk BCM6338_CLK_SPI>;
resets = <&periph_rst BCM6338_RST_SPI>;
spi-max-frequency = <20000000>;
num-cs = <4>;
space?
status = "disabled";
};
As per dts(or with new node) additions/update we generally rely with Linux for maximum possible extent So can you try to add this in Linux first so-that we can have proper sync.
thanks!

El 7/6/17 a las 10:00, Jagan Teki escribió:
On Sat, Jun 3, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..0cab44c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
aliases {
spi0 = &spi;
};
cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>;
@@ -109,6 +113,19 @@ status = "disabled"; };
spi: spi@fffe0c00 {
compatible = "brcm,bcm6348-spi";
reg = <0xfffe0c00 0xc0>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&periph_clk BCM6338_CLK_SPI>;
resets = <&periph_rst BCM6338_RST_SPI>;
spi-max-frequency = <20000000>;
num-cs = <4>;
space?
I added that space on every disabled node for all the bmips .dtsi files... If you really want it removed I can do that, but it won't be in line with other nodes.
status = "disabled";
};
As per dts(or with new node) additions/update we generally rely with Linux for maximum possible extent So can you try to add this in Linux first so-that we can have proper sync.
Actually it's already upstream (even though there aren't any users yet): https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindi...
thanks!
Thanks.

On Wed, Jun 7, 2017 at 9:08 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
El 7/6/17 a las 10:00, Jagan Teki escribió:
On Sat, Jun 3, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..0cab44c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
aliases {
spi0 = &spi;
};
cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>;
@@ -109,6 +113,19 @@ status = "disabled"; };
spi: spi@fffe0c00 {
compatible = "brcm,bcm6348-spi";
reg = <0xfffe0c00 0xc0>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&periph_clk BCM6338_CLK_SPI>;
resets = <&periph_rst BCM6338_RST_SPI>;
spi-max-frequency = <20000000>;
num-cs = <4>;
space?
I added that space on every disabled node for all the bmips .dtsi files... If you really want it removed I can do that, but it won't be in line with other nodes.
status = "disabled";
};
As per dts(or with new node) additions/update we generally rely with Linux for maximum possible extent So can you try to add this in Linux first so-that we can have proper sync.
Actually it's already upstream (even though there aren't any users yet): https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindi...
But what about dts file, already in upstream?
thanks!

Hi Jagan,
El 07/06/2017 a las 19:30, Jagan Teki escribió:
On Wed, Jun 7, 2017 at 9:08 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
El 7/6/17 a las 10:00, Jagan Teki escribió:
On Sat, Jun 3, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..0cab44c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
aliases {
spi0 = &spi;
};
cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>;
@@ -109,6 +113,19 @@ status = "disabled"; };
spi: spi@fffe0c00 {
compatible = "brcm,bcm6348-spi";
reg = <0xfffe0c00 0xc0>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&periph_clk BCM6338_CLK_SPI>;
resets = <&periph_rst BCM6338_RST_SPI>;
spi-max-frequency = <20000000>;
num-cs = <4>;
space?
I added that space on every disabled node for all the bmips .dtsi files... If you really want it removed I can do that, but it won't be in line with other nodes.
status = "disabled";
};
As per dts(or with new node) additions/update we generally rely with Linux for maximum possible extent So can you try to add this in Linux first so-that we can have proper sync.
Actually it's already upstream (even though there aren't any users yet): https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindi...
But what about dts file, already in upstream?
Nope, these boards aren't in upstream since we use them for LEDE/OpenWrt and support has never been upstreamed. Appart from that, there are currently two targets for bcm63xx -> brcm63xx and bmips, and every driver is being adapted to device tree slowly (adding support for bmips too).
thanks!
Thanks.

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643..540b9fe 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf8..1662783 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb..f83a6ea 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96b..6e3d9c3 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5..dd0e5b8 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index 8e89c15..3457116 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -3,6 +3,7 @@ CONFIG_BAUDRATE=115200 CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM6348_SPI=y CONFIG_BMIPS_BOOT_RAM=y CONFIG_BOARD_SAGEM_FAST1704=y # CONFIG_CMD_BOOTD is not set @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="sagem,f@st1704" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_GPIO=y @@ -45,6 +50,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM6338=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7..5f85c73 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index a6eff10..da2426b 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -3,6 +3,7 @@ CONFIG_BAUDRATE=115200 CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM6358_SPI=y CONFIG_BMIPS_BOOT_RAM=y CONFIG_BOARD_NETGEAR_CG3100D=y # CONFIG_CMD_BOOTD is not set @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="netgear,cg3100d" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_BCM6328=y @@ -47,6 +52,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM3380=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

Am 03.06.2017 um 11:57 schrieb Álvaro Fernández Rojas:
It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (10): drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/mips/dts/brcm,bcm3380.dtsi | 17 ++ arch/mips/dts/brcm,bcm63268.dtsi | 17 ++ arch/mips/dts/brcm,bcm6338.dtsi | 17 ++ arch/mips/dts/brcm,bcm6348.dtsi | 17 ++ arch/mips/dts/brcm,bcm6358.dtsi | 17 ++ arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/mtd/spi/spi_flash.c | 5 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 419 ++++++++++++++++++++++++++++++++++ include/spi.h | 5 +- 14 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 0034a28..5ee33d8 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -487,6 +487,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 8c4b882..d0fa537 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map; u8 option;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 5ee33d8..1e194ce 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index d0fa537..c4e1da6 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 419 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bef864f..6da78bd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..260ba06 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..e1629fa --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +# define SPI_CTL_TYPE_FD_RW 0 +# define SPI_CTL_TYPE_HD_W 1 +# define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +struct bcm63xx_spi_hw { + /* SPI Clock register */ + uint16_t clk; + + /* SPI Command register */ + uint16_t cmd; + + /* SPI Control register */ + uint16_t ctl; + uint8_t ctl_shift; + + /* SPI Fill register */ + uint16_t fill; + + /* SPI Interrupt registers */ + uint16_t ir_stat; + uint16_t ir_mask; + + /* SPI RX Data registers */ + uint16_t rx; + uint16_t rx_size; + + /* SPI TX Data registers */ + uint16_t tx; + uint16_t tx_size; +}; + +struct bcm63xx_spi_priv { + const struct bcm63xx_spi_hw *hw; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + error("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const struct bcm63xx_spi_hw *hw = priv->hw; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const struct bcm63xx_spi_hw *hw = priv->hw; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + hw->clk, + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const struct bcm63xx_spi_hw *hw = priv->hw; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + hw->ir_mask); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + error("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > hw->rx_size) { + error("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > hw->tx_size) { + error("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + hw->tx + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val; + uint8_t irq; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << hw->ctl_shift); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << hw->ctl_shift); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << hw->ctl_shift); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + error("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (hw->ctl_shift >= 8) + writew_be(val, priv->base + hw->ctl); + else + writeb_be(val, priv->base + hw->ctl); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + hw->ir_stat); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + val |= SPI_CMD_3WIRE_MASK; + writew_be(val, priv->base + hw->cmd); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + hw->ir_mask); + + do { + /* read interupts */ + irq = readb_be(priv->base + hw->ir_stat); + + /* transfer completed */ + if (irq & SPI_IR_DONE_MASK) + break; + } while (1); + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + hw->rx, + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6348 = { + .clk = 0x06, + .cmd = 0x00, + .ctl = 0x40, + .ctl_shift = 6, + .fill = 0x07, + .ir_stat = 0x02, + .ir_mask = 0x04, + .rx = 0x80, + .rx_size = 0x3f, + .tx = 0x41, + .tx_size = 0x3f, +}; + +static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6358 = { + .clk = 0x706, + .cmd = 0x700, + .ctl = 0x000, + .ctl_shift = 14, + .fill = 0x707, + .ir_stat = 0x702, + .ir_mask = 0x704, + .rx = 0x400, + .rx_size = 0x220, + .tx = 0x002, + .tx_size = 0x21e, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm63xx_spi_bcm6348, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm63xx_spi_bcm6358, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const struct bcm63xx_spi_hw *hw = priv->hw; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + error("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = hw->rx_size; + slave->max_write_size = hw->tx_size; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const struct bcm63xx_spi_hw *hw = + (const struct bcm63xx_spi_hw *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->hw = hw; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + hw->ir_mask); + + /* set fill register */ + writeb_be(0xff, priv->base + hw->fill); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

On Wed, Jun 14, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
- Also check clock and reset returns as suggested by Simon Glass for HSSPI.
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 419 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bef864f..6da78bd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
help
Enable the BCM6348/BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding these Broadcom
SPI cores.
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..260ba06 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
Thanks for doing this change.
obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..e1629fa --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,419 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Control register */ +# define SPI_CTL_TYPE_FD_RW 0 +# define SPI_CTL_TYPE_HD_W 1 +# define SPI_CTL_TYPE_HD_R 2
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+struct bcm63xx_spi_hw {
/* SPI Clock register */
uint16_t clk;
/* SPI Command register */
uint16_t cmd;
/* SPI Control register */
uint16_t ctl;
uint8_t ctl_shift;
/* SPI Fill register */
uint16_t fill;
/* SPI Interrupt registers */
uint16_t ir_stat;
uint16_t ir_mask;
/* SPI RX Data registers */
uint16_t rx;
uint16_t rx_size;
/* SPI TX Data registers */
uint16_t tx;
uint16_t tx_size;
+};
+struct bcm63xx_spi_priv {
const struct bcm63xx_spi_hw *hw;
void __iomem *base;
size_t tx_bytes;
uint8_t num_cs;
+};
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const struct bcm63xx_spi_hw *hw = priv->hw;
if (mode & SPI_LSB_FIRST)
setbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const struct bcm63xx_spi_hw *hw = priv->hw;
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->base + hw->clk,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
const struct bcm63xx_spi_hw *hw = priv->hw;
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->base + hw->ir_mask);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > hw->rx_size) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > hw->tx_size) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->base + hw->tx + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val = priv->tx_bytes;
val |= (SPI_CTL_TYPE_HD_W << hw->ctl_shift);
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val = data_bytes;
val |= (SPI_CTL_TYPE_FD_RW << hw->ctl_shift);
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val = data_bytes;
val |= (SPI_CTL_TYPE_HD_R << hw->ctl_shift);
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
if (hw->ctl_shift >= 8)
writew_be(val, priv->base + hw->ctl);
else
writeb_be(val, priv->base + hw->ctl);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->base + hw->ir_stat);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->base + hw->cmd);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->base + hw->ir_mask);
do {
/* read interupts */
irq = readb_be(priv->base + hw->ir_stat);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->base + hw->rx,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6348 = {
.clk = 0x06,
.cmd = 0x00,
.ctl = 0x40,
.ctl_shift = 6,
.fill = 0x07,
.ir_stat = 0x02,
.ir_mask = 0x04,
.rx = 0x80,
.rx_size = 0x3f,
.tx = 0x41,
.tx_size = 0x3f,
+};
Define these hexcodes into macros like SPI_6348_CLK_CFG
+static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6358 = {
.clk = 0x706,
.cmd = 0x700,
.ctl = 0x000,
.ctl_shift = 14,
.fill = 0x707,
.ir_stat = 0x702,
.ir_mask = 0x704,
.rx = 0x400,
.rx_size = 0x220,
.tx = 0x002,
.tx_size = 0x21e,
+};
Define these hexcodes into macros like SPI_6358_CLK_CFG
+static const struct udevice_id bcm63xx_spi_ids[] = {
{
.compatible = "brcm,bcm6348-spi",
.data = (ulong)&bcm63xx_spi_bcm6348,
}, {
.compatible = "brcm,bcm6358-spi",
.data = (ulong)&bcm63xx_spi_bcm6358,
}, { /* sentinel */ }
+};
I think this can even write more like long value instead making it in structure, so-that rotating the 'struct bcm63xx_spi_hw' to respective functions can avoid. like definig members in enum and use these as an offset along with specific reg_space while doing io ops.
thanks!

Hi Jagan,
El 15/6/17 a las 7:38, Jagan Teki escribió:
On Wed, Jun 14, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
- Also check clock and reset returns as suggested by Simon Glass for HSSPI.
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 419 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bef864f..6da78bd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
help
Enable the BCM6348/BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding these Broadcom
SPI cores.
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..260ba06 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
Thanks for doing this change.
That's what you requested ;).
obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..e1629fa --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,419 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Control register */ +# define SPI_CTL_TYPE_FD_RW 0 +# define SPI_CTL_TYPE_HD_W 1 +# define SPI_CTL_TYPE_HD_R 2
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+struct bcm63xx_spi_hw {
/* SPI Clock register */
uint16_t clk;
/* SPI Command register */
uint16_t cmd;
/* SPI Control register */
uint16_t ctl;
uint8_t ctl_shift;
/* SPI Fill register */
uint16_t fill;
/* SPI Interrupt registers */
uint16_t ir_stat;
uint16_t ir_mask;
/* SPI RX Data registers */
uint16_t rx;
uint16_t rx_size;
/* SPI TX Data registers */
uint16_t tx;
uint16_t tx_size;
+};
+struct bcm63xx_spi_priv {
const struct bcm63xx_spi_hw *hw;
void __iomem *base;
size_t tx_bytes;
uint8_t num_cs;
+};
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const struct bcm63xx_spi_hw *hw = priv->hw;
if (mode & SPI_LSB_FIRST)
setbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const struct bcm63xx_spi_hw *hw = priv->hw;
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->base + hw->clk,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
const struct bcm63xx_spi_hw *hw = priv->hw;
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->base + hw->ir_mask);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > hw->rx_size) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > hw->tx_size) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->base + hw->tx + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val = priv->tx_bytes;
val |= (SPI_CTL_TYPE_HD_W << hw->ctl_shift);
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val = data_bytes;
val |= (SPI_CTL_TYPE_FD_RW << hw->ctl_shift);
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val = data_bytes;
val |= (SPI_CTL_TYPE_HD_R << hw->ctl_shift);
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
if (hw->ctl_shift >= 8)
writew_be(val, priv->base + hw->ctl);
else
writeb_be(val, priv->base + hw->ctl);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->base + hw->ir_stat);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->base + hw->cmd);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->base + hw->ir_mask);
do {
/* read interupts */
irq = readb_be(priv->base + hw->ir_stat);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->base + hw->rx,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6348 = {
.clk = 0x06,
.cmd = 0x00,
.ctl = 0x40,
.ctl_shift = 6,
.fill = 0x07,
.ir_stat = 0x02,
.ir_mask = 0x04,
.rx = 0x80,
.rx_size = 0x3f,
.tx = 0x41,
.tx_size = 0x3f,
+};
Define these hexcodes into macros like SPI_6348_CLK_CFG
Ok, I will do that.
+static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6358 = {
.clk = 0x706,
.cmd = 0x700,
.ctl = 0x000,
.ctl_shift = 14,
.fill = 0x707,
.ir_stat = 0x702,
.ir_mask = 0x704,
.rx = 0x400,
.rx_size = 0x220,
.tx = 0x002,
.tx_size = 0x21e,
+};
Define these hexcodes into macros like SPI_6358_CLK_CFG
Ok, I will do that.
+static const struct udevice_id bcm63xx_spi_ids[] = {
{
.compatible = "brcm,bcm6348-spi",
.data = (ulong)&bcm63xx_spi_bcm6348,
}, {
.compatible = "brcm,bcm6358-spi",
.data = (ulong)&bcm63xx_spi_bcm6358,
}, { /* sentinel */ }
+};
I think this can even write more like long value instead making it in structure, so-that rotating the 'struct bcm63xx_spi_hw' to respective functions can avoid. like definig members in enum and use these as an offset along with specific reg_space while doing io ops.
I don't think I'm following, can you provide an example of how you want it done? Maybe a driver that is doing that in u-boot or something similar...
thanks!
Thanks!

On Thu, Jun 15, 2017 at 2:54 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Hi Jagan,
El 15/6/17 a las 7:38, Jagan Teki escribió:
On Wed, Jun 14, 2017 at 3:27 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c Instead of supporting both HW revisions of the controller in a single build, support has been split by the selected config to save space.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
- Also check clock and reset returns as suggested by Simon Glass for HSSPI.
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 419 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bef864f..6da78bd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT32 (AVR32) and AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
help
Enable the BCM6348/BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding these Broadcom
SPI cores.
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c090562..260ba06 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
Thanks for doing this change.
That's what you requested ;).
obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000..e1629fa --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,419 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Control register */ +# define SPI_CTL_TYPE_FD_RW 0 +# define SPI_CTL_TYPE_HD_W 1 +# define SPI_CTL_TYPE_HD_R 2
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+struct bcm63xx_spi_hw {
/* SPI Clock register */
uint16_t clk;
/* SPI Command register */
uint16_t cmd;
/* SPI Control register */
uint16_t ctl;
uint8_t ctl_shift;
/* SPI Fill register */
uint16_t fill;
/* SPI Interrupt registers */
uint16_t ir_stat;
uint16_t ir_mask;
/* SPI RX Data registers */
uint16_t rx;
uint16_t rx_size;
/* SPI TX Data registers */
uint16_t tx;
uint16_t tx_size;
+};
+struct bcm63xx_spi_priv {
const struct bcm63xx_spi_hw *hw;
void __iomem *base;
size_t tx_bytes;
uint8_t num_cs;
+};
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const struct bcm63xx_spi_hw *hw = priv->hw;
if (mode & SPI_LSB_FIRST)
setbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->base + hw->clk, SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const struct bcm63xx_spi_hw *hw = priv->hw;
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->base + hw->clk,
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
const struct bcm63xx_spi_hw *hw = priv->hw;
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->base + hw->ir_mask);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > hw->rx_size) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > hw->tx_size) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->base + hw->tx + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val = priv->tx_bytes;
val |= (SPI_CTL_TYPE_HD_W << hw->ctl_shift);
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val = data_bytes;
val |= (SPI_CTL_TYPE_FD_RW << hw->ctl_shift);
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val = data_bytes;
val |= (SPI_CTL_TYPE_HD_R << hw->ctl_shift);
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
if (hw->ctl_shift >= 8)
writew_be(val, priv->base + hw->ctl);
else
writeb_be(val, priv->base + hw->ctl);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->base + hw->ir_stat);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
writew_be(val, priv->base + hw->cmd);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->base + hw->ir_mask);
do {
/* read interupts */
irq = readb_be(priv->base + hw->ir_stat);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
/* copy rx data */
if (din)
memcpy_fromio(din, priv->base + hw->rx,
data_bytes);
}
return 0;
+}
+static const struct dm_spi_ops bcm63xx_spi_ops = {
.cs_info = bcm63xx_spi_cs_info,
.set_mode = bcm63xx_spi_set_mode,
.set_speed = bcm63xx_spi_set_speed,
.xfer = bcm63xx_spi_xfer,
+};
+static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6348 = {
.clk = 0x06,
.cmd = 0x00,
.ctl = 0x40,
.ctl_shift = 6,
.fill = 0x07,
.ir_stat = 0x02,
.ir_mask = 0x04,
.rx = 0x80,
.rx_size = 0x3f,
.tx = 0x41,
.tx_size = 0x3f,
+};
Define these hexcodes into macros like SPI_6348_CLK_CFG
Ok, I will do that.
+static const struct bcm63xx_spi_hw bcm63xx_spi_bcm6358 = {
.clk = 0x706,
.cmd = 0x700,
.ctl = 0x000,
.ctl_shift = 14,
.fill = 0x707,
.ir_stat = 0x702,
.ir_mask = 0x704,
.rx = 0x400,
.rx_size = 0x220,
.tx = 0x002,
.tx_size = 0x21e,
+};
Define these hexcodes into macros like SPI_6358_CLK_CFG
Ok, I will do that.
+static const struct udevice_id bcm63xx_spi_ids[] = {
{
.compatible = "brcm,bcm6348-spi",
.data = (ulong)&bcm63xx_spi_bcm6348,
}, {
.compatible = "brcm,bcm6358-spi",
.data = (ulong)&bcm63xx_spi_bcm6358,
}, { /* sentinel */ }
+};
I think this can even write more like long value instead making it in structure, so-that rotating the 'struct bcm63xx_spi_hw' to respective functions can avoid. like definig members in enum and use these as an offset along with specific reg_space while doing io ops.
I don't think I'm following, can you provide an example of how you want it done? Maybe a driver that is doing that in u-boot or something similar...
static const unsigned log bcm63xx_spi_bcm6348 = { [SPI_CMD] = SPI_6348_CMD, [SPI_INT_STATUS] = SPI_6348_INT_STATUS, ....... ..... };
*_probe(struct udevice *dev) { const unsigned long *bcm63xx_spireg;
/* need to assign the probed data to bcm63xx_spireg */ }
For more infor this mught look simialr to Linux drivers/spi/spi-bcm63xx.c
thanks!

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a43..0cab44c 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643..540b9fe 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf8..1662783 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb..f83a6ea 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96b..6e3d9c3 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5..dd0e5b8 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index 8e89c15..b6f9b74 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -3,6 +3,7 @@ CONFIG_BAUDRATE=115200 CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y CONFIG_BMIPS_BOOT_RAM=y CONFIG_BOARD_SAGEM_FAST1704=y # CONFIG_CMD_BOOTD is not set @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="sagem,f@st1704" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_GPIO=y @@ -45,6 +50,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM6338=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7..5f85c73 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index a6eff10..dd6044e 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -3,6 +3,7 @@ CONFIG_BAUDRATE=115200 CONFIG_BCM6345_CLK=y CONFIG_BCM6345_GPIO=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y CONFIG_BMIPS_BOOT_RAM=y CONFIG_BOARD_NETGEAR_CG3100D=y # CONFIG_CMD_BOOTD is not set @@ -27,6 +28,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_XIMG is not set CONFIG_DEFAULT_DEVICE_TREE="netgear,cg3100d" CONFIG_DISPLAY_CPUINFO=y @@ -34,6 +37,8 @@ CONFIG_DISPLAY_CPUINFO=y CONFIG_DM_GPIO=y CONFIG_DM_RESET=y CONFIG_DM_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_LED=y CONFIG_LED_BCM6328=y @@ -47,6 +52,9 @@ CONFIG_OF_STDOUT_VIA_ALIAS=y CONFIG_RESET=y CONFIG_RESET_BCM6345=y CONFIG_SOC_BMIPS_BCM3380=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y # CONFIG_SPL_SERIAL_PRESENT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_NO_FLASH=y

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v5: Introduce changes suggested by Jagan Teki: - Use long structs for registers v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (10): drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/mips/dts/brcm,bcm3380.dtsi | 17 ++ arch/mips/dts/brcm,bcm63268.dtsi | 17 ++ arch/mips/dts/brcm,bcm6338.dtsi | 17 ++ arch/mips/dts/brcm,bcm6348.dtsi | 17 ++ arch/mips/dts/brcm,bcm6358.dtsi | 17 ++ arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/mtd/spi/spi_flash.c | 5 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 434 ++++++++++++++++++++++++++++++++++ include/spi.h | 5 +- 14 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 0034a28d5f..5ee33d8bb2 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -487,6 +487,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 8c4b882c54..d0fa5379ea 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map; u8 option;

On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com
thanks!

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 5ee33d8bb2..1e194cedd8 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -380,7 +380,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index d0fa5379ea..c4e1da68ab 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com
thanks!

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: Introduce changes suggested by Jagan Teki: - Use long structure instead of a custom bmips_spi_hw structure. - Define constants for each SPI core. v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8a8e8e480f..511643607b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9f8b86de76..d9802dd8c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..904db2b7c7 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + error("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + error("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + error("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + error("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val; + uint8_t irq; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + error("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + val = SPI_CMD_OP_START; + val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + val |= SPI_CMD_3WIRE_MASK; + writew_be(val, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + do { + /* read interupts */ + irq = readb_be(priv->base + regs[SPI_IR_STAT]); + + /* transfer completed */ + if (irq & SPI_IR_DONE_MASK) + break; + } while (1); + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + error("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v5: Introduce changes suggested by Jagan Teki:
- Use long structure instead of a custom bmips_spi_hw structure.
- Define constants for each SPI core.
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
- Also check clock and reset returns as suggested by Simon Glass for HSSPI.
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8a8e8e480f..511643607b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
help
Enable the BCM6348/BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding these Broadcom
SPI cores.
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9f8b86de76..d9802dd8c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..904db2b7c7 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,434 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f
+/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+enum bcm63xx_regs_spi {
SPI_CLK,
SPI_CMD,
SPI_CTL,
SPI_CTL_SHIFT,
SPI_FILL,
SPI_IR_MASK,
SPI_IR_STAT,
SPI_RX,
SPI_RX_SIZE,
SPI_TX,
SPI_TX_SIZE,
+};
+struct bcm63xx_spi_priv {
const unsigned long *regs;
void __iomem *base;
size_t tx_bytes;
uint8_t num_cs;
+};
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const unsigned long *regs = priv->regs;
if (mode & SPI_LSB_FIRST)
setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const unsigned long *regs = priv->regs;
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->base + regs[SPI_CLK],
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
const unsigned long *regs = priv->regs;
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->base + regs[SPI_IR_MASK]);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > regs[SPI_RX_SIZE]) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val = priv->tx_bytes;
val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]);
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val = data_bytes;
val |= (SPI_CTL_TYPE_FD_RW <<
regs[SPI_CTL_SHIFT]);
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val = data_bytes;
val |= (SPI_CTL_TYPE_HD_R <<
regs[SPI_CTL_SHIFT]);
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
if (regs[SPI_CTL_SHIFT] >= 8)
writew_be(val, priv->base + regs[SPI_CTL]);
else
writeb_be(val, priv->base + regs[SPI_CTL]);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
Please use cmd instead of val to avoid confusion for previous register reads use same val. And for issue the transfer why do we need to update the mode here? usually transfer will be initiate with OP_START, CS and prepend length right? and modes are usually altered during .set_mode.
writew_be(val, priv->base + regs[SPI_CMD]);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]);
do {
/* read interupts */
irq = readb_be(priv->base + regs[SPI_IR_STAT]);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
This shouldn't be an infinite, try to use wait_for_bit with necessary timeout.
thanks!

On Thu, Aug 10, 2017 at 2:55 PM, Jagan Teki jagannadh.teki@gmail.com wrote:
On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v5: Introduce changes suggested by Jagan Teki:
- Use long structure instead of a custom bmips_spi_hw structure.
- Define constants for each SPI core.
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
- Also check clock and reset returns as suggested by Simon Glass for HSSPI.
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8a8e8e480f..511643607b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
help
Enable the BCM6348/BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding these Broadcom
SPI cores.
config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9f8b86de76..d9802dd8c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..904db2b7c7 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,434 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f
+/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+enum bcm63xx_regs_spi {
SPI_CLK,
SPI_CMD,
SPI_CTL,
SPI_CTL_SHIFT,
SPI_FILL,
SPI_IR_MASK,
SPI_IR_STAT,
SPI_RX,
SPI_RX_SIZE,
SPI_TX,
SPI_TX_SIZE,
+};
+struct bcm63xx_spi_priv {
const unsigned long *regs;
void __iomem *base;
size_t tx_bytes;
uint8_t num_cs;
+};
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const unsigned long *regs = priv->regs;
if (mode & SPI_LSB_FIRST)
setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const unsigned long *regs = priv->regs;
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->base + regs[SPI_CLK],
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
const unsigned long *regs = priv->regs;
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->base + regs[SPI_IR_MASK]);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > regs[SPI_RX_SIZE]) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val = priv->tx_bytes;
val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]);
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val = data_bytes;
val |= (SPI_CTL_TYPE_FD_RW <<
regs[SPI_CTL_SHIFT]);
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val = data_bytes;
val |= (SPI_CTL_TYPE_HD_R <<
regs[SPI_CTL_SHIFT]);
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
if (regs[SPI_CTL_SHIFT] >= 8)
writew_be(val, priv->base + regs[SPI_CTL]);
else
writeb_be(val, priv->base + regs[SPI_CTL]);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
Please use cmd instead of val to avoid confusion for previous register reads use same val. And for issue the transfer why do we need to update the mode here? usually transfer will be initiate with OP_START, CS and prepend length right? and modes are usually altered during .set_mode.
writew_be(val, priv->base + regs[SPI_CMD]);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]);
do {
/* read interupts */
irq = readb_be(priv->base + regs[SPI_IR_STAT]);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
This shouldn't be an infinite, try to use wait_for_bit with necessary timeout.
Except this patch, all look OK on this series.
thanks!

Hello Jagan,
El 10/08/2017 a las 11:25, Jagan Teki escribió:
On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
v5: Introduce changes suggested by Jagan Teki:
- Use long structure instead of a custom bmips_spi_hw structure.
- Define constants for each SPI core.
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
- Also check clock and reset returns as suggested by Simon Glass for HSSPI.
v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8a8e8e480f..511643607b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS
help
Enable the BCM6348/BCM6358 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding these Broadcom
SPI cores.
- config CADENCE_QSPI bool "Cadence QSPI driver" help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9f8b86de76..d9802dd8c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..904db2b7c7 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,434 @@ +/*
- Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com
- Derived from linux/drivers/spi/spi-bcm63xx.c:
Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f
+/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e
+/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT)
+/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT)
+/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2
+/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\
SPI_IR_RXOVER_MASK |\
SPI_IR_TXUNDER_MASK |\
SPI_IR_TXOVER_MASK |\
SPI_IR_RXUNDER_MASK)
+enum bcm63xx_regs_spi {
SPI_CLK,
SPI_CMD,
SPI_CTL,
SPI_CTL_SHIFT,
SPI_FILL,
SPI_IR_MASK,
SPI_IR_STAT,
SPI_RX,
SPI_RX_SIZE,
SPI_TX,
SPI_TX_SIZE,
+};
+struct bcm63xx_spi_priv {
const unsigned long *regs;
void __iomem *base;
size_t tx_bytes;
uint8_t num_cs;
+};
+#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = {
{ 25000000, SPI_CLK_25MHZ },
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
+};
+static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
error("no cs %u\n", cs);
return -ENODEV;
}
return 0;
+}
+static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const unsigned long *regs = priv->regs;
if (mode & SPI_LSB_FIRST)
setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK);
else
clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK);
return 0;
+}
+static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{
struct bcm63xx_spi_priv *priv = dev_get_priv(bus);
const unsigned long *regs = priv->regs;
uint8_t clk_cfg;
int i;
/* default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* find the closest clock configuration */
for (i = 0; i < SPI_CLK_CNT; i++) {
if (speed >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* write clock configuration */
clrsetbits_8(priv->base + regs[SPI_CLK],
SPI_CLK_SSOFF_MASK | SPI_CLK_MASK,
clk_cfg | SPI_CLK_SSOFF_2);
return 0;
+}
+/*
- BCM63xx SPI driver doesn't allow keeping CS active between transfers since
- they are HW controlled.
- However, it provides a mechanism to prepend write transfers prior to read
- transfers (with a maximum prepend of 15 bytes), which is usually enough for
- SPI-connected flashes since reading requires prepending a write transfer of
- 5 bytes.
- This implementation takes advantage of the prepend mechanism and combines
- multiple transfers into a single one where possible (single/multiple write
- transfer(s) followed by a final read/write transfer).
- However, it's not possible to buffer reads, which means that read transfers
- should always be done as the final ones.
- On the other hand, take into account that combining write transfers into
- a single one is just buffering and doesn't require prepend mechanism.
- */
+static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
+{
struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent);
const unsigned long *regs = priv->regs;
size_t data_bytes = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->tx_bytes = 0;
/* initialize hardware */
writeb_be(0, priv->base + regs[SPI_IR_MASK]);
}
if (din) {
/* buffering reads not possible since cs is hw controlled */
if (!(flags & SPI_XFER_END)) {
error("unable to buffer reads\n");
return -EINVAL;
}
/* check rx size */
if (data_bytes > regs[SPI_RX_SIZE]) {
error("max rx bytes exceeded\n");
return -EMSGSIZE;
}
}
if (dout) {
/* check tx size */
if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) {
error("max tx bytes exceeded\n");
return -EMSGSIZE;
}
/* copy tx data */
memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes,
dout, data_bytes);
priv->tx_bytes += data_bytes;
}
if (flags & SPI_XFER_END) {
struct dm_spi_slave_platdata *plat =
dev_get_parent_platdata(dev);
uint16_t val;
uint8_t irq;
/* determine control config */
if (dout && !din) {
/* buffered write transfers */
val = priv->tx_bytes;
val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]);
priv->tx_bytes = 0;
} else {
if (dout && din && (flags & SPI_XFER_ONCE)) {
/* full duplex read/write */
val = data_bytes;
val |= (SPI_CTL_TYPE_FD_RW <<
regs[SPI_CTL_SHIFT]);
priv->tx_bytes = 0;
} else {
/* prepended write transfer */
val = data_bytes;
val |= (SPI_CTL_TYPE_HD_R <<
regs[SPI_CTL_SHIFT]);
if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) {
error("max prepend bytes exceeded\n");
return -EMSGSIZE;
}
}
}
if (regs[SPI_CTL_SHIFT] >= 8)
writew_be(val, priv->base + regs[SPI_CTL]);
else
writeb_be(val, priv->base + regs[SPI_CTL]);
/* clear interrupts */
writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
val |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
val |= SPI_CMD_3WIRE_MASK;
Please use cmd instead of val to avoid confusion for previous register reads use same val.
OK
And for issue the transfer why do we need to update the mode here? usually transfer will be initiate with OP_START, CS and prepend length right? and modes are usually altered during .set_mode.
Because 3 wire mode needs to be set on every issued command. Anyway, I haven't tested this mode, so if you don't want mode altered here I can remove it completely.
writew_be(val, priv->base + regs[SPI_CMD]);
/* enable interrupts */
writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]);
do {
/* read interupts */
irq = readb_be(priv->base + regs[SPI_IR_STAT]);
/* transfer completed */
if (irq & SPI_IR_DONE_MASK)
break;
} while (1);
This shouldn't be an infinite, try to use wait_for_bit with necessary timeout.
I can't use wait_for_bit because reading that address as a 32 bit register causes an exception. However, I could add another version of wait_for_bit such as the following: https://gist.github.com/Noltari/8ab369cae5f8577255c81e16402b358e Is that acceptable?
thanks!

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a4372b..0cab44cb8d 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643b5a..540b9fea5b 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf80e0..1662783279 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb048..f83a6ea8df 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96bef8..6e3d9c3820 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index 8b5d7f4d65..55a3170aa5 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -40,3 +40,11 @@ CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7616..5f85c7346f 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index e3537943e5..1390c9e79c 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -42,3 +42,11 @@ CONFIG_RESET_BCM6345=y CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y CONFIG_WDT_BCM6345=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y

Hi,
On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v5: Introduce changes suggested by Jagan Teki:
- Use long structs for registers
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
Also move SPI aliases from .dts to .dtsi files.
Did you sent the latest changes? I couldn't find it on patchwork even with status "any"
thanks!

Hi Jagan,
Am 07.08.2017 um 10:35 schrieb Jagan Teki:
Hi,
On Sun, Jul 30, 2017 at 5:43 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v5: Introduce changes suggested by Jagan Teki:
- Use long structs for registers
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
Also move SPI aliases from .dts to .dtsi files.
Did you sent the latest changes? I couldn't find it on patchwork even with status "any"
thanks!
someone assigned all patches to me. I reassigned them to you. Please apply them, thanks.

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structs for registers v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (11): wait_bit: add big endian version of wait_for_bit function drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/mips/dts/brcm,bcm3380.dtsi | 17 ++ arch/mips/dts/brcm,bcm63268.dtsi | 17 ++ arch/mips/dts/brcm,bcm6338.dtsi | 17 ++ arch/mips/dts/brcm,bcm6348.dtsi | 17 ++ arch/mips/dts/brcm,bcm6358.dtsi | 17 ++ arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/mtd/spi/spi_flash.c | 5 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++ include/spi.h | 5 +- include/wait_bit.h | 44 ++++ 15 files changed, 619 insertions(+), 2 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v6: Introduce changes suggested by Jagan Teki: - Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..47891fa75c 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,49 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+#define BUILD_WAIT_FOR_BIT(sfx, type, read) \ + \ +static inline int wait_for_bit_##sfx(const char *prefix, \ + const u32 *reg, \ + const type mask, \ + const bool set, \ + const unsigned int timeout_ms, \ + const bool breakable) \ +{ \ + type val; \ + unsigned long start = get_timer(0); \ + \ + while (1) { \ + val = read(reg); \ + \ + if (!set) \ + val = ~val; \ + \ + if ((val & mask) == mask) \ + return 0; \ + \ + if (get_timer(start) > timeout_ms) \ + break; \ + \ + if (breakable && ctrlc()) { \ + puts("Abort\n"); \ + return -EINTR; \ + } \ + \ + udelay(1); \ + WATCHDOG_RESET(); \ + } \ + \ + debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", prefix, \ + reg, mask, set); \ + \ + return -ETIMEDOUT; \ +} + +BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
#endif

On 02.01.2018 20:01, Álvaro Fernández Rojas wrote:
Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v6: Introduce changes suggested by Jagan Teki:
- Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..47891fa75c 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,49 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+#define BUILD_WAIT_FOR_BIT(sfx, type, read) \
\
+static inline int wait_for_bit_##sfx(const char *prefix, \
const u32 *reg, \
I suggest to use 'const void *reg' for compatibility with 64 bit systems and to be consistant with the most readl() implementations
const type mask, \
const bool set, \
const unsigned int timeout_ms, \
const bool breakable) \
+{ \
- type val; \
- unsigned long start = get_timer(0); \
\
- while (1) { \
val = read(reg); \
\
if (!set) \
val = ~val; \
\
if ((val & mask) == mask) \
return 0; \
\
if (get_timer(start) > timeout_ms) \
break; \
\
if (breakable && ctrlc()) { \
puts("Abort\n"); \
return -EINTR; \
} \
\
udelay(1); \
WATCHDOG_RESET(); \
- } \
\
- debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", prefix, \
reg, mask, set); \
almost all users pass __func__ as value for the prefix argument. Since this is a macro now, we could directly use __func__ in the debug() call and omit the prefix argument
\
- return -ETIMEDOUT; \
+}
+BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
#endif

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v6: Introduce changes suggested by Jagan Teki:
- Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..47891fa75c 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,49 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+#define BUILD_WAIT_FOR_BIT(sfx, type, read) \
\
+static inline int wait_for_bit_##sfx(const char *prefix, \
const u32 *reg, \
const type mask, \
const bool set, \
const unsigned int timeout_ms, \
const bool breakable) \
+{ \
type val; \
unsigned long start = get_timer(0); \
\
while (1) { \
val = read(reg); \
\
if (!set) \
val = ~val; \
\
if ((val & mask) == mask) \
return 0; \
\
if (get_timer(start) > timeout_ms) \
break; \
\
if (breakable && ctrlc()) { \
puts("Abort\n"); \
return -EINTR; \
} \
\
udelay(1); \
WATCHDOG_RESET(); \
} \
\
debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", prefix, \
reg, mask, set); \
\
return -ETIMEDOUT; \
+}
+BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl)
look like existing wait_for_bit is of this type, better add these macros to existing code and update wait_for_bit from callers if less changes or add macro to redirect wait_for_bit_le32 but I prefer first one because this even need to update in future.

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v6: no changes v5: no changes v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 51e28bf07b..e40e1c01de 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -516,6 +516,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 08c7480fda..4787454e59 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v6: no changes v5: no changes v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e40e1c01de..294d9f9d79 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -405,7 +405,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index 4787454e59..5a7df1c706 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structure instead of a custom bmips_spi_hw structure. - Define constants for each SPI core. v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 494639fb01..ebc71c2e42 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3184db67f..5770b3f7cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..88257db728 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + printf("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + printf("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + printf("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val, cmd; + int ret; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + printf("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + cmd = SPI_CMD_OP_START; + cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + cmd |= SPI_CMD_3WIRE_MASK; + writew_be(cmd, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + ret = wait_for_bit_8(__func__, priv->base + regs[SPI_IR_STAT], + SPI_IR_DONE_MASK, true, 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a4372b..0cab44cb8d 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643b5a..540b9fea5b 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf80e0..1662783279 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb048..f83a6ea8df 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96bef8..6e3d9c3820 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index cfc56cba37..5c091353e5 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -39,3 +39,11 @@ CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com --- v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7616..5f85c7346f 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index 7665c78d3f..369c919ac7 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -41,3 +41,11 @@ CONFIG_RESET_BCM6345=y CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y CONFIG_WDT_BCM6345=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y

On Wed, Jan 3, 2018 at 12:31 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v7: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. - Remove wait_for_bit and update callers to wait_for_bit_le32. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structs for registers v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (13): wait_bit: add 8/16/32 BE/LE versions of wait_for_bit wait_bit: use wait_for_bit_le32 instead of wait_for_bit wait_bit: remove old wait_for_bit function drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/arm/mach-imx/mx6/ddr.c | 22 +- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 8 +- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +-- arch/mips/dts/brcm,bcm3380.dtsi | 17 + arch/mips/dts/brcm,bcm63268.dtsi | 17 + arch/mips/dts/brcm,bcm6338.dtsi | 17 + arch/mips/dts/brcm,bcm6348.dtsi | 17 + arch/mips/dts/brcm,bcm6358.dtsi | 17 + arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/clk/clk_pic32.c | 12 +- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 +- drivers/fpga/socfpga_arria10.c | 17 +- drivers/mmc/msm_sdhci.c | 8 +- drivers/mtd/pic32_flash.c | 4 +- drivers/mtd/spi/spi_flash.c | 5 +- drivers/net/ag7xxx.c | 16 +- drivers/net/dwc_eth_qos.c | 17 +- drivers/net/ethoc.c | 8 +- drivers/net/pic32_eth.c | 12 +- drivers/net/pic32_mdio.c | 28 +- drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 +- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/atmel_spi.c | 4 +- drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++ drivers/spi/cadence_qspi_apb.c | 14 +- drivers/spi/fsl_qspi.c | 20 +- drivers/spi/mvebu_a3700_spi.c | 20 +- drivers/usb/host/dwc2.c | 24 +- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +- drivers/usb/host/ohci-lpc32xx.c | 12 +- drivers/usb/host/xhci-rcar.c | 12 +- drivers/video/atmel_hlcdfb.c | 64 ++-- include/spi.h | 5 +- include/wait_bit.h | 81 ++--- 47 files changed, 826 insertions(+), 248 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v7: Introduce changes suggested by Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..bde6d2cfc3 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,66 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+/** + * wait_for_bit_x() waits for bit set/cleared in register + * + * Function polls register waiting for specific bit(s) change + * (either 0->1 or 1->0). It can fail under two conditions: + * - Timeout + * - User interaction (CTRL-C) + * Function succeeds only if all bits of masked register are set/cleared + * (depending on set option). + * + * @param reg Register that will be read (using read_x()) + * @param mask Bit(s) of register that must be active + * @param set Selects wait condition (bit set or clear) + * @param timeout_ms Timeout (in milliseconds) + * @param breakable Enables CTRL-C interruption + * @return 0 on success, -ETIMEDOUT or -EINTR on failure + */ + +#define BUILD_WAIT_FOR_BIT(sfx, type, read) \ + \ +static inline int wait_for_bit_##sfx(const void *reg, \ + const type mask, \ + const bool set, \ + const unsigned int timeout_ms, \ + const bool breakable) \ +{ \ + type val; \ + unsigned long start = get_timer(0); \ + \ + while (1) { \ + val = read(reg); \ + \ + if (!set) \ + val = ~val; \ + \ + if ((val & mask) == mask) \ + return 0; \ + \ + if (get_timer(start) > timeout_ms) \ + break; \ + \ + if (breakable && ctrlc()) { \ + puts("Abort\n"); \ + return -EINTR; \ + } \ + \ + udelay(1); \ + WATCHDOG_RESET(); \ + } \ + \ + debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \ + reg, mask, set); \ + \ + return -ETIMEDOUT; \ +} + +BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
#endif

On 10.01.2018 21:26, Álvaro Fernández Rojas wrote:
Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v7: Introduce changes suggested by Daniel Schwierzeck:
- Use const void* reg for compatibility with 64 bit systems.
- Remove prefix and use __func__ instead.
v6: Introduce changes suggested by Jagan Teki:
- Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com

On Thu, Jan 11, 2018 at 7:20 AM, Daniel Schwierzeck daniel.schwierzeck@gmail.com wrote:
On 10.01.2018 21:26, Álvaro Fernández Rojas wrote:
Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v7: Introduce changes suggested by Daniel Schwierzeck:
- Use const void* reg for compatibility with 64 bit systems.
- Remove prefix and use __func__ instead.
v6: Introduce changes suggested by Jagan Teki:
- Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
Reviewed-by: Jagan Teki jagan@openedev.com

wait_for_bit callers use the 32 bit LE version
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v7: Introduce changes suggested by Jagan Teki: - Remove wait_for_bit and update callers to wait_for_bit_le32.
arch/arm/mach-imx/mx6/ddr.c | 22 ++++----- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 8 ++-- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +++++++-------- arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- drivers/clk/clk_pic32.c | 12 ++--- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 ++-- drivers/fpga/socfpga_arria10.c | 17 +++---- drivers/mmc/msm_sdhci.c | 8 ++-- drivers/mtd/pic32_flash.c | 4 +- drivers/net/ag7xxx.c | 16 +++---- drivers/net/dwc_eth_qos.c | 17 +++---- drivers/net/ethoc.c | 8 ++-- drivers/net/pic32_eth.c | 12 ++--- drivers/net/pic32_mdio.c | 28 ++++++------ drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 ++--- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/cadence_qspi_apb.c | 14 +++--- drivers/spi/fsl_qspi.c | 20 ++++----- drivers/spi/mvebu_a3700_spi.c | 20 +++++---- drivers/usb/host/dwc2.c | 24 +++++----- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +-- drivers/usb/host/ohci-lpc32xx.c | 12 ++--- drivers/usb/host/xhci-rcar.c | 12 ++--- drivers/video/atmel_hlcdfb.c | 64 +++++++++++++-------------- 32 files changed, 207 insertions(+), 209 deletions(-)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 52a9a25904..f07f938c65 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -21,10 +21,10 @@ static void reset_read_data_fifos(void)
/* Reset data FIFOs twice. */ setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0);
setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); }
static void precharge_all(const bool cs0_enable, const bool cs1_enable) @@ -39,12 +39,12 @@ static void precharge_all(const bool cs0_enable, const bool cs1_enable) */ if (cs0_enable) { /* CS0 */ writel(0x04008050, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); }
if (cs1_enable) { /* CS1 */ writel(0x04008058, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); } }
@@ -146,7 +146,7 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) * 7. Upon completion of this process the MMDC de-asserts * the MPWLGCR[HW_WL_EN] */ - wait_for_bit("MMDC", &mmdc0->mpwlgcr, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwlgcr, 1 << 0, 0, 100, 0);
/* * 8. check for any errors: check both PHYs for x64 configuration, @@ -278,7 +278,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x00008028, &mmdc0->mdscr);
/* poll to make sure the con_ack bit was asserted */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0);
/* * Check MDMISC register CALIB_PER_CS to see which CS calibration @@ -312,7 +312,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * this bit until it clears to indicate completion of the write access. */ setbits_le32(&mmdc0->mpswdar0, 1); - wait_for_bit("MMDC", &mmdc0->mpswdar0, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpswdar0, 1 << 0, 0, 100, 0);
/* Set the RD_DL_ABS# bits to their default values * (will be calibrated later in the read delay-line calibration). @@ -359,7 +359,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) setbits_le32(&mmdc0->mpdgctrl0, 5 << 28);
/* Poll for completion. MPDGCTRL0[HW_DG_EN] should be 0 */ - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0);
/* * Check to see if any errors were encountered during calibration @@ -423,7 +423,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * setting MPRDDLHWCTL[HW_RD_DL_EN] = 0. Also, ensure that * no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0);
/* check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mprddlhwctl) & 0x0000000f) @@ -477,7 +477,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * by setting MPWRDLHWCTL[HW_WR_DL_EN] = 0. * Also, ensure that no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0);
/* Check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mpwrdlhwctl) & 0x0000000f) @@ -526,7 +526,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x0, &mmdc0->mdscr); /* CS0 */
/* Poll to make sure the con_ack bit is clear */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 0, 100, 0);
/* * Print out the registers that were updated as a result diff --git a/arch/arm/mach-socfpga/clock_manager.c b/arch/arm/mach-socfpga/clock_manager.c index cb6ae03696..42de783217 100644 --- a/arch/arm/mach-socfpga/clock_manager.c +++ b/arch/arm/mach-socfpga/clock_manager.c @@ -37,8 +37,8 @@ void cm_wait_for_lock(u32 mask) /* function to poll in the fsm busy bit */ int cm_wait_for_fsm(void) { - return wait_for_bit(__func__, (const u32 *)&clock_manager_base->stat, - CLKMGR_STAT_BUSY, false, 20000, false); + return wait_for_bit_le32((const u32 *)&clock_manager_base->stat, + CLKMGR_STAT_BUSY, false, 20000, false); }
int set_cpu_clk_info(void) diff --git a/arch/arm/mach-socfpga/clock_manager_gen5.c b/arch/arm/mach-socfpga/clock_manager_gen5.c index 31fd51097a..309ddde0d0 100644 --- a/arch/arm/mach-socfpga/clock_manager_gen5.c +++ b/arch/arm/mach-socfpga/clock_manager_gen5.c @@ -37,15 +37,15 @@ static int cm_write_with_phase(u32 value, u32 reg_address, u32 mask) int ret;
/* poll until phase is zero */ - ret = wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + ret = wait_for_bit_le32((const u32 *)reg_address, mask, + false, 20000, false); if (ret) return ret;
writel(value, reg_address);
- return wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + return wait_for_bit_le32((const u32 *)reg_address, mask, + false, 20000, false); }
/* diff --git a/arch/arm/mach-socfpga/reset_manager_arria10.c b/arch/arm/mach-socfpga/reset_manager_arria10.c index ae16897494..54f0ddb255 100644 --- a/arch/arm/mach-socfpga/reset_manager_arria10.c +++ b/arch/arm/mach-socfpga/reset_manager_arria10.c @@ -222,8 +222,8 @@ int socfpga_reset_deassert_bridges_handoff(void) clrbits_le32(&reset_manager_base->brgmodrst, mask_rstmgr);
/* Poll until all idleack to 0, timeout at 1000ms */ - return wait_for_bit(__func__, &sysmgr_regs->noc_idleack, mask_noc, - false, 1000, false); + return wait_for_bit_le32(&sysmgr_regs->noc_idleack, mask_noc, + false, 1000, false); }
void socfpga_reset_assert_fpga_connected_peripherals(void) @@ -343,26 +343,26 @@ int socfpga_bridges_reset(void) writel(ALT_SYSMGR_NOC_TMO_EN_SET_MSK, &sysmgr_regs->noc_timeout);
/* Poll until all idleack to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idleack, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idleack, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
/* Poll until all idlestatus to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idlestatus, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idlestatus, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
diff --git a/arch/mips/mach-ath79/ar934x/clk.c b/arch/mips/mach-ath79/ar934x/clk.c index 9b41d3de60..ba2243c9be 100644 --- a/arch/mips/mach-ath79/ar934x/clk.c +++ b/arch/mips/mach-ath79/ar934x/clk.c @@ -90,7 +90,7 @@ static void ar934x_srif_pll_cfg(void __iomem *pll_reg_base, const u32 srif_val) setbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5);
- wait_for_bit("clk", pll_reg_base + 0xc, BIT(3), 1, 10, 0); + wait_for_bit_le32(pll_reg_base + 0xc, BIT(3), 1, 10, 0);
clrbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5); diff --git a/board/samtec/vining_2000/vining_2000.c b/board/samtec/vining_2000/vining_2000.c index af1a3e75cb..cced08b8b8 100644 --- a/board/samtec/vining_2000/vining_2000.c +++ b/board/samtec/vining_2000/vining_2000.c @@ -378,7 +378,7 @@ static int read_adc(u32 *val)
/* start auto calibration */ setbits_le32(b + ADCx_GC, ADCx_GC_CAL); - ret = wait_for_bit("ADC", b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); + ret = wait_for_bit_le32(b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); if (ret) goto adc_exit;
@@ -386,7 +386,7 @@ static int read_adc(u32 *val) writel(0, b + ADCx_HC0);
/* wait for conversion */ - ret = wait_for_bit("ADC", b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); + ret = wait_for_bit_le32(b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); if (ret) goto adc_exit;
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c index f6eef314ec..177803943d 100644 --- a/drivers/clk/clk_pic32.c +++ b/drivers/clk/clk_pic32.c @@ -197,8 +197,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
/* wait till previous src change is active */ - wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE, + false, CONFIG_SYS_HZ, false);
/* parent_id */ v = readl(reg); @@ -223,8 +223,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
/* wait for divider switching to complete */ - return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false, - CONFIG_SYS_HZ, false); + return wait_for_bit_le32(reg, REFO_DIVSW_EN, false, + CONFIG_SYS_HZ, false); }
static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph) @@ -311,8 +311,8 @@ static int pic32_mpll_init(struct pic32_clk_priv *priv)
/* Wait for ready */ mask = MPLL_RDY | MPLL_VREG_RDY; - return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask, - true, get_tbclk(), false); + return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask, + true, get_tbclk(), false); }
static void pic32_clk_init(struct udevice *dev) diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index b26bbcc59f..22828fd470 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -1046,8 +1046,8 @@ static int gen3_clk_endisable(struct clk *clk, bool enable) if (ret) return ret; clrbits_le32(priv->base + SMSTPCR(reg), bitmask); - return wait_for_bit("MSTP", priv->base + MSTPSR(reg), - bitmask, 0, 100, 0); + return wait_for_bit_le32(priv->base + MSTPSR(reg), + bitmask, 0, 100, 0); } else { setbits_le32(priv->base + SMSTPCR(reg), bitmask); return 0; diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c index 6056418588..a52427c3d6 100644 --- a/drivers/ddr/microchip/ddr2.c +++ b/drivers/ddr/microchip/ddr2.c @@ -57,8 +57,8 @@ static int ddr2_phy_calib_start(void) writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
/* Wait for SCL for data byte to pass */ - return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS, - true, CONFIG_SYS_HZ, false); + return wait_for_bit_le32(&ddr2_phy->scl_start, SCL_LUBPASS, + true, CONFIG_SYS_HZ, false); }
/* DDR2 Controller initialization */ @@ -256,8 +256,8 @@ void ddr2_ctrl_init(void) writel(INIT_START, &ctrl->memcon);
/* wait for all host cmds to be transmitted */ - wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ctrl->cmdissue, CMD_VALID, false, + CONFIG_SYS_HZ, false);
/* inform all cmds issued, ready for normal operation */ writel(INIT_START | INIT_DONE, &ctrl->memcon); diff --git a/drivers/fpga/socfpga_arria10.c b/drivers/fpga/socfpga_arria10.c index 5c1a68a009..d5763965dd 100644 --- a/drivers/fpga/socfpga_arria10.c +++ b/drivers/fpga/socfpga_arria10.c @@ -62,8 +62,7 @@ int is_fpgamgr_user_mode(void)
static int wait_for_user_mode(void) { - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, ALT_FPGAMGR_IMGCFG_STAT_F2S_USERMODE_SET_MSK, 1, FPGA_TIMEOUT_MSEC, false); } @@ -115,19 +114,17 @@ static int wait_for_nconfig_pin_and_nstatus_pin(void) /* Poll until f2s_nconfig_pin and f2s_nstatus_pin; loop until de-asserted, * timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - mask, - false, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + mask, + false, FPGA_TIMEOUT_MSEC, false); }
static int wait_for_f2s_nstatus_pin(unsigned long value) { /* Poll until f2s to specific value, timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, - value, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, + value, FPGA_TIMEOUT_MSEC, false); }
/* set CD ratio */ diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 9117ab6bf9..f0661bd96c 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -109,15 +109,15 @@ static int msm_sdc_probe(struct udevice *dev)
/* Wait for reset to be written to register */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2, - SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2, + SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { printf("msm_sdhci: reset request failed\n"); return -EIO; }
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER, - SDCC_MCI_POWER_SW_RST, false, 2, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER, + SDCC_MCI_POWER_SW_RST, false, 2, false)) { printf("msm_sdhci: stuck in reset\n"); return -ETIMEDOUT; } diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c index e1a8d3bc4b..8bbf2fa9a2 100644 --- a/drivers/mtd/pic32_flash.c +++ b/drivers/mtd/pic32_flash.c @@ -66,8 +66,8 @@ static inline void flash_initiate_operation(u32 nvmop)
static int flash_wait_till_busy(const char *func, ulong timeout) { - int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw, - NVM_WR, false, timeout, false); + int ret = wait_for_bit_le32(&nvm_regs_p->ctrl.raw, + NVM_WR, false, timeout, false);
return ret ? ERR_TIMOUT : ERR_OK; } diff --git a/drivers/net/ag7xxx.c b/drivers/net/ag7xxx.c index 00e6806892..f28187058e 100644 --- a/drivers/net/ag7xxx.c +++ b/drivers/net/ag7xxx.c @@ -164,8 +164,8 @@ static int ag7xxx_switch_read(struct mii_dev *bus, int addr, int reg, u16 *val) writel(AG7XXX_ETH_MII_MGMT_CMD_READ, regs + AG7XXX_ETH_MII_MGMT_CMD);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); if (ret) return ret;
@@ -185,8 +185,8 @@ static int ag7xxx_switch_write(struct mii_dev *bus, int addr, int reg, u16 val) regs + AG7XXX_ETH_MII_MGMT_ADDRESS); writel(val, regs + AG7XXX_ETH_MII_MGMT_CTRL);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0);
return ret; } @@ -510,13 +510,13 @@ static void ag7xxx_eth_stop(struct udevice *dev)
/* Stop the TX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_TX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, + 1000, 0);
/* Stop the RX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_RX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, + 1000, 0); }
/* diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 00076cffbe..232e8034df 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -361,8 +361,9 @@ static void eqos_flush_buffer(void *buf, size_t size)
static int eqos_mdio_wait_idle(struct eqos_priv *eqos) { - return wait_for_bit(__func__, &eqos->mac_regs->mdio_address, - EQOS_MAC_MDIO_ADDRESS_GB, false, 1000000, true); + return wait_for_bit_le32(&eqos->mac_regs->mdio_address, + EQOS_MAC_MDIO_ADDRESS_GB, false, + 1000000, true); }
static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, @@ -588,15 +589,15 @@ static int eqos_calibrate_pads_tegra186(struct udevice *dev) setbits_le32(&eqos->tegra186_regs->auto_cal_config, EQOS_AUTO_CAL_CONFIG_START | EQOS_AUTO_CAL_CONFIG_ENABLE);
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); if (ret) { pr_err("calibrate didn't start"); goto failed; }
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); if (ret) { pr_err("calibrate didn't finish"); goto failed; @@ -862,8 +863,8 @@ static int eqos_start(struct udevice *dev)
eqos->reg_access_ok = true;
- ret = wait_for_bit(__func__, &eqos->dma_regs->mode, - EQOS_DMA_MODE_SWR, false, 10, false); + ret = wait_for_bit_le32(&eqos->dma_regs->mode, + EQOS_DMA_MODE_SWR, false, 10, false); if (ret) { pr_err("EQOS_DMA_MODE_SWR stuck"); goto err_stop_resets; diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a6df950081..51a6c97550 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -548,8 +548,8 @@ static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { u32 data = ethoc_read(priv, MIIRX_DATA); @@ -571,8 +571,8 @@ static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, ethoc_write(priv, MIITX_DATA, val); ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { /* reset MII command register */ diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c index 0b89911f04..7129372790 100644 --- a/drivers/net/pic32_eth.c +++ b/drivers/net/pic32_eth.c @@ -64,8 +64,8 @@ static int pic32_mii_init(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false);
/* turn controller ON to access PHY over MII */ writel(ETHCON_ON, &ectl_p->con1.set); @@ -239,8 +239,8 @@ static void pic32_ctrl_reset(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false); /* decrement received buffcnt to zero. */ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT) writel(ETHCON_BUFCDEC, &ectl_p->con1.set); @@ -375,8 +375,8 @@ static void pic32_eth_stop(struct udevice *dev) mdelay(10);
/* wait until everything is down */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - 2 * CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + 2 * CONFIG_SYS_HZ, false);
/* clear any existing interrupt event */ writel(0xffffffff, &ectl_p->irq.clr); diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c index 578fc96905..6ae5c40fa3 100644 --- a/drivers/net/pic32_mdio.c +++ b/drivers/net/pic32_mdio.c @@ -22,8 +22,8 @@ static int pic32_mdio_write(struct mii_dev *bus, struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -36,8 +36,8 @@ static int pic32_mdio_write(struct mii_dev *bus, udelay(12);
/* Wait for write to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
return 0; } @@ -48,8 +48,8 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -62,9 +62,9 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) udelay(12);
/* Wait for read to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, - MIIMIND_NOTVALID | MIIMIND_BUSY, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(&mii_regs->mind.raw, + MIIMIND_NOTVALID | MIIMIND_BUSY, + false, CONFIG_SYS_HZ, false);
/* Clear the command register */ writel(0, &mii_regs->mcmd.raw); @@ -82,22 +82,22 @@ static int pic32_mdio_reset(struct mii_dev *bus) writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, false, CONFIG_SYS_HZ, true);
/* Clear reset bit */ writel(0, &mii_regs->mcfg);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */ writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true); return 0; }
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index dc743e113d..a39f9c5c86 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -222,8 +222,8 @@ static int ravb_reset(struct udevice *dev) writel(CCC_OPC_CONFIG, eth->iobase + RAVB_REG_CCC);
/* Check the operating mode is changed to the config mode. */ - return wait_for_bit(dev->name, (void *)eth->iobase + RAVB_REG_CSR, - CSR_OPS_CONFIG, true, 100, true); + return wait_for_bit_le32((void *)eth->iobase + RAVB_REG_CSR, + CSR_OPS_CONFIG, true, 100, true); }
static void ravb_base_desc_init(struct ravb_priv *eth) diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 9a2a578ff9..55fa1e49d5 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -366,8 +366,8 @@ static int axi_ethernet_init(struct axidma_priv *priv) * processor mode and hence bypass in this mode */ if (!priv->eth_hasnobuf) { - err = wait_for_bit(__func__, (const u32 *)®s->is, - XAE_INT_MGTRDY_MASK, true, 200, false); + err = wait_for_bit_le32((const u32 *)®s->is, + XAE_INT_MGTRDY_MASK, true, 200, false); if (err) { printf("%s: Timeout\n", __func__); return 1; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 1dfd631e1a..2cc49bca92 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -192,8 +192,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, struct zynq_gem_regs *regs = priv->iobase; int err;
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -205,8 +205,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, /* Write mgtcr and wait for completion */ writel(mgtcr, ®s->phymntnc);
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -514,8 +514,8 @@ static int zynq_gem_send(struct udevice *dev, void *ptr, int len) if (priv->tx_bd->status & ZYNQ_GEM_TXBUF_EXHAUSTED) printf("TX buffers exhausted in mid frame\n");
- return wait_for_bit(__func__, ®s->txsr, ZYNQ_GEM_TSR_DONE, - true, 20000, true); + return wait_for_bit_le32(®s->txsr, ZYNQ_GEM_TSR_DONE, + true, 20000, true); }
/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ diff --git a/drivers/reset/sti-reset.c b/drivers/reset/sti-reset.c index 17786f976a..0fc5a28802 100644 --- a/drivers/reset/sti-reset.c +++ b/drivers/reset/sti-reset.c @@ -266,8 +266,8 @@ static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert) return 0;
reg = (void __iomem *)base + ch->ack_offset; - if (wait_for_bit(__func__, reg, BIT(ch->ack_bit), ctrl_val, - 1000, false)) { + if (wait_for_bit_le32(reg, BIT(ch->ack_bit), ctrl_val, + 1000, false)) { pr_err("Stuck on waiting ack reset_ctl=%p dev=%p id=%lu\n", reset_ctl, reset_ctl->dev, reset_ctl->id);
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c index b0e01aa0e5..0632d26211 100644 --- a/drivers/serial/serial_pic32.c +++ b/drivers/serial/serial_pic32.c @@ -51,8 +51,8 @@ static int pic32_serial_init(void __iomem *base, ulong clk, u32 baudrate) u32 div = DIV_ROUND_CLOSEST(clk, baudrate * 16);
/* wait for TX FIFO to empty */ - wait_for_bit(__func__, base + U_STA, UART_TX_EMPTY, - true, CONFIG_SYS_HZ, false); + wait_for_bit_le32(base + U_STA, UART_TX_EMPTY, + true, CONFIG_SYS_HZ, false);
/* send break */ writel(UART_TX_BRK, base + U_STASET); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 228e714e09..8010ab434c 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -394,8 +394,8 @@ out: * Wait until the transfer is completely done before * we deactivate CS. */ - wait_for_bit(__func__, ®_base->sr, - ATMEL_SPI_SR_TXEMPTY, true, 1000, false); + wait_for_bit_le32(®_base->sr, + ATMEL_SPI_SR_TXEMPTY, true, 1000, false);
atmel_spi_cs_deactivate(dev); } diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index e02f2217f4..dca3fdfdea 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -675,8 +675,8 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD, - CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD, + CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); if (ret) { printf("Indirect read completion error (%i)\n", ret); goto failrd; @@ -762,9 +762,9 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, bb_txbuf + rounddown(write_bytes, 4), write_bytes % 4);
- ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL, - CQSPI_REG_SDRAMLEVEL_WR_MASK << - CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, + CQSPI_REG_SDRAMLEVEL_WR_MASK << + CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); if (ret) { printf("Indirect write timed out (%i)\n", ret); goto failwr; @@ -775,8 +775,8 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR, - CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR, + CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); if (ret) { printf("Indirect write completion error (%i)\n", ret); goto failwr; diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 0f3f7d97f0..eed52c15c8 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -1011,11 +1011,11 @@ static int fsl_qspi_probe(struct udevice *bus) priv->num_chipselect = plat->num_chipselect;
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); @@ -1173,11 +1173,11 @@ static int fsl_qspi_claim_bus(struct udevice *dev) priv = dev_get_priv(bus);
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index ec4907391c..d1708a8d56 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -95,8 +95,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, din_8 = din;
while (bytelen) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true,100, false); if (ret) return ret;
@@ -109,9 +110,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, writel(pending_dout, ®->dout);
if (din) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, - true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -160,8 +161,9 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Deactivate CS */ if (flags & SPI_XFER_END) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -231,8 +233,8 @@ static int mvebu_spi_probe(struct udevice *bus) /* Flush read/write FIFO */ data = readl(®->cfg); writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg); - ret = wait_for_bit(__func__, ®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, - false, 1000, false); + ret = wait_for_bit_le32(®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, + false, 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 1293e18f75..540c016412 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -108,8 +108,8 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num)
writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_TXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -127,8 +127,8 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) int ret;
writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_RXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -145,15 +145,15 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) int ret;
/* Wait for AHB master IDLE state. */ - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_AHBIDLE, - true, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, + true, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
/* Core Soft Reset */ writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_CSFTRST, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -267,8 +267,8 @@ static void dwc_otg_core_host_init(struct udevice *dev, clrsetbits_le32(®s->hc_regs[i].hcchar, DWC2_HCCHAR_EPDIR, DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = wait_for_bit(__func__, ®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN, false, 1000, false); + ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_CHEN, false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__); } @@ -783,8 +783,8 @@ int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) int ret; uint32_t hcint, hctsiz;
- ret = wait_for_bit(__func__, &hc_regs->hcint, DWC2_HCINT_CHHLTD, true, - 1000, false); + ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, + 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 2c0c63322c..f5320ca298 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -133,8 +133,7 @@ static int ehci_usb_remove(struct udevice *dev) setbits_le32(&ehci->usbcmd, CMD_RESET);
/* Wait for reset */ - if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30, - false)) { + if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) { printf("Stuck on USB reset.\n"); return -ETIMEDOUT; } diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index fe2627ea93..2c8fc3c4b1 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -142,13 +142,12 @@ static int usb_phy_enable(int index, struct usb_ehci *ehci)
/* Stop then Reset */ clrbits_le32(usb_cmd, UCMD_RUN_STOP); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RUN_STOP, false, 10000, - false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RUN_STOP, false, 10000, false); if (ret) return ret;
setbits_le32(usb_cmd, UCMD_RESET); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RESET, false, 10000, false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RESET, false, 10000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ohci-lpc32xx.c b/drivers/usb/host/ohci-lpc32xx.c index 2f2b4b90de..44a49807a4 100644 --- a/drivers/usb/host/ohci-lpc32xx.c +++ b/drivers/usb/host/ohci-lpc32xx.c @@ -143,8 +143,8 @@ static int usbpll_setup(void) setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01)); setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);
- ret = wait_for_bit(__func__, &clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, - true, CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, + true, CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -178,8 +178,8 @@ int usb_cpu_init(void)
/* enable I2C clock */ writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl); - ret = wait_for_bit(__func__, &otg->otg_clk_sts, OTG_CLK_I2C_EN, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, OTG_CLK_I2C_EN, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -199,8 +199,8 @@ int usb_cpu_init(void) OTG_CLK_I2C_EN | OTG_CLK_HOST_EN; writel(mask, &otg->otg_clk_ctrl);
- ret = wait_for_bit(__func__, &otg->otg_clk_sts, mask, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, mask, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index d47c99644d..71202d7b03 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -55,18 +55,18 @@ static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data, setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SET_DATA0);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, + 10, false); if (ret) break; }
clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SUCCESS, true, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SUCCESS, true, + 10, false);
return ret; } diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c index f77da2ec97..c0dd689e7c 100644 --- a/drivers/video/atmel_hlcdfb.c +++ b/drivers/video/atmel_hlcdfb.c @@ -70,26 +70,26 @@ void lcd_ctrl_init(void *lcdbase)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -215,26 +215,26 @@ void lcd_ctrl_init(void *lcdbase) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -299,26 +299,26 @@ static void atmel_hlcdc_init(struct udevice *dev)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -451,26 +451,26 @@ static void atmel_hlcdc_init(struct udevice *dev) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); }

On 10.01.2018 21:26, Álvaro Fernández Rojas wrote:
wait_for_bit callers use the 32 bit LE version
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v7: Introduce changes suggested by Jagan Teki:
- Remove wait_for_bit and update callers to wait_for_bit_le32.
arch/arm/mach-imx/mx6/ddr.c | 22 ++++----- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 8 ++-- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +++++++-------- arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- drivers/clk/clk_pic32.c | 12 ++--- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 ++-- drivers/fpga/socfpga_arria10.c | 17 +++---- drivers/mmc/msm_sdhci.c | 8 ++-- drivers/mtd/pic32_flash.c | 4 +- drivers/net/ag7xxx.c | 16 +++---- drivers/net/dwc_eth_qos.c | 17 +++---- drivers/net/ethoc.c | 8 ++-- drivers/net/pic32_eth.c | 12 ++--- drivers/net/pic32_mdio.c | 28 ++++++------ drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 ++--- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/cadence_qspi_apb.c | 14 +++--- drivers/spi/fsl_qspi.c | 20 ++++----- drivers/spi/mvebu_a3700_spi.c | 20 +++++---- drivers/usb/host/dwc2.c | 24 +++++----- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +-- drivers/usb/host/ohci-lpc32xx.c | 12 ++--- drivers/usb/host/xhci-rcar.c | 12 ++--- drivers/video/atmel_hlcdfb.c | 64 +++++++++++++-------------- 32 files changed, 207 insertions(+), 209 deletions(-)
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
nits below
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index dc743e113d..a39f9c5c86 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -222,8 +222,8 @@ static int ravb_reset(struct udevice *dev) writel(CCC_OPC_CONFIG, eth->iobase + RAVB_REG_CCC);
/* Check the operating mode is changed to the config mode. */
- return wait_for_bit(dev->name, (void *)eth->iobase + RAVB_REG_CSR,
CSR_OPS_CONFIG, true, 100, true);
- return wait_for_bit_le32((void *)eth->iobase + RAVB_REG_CSR,
CSR_OPS_CONFIG, true, 100, true);
the cast to (void *) is not necessary anymore
}
static void ravb_base_desc_init(struct ravb_priv *eth) diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 9a2a578ff9..55fa1e49d5 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -366,8 +366,8 @@ static int axi_ethernet_init(struct axidma_priv *priv) * processor mode and hence bypass in this mode */ if (!priv->eth_hasnobuf) {
err = wait_for_bit(__func__, (const u32 *)®s->is,
XAE_INT_MGTRDY_MASK, true, 200, false);
err = wait_for_bit_le32((const u32 *)®s->is,
XAE_INT_MGTRDY_MASK, true, 200, false);
the cast to (const u32 *) is not necessary anymore
if (err) { printf("%s: Timeout\n", __func__); return 1;

On Thu, Jan 11, 2018 at 7:20 AM, Daniel Schwierzeck daniel.schwierzeck@gmail.com wrote:
On 10.01.2018 21:26, Álvaro Fernández Rojas wrote:
wait_for_bit callers use the 32 bit LE version
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v7: Introduce changes suggested by Jagan Teki:
- Remove wait_for_bit and update callers to wait_for_bit_le32.
arch/arm/mach-imx/mx6/ddr.c | 22 ++++----- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 8 ++-- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +++++++-------- arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- drivers/clk/clk_pic32.c | 12 ++--- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 ++-- drivers/fpga/socfpga_arria10.c | 17 +++---- drivers/mmc/msm_sdhci.c | 8 ++-- drivers/mtd/pic32_flash.c | 4 +- drivers/net/ag7xxx.c | 16 +++---- drivers/net/dwc_eth_qos.c | 17 +++---- drivers/net/ethoc.c | 8 ++-- drivers/net/pic32_eth.c | 12 ++--- drivers/net/pic32_mdio.c | 28 ++++++------ drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 ++--- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/cadence_qspi_apb.c | 14 +++--- drivers/spi/fsl_qspi.c | 20 ++++----- drivers/spi/mvebu_a3700_spi.c | 20 +++++---- drivers/usb/host/dwc2.c | 24 +++++----- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +-- drivers/usb/host/ohci-lpc32xx.c | 12 ++--- drivers/usb/host/xhci-rcar.c | 12 ++--- drivers/video/atmel_hlcdfb.c | 64 +++++++++++++-------------- 32 files changed, 207 insertions(+), 209 deletions(-)
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com
squash this with 03/13
Reviewed-by: Jagan Teki jagan@openedev.com

Since wait_for_bit callers have been updated to use wait_for_bit_le32, wait_for_bit is no longer needed.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com --- v7: Introduce changes suggested by Jagan Teki: - Remove wait_for_bit and update callers to wait_for_bit_le32.
include/wait_bit.h | 54 ------------------------------------------------------ 1 file changed, 54 deletions(-)
diff --git a/include/wait_bit.h b/include/wait_bit.h index bde6d2cfc3..9f00e54e50 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -16,60 +16,6 @@ #include <asm/io.h>
/** - * wait_for_bit() waits for bit set/cleared in register - * - * Function polls register waiting for specific bit(s) change - * (either 0->1 or 1->0). It can fail under two conditions: - * - Timeout - * - User interaction (CTRL-C) - * Function succeeds only if all bits of masked register are set/cleared - * (depending on set option). - * - * @param prefix Prefix added to timeout messagge (message visible only - * with debug enabled) - * @param reg Register that will be read (using readl()) - * @param mask Bit(s) of register that must be active - * @param set Selects wait condition (bit set or clear) - * @param timeout_ms Timeout (in miliseconds) - * @param breakable Enables CTRL-C interruption - * @return 0 on success, -ETIMEDOUT or -EINTR on failure - */ -static inline int wait_for_bit(const char *prefix, const u32 *reg, - const u32 mask, const bool set, - const unsigned int timeout_ms, - const bool breakable) -{ - u32 val; - unsigned long start = get_timer(0); - - while (1) { - val = readl(reg); - - if (!set) - val = ~val; - - if ((val & mask) == mask) - return 0; - - if (get_timer(start) > timeout_ms) - break; - - if (breakable && ctrlc()) { - puts("Abort\n"); - return -EINTR; - } - - udelay(1); - WATCHDOG_RESET(); - } - - debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", prefix, reg, mask, - set); - - return -ETIMEDOUT; -} - -/** * wait_for_bit_x() waits for bit set/cleared in register * * Function polls register waiting for specific bit(s) change

On 10.01.2018 21:26, Álvaro Fernández Rojas wrote:
Since wait_for_bit callers have been updated to use wait_for_bit_le32, wait_for_bit is no longer needed.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com
v7: Introduce changes suggested by Jagan Teki:
- Remove wait_for_bit and update callers to wait_for_bit_le32.
include/wait_bit.h | 54 ------------------------------------------------------ 1 file changed, 54 deletions(-)
Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 51e28bf07b..e40e1c01de 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -516,6 +516,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 08c7480fda..4787454e59 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e40e1c01de..294d9f9d79 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -405,7 +405,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index 4787454e59..5a7df1c706 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: Introduce changes suggested by Daniel Schwierzeck: - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structure instead of a custom bmips_spi_hw structure. - Define constants for each SPI core. v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 494639fb01..ebc71c2e42 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3184db67f..5770b3f7cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..f0df6871d8 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + printf("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + printf("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + printf("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val, cmd; + int ret; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + printf("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + cmd = SPI_CMD_OP_START; + cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + cmd |= SPI_CMD_3WIRE_MASK; + writew_be(cmd, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + ret = wait_for_bit_8(priv->base + regs[SPI_IR_STAT], + SPI_IR_DONE_MASK, true, 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a4372b..0cab44cb8d 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643b5a..540b9fea5b 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf80e0..1662783279 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb048..f83a6ea8df 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96bef8..6e3d9c3820 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index cfc56cba37..5c091353e5 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -39,3 +39,11 @@ CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7616..5f85c7346f 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index 7665c78d3f..369c919ac7 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -41,3 +41,11 @@ CONFIG_RESET_BCM6345=y CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y CONFIG_WDT_BCM6345=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Squash wait_bit commits. - Remove register param castings. v7: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. - Remove wait_for_bit and update callers to wait_for_bit_le32. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structs for registers v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (12): wait_bit: add 8/16/32 BE/LE versions of wait_for_bit wait_bit: use wait_for_bit_le32 and remove wait_for_bit drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/arm/mach-imx/mx6/ddr.c | 22 +- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 6 +- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +-- arch/mips/dts/brcm,bcm3380.dtsi | 17 + arch/mips/dts/brcm,bcm63268.dtsi | 17 + arch/mips/dts/brcm,bcm6338.dtsi | 17 + arch/mips/dts/brcm,bcm6348.dtsi | 17 + arch/mips/dts/brcm,bcm6358.dtsi | 17 + arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/clk/clk_pic32.c | 12 +- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 +- drivers/fpga/socfpga_arria10.c | 17 +- drivers/mmc/msm_sdhci.c | 8 +- drivers/mtd/pic32_flash.c | 4 +- drivers/mtd/spi/spi_flash.c | 5 +- drivers/net/ag7xxx.c | 16 +- drivers/net/dwc_eth_qos.c | 17 +- drivers/net/ethoc.c | 8 +- drivers/net/pic32_eth.c | 12 +- drivers/net/pic32_mdio.c | 28 +- drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 +- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/atmel_spi.c | 4 +- drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++ drivers/spi/cadence_qspi_apb.c | 14 +- drivers/spi/fsl_qspi.c | 20 +- drivers/spi/mvebu_a3700_spi.c | 20 +- drivers/usb/host/dwc2.c | 24 +- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +- drivers/usb/host/ohci-lpc32xx.c | 12 +- drivers/usb/host/xhci-rcar.c | 12 +- drivers/video/atmel_hlcdfb.c | 64 ++-- include/spi.h | 5 +- include/wait_bit.h | 81 ++--- 47 files changed, 824 insertions(+), 248 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes. v7: Introduce changes suggested by Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..bde6d2cfc3 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,66 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+/** + * wait_for_bit_x() waits for bit set/cleared in register + * + * Function polls register waiting for specific bit(s) change + * (either 0->1 or 1->0). It can fail under two conditions: + * - Timeout + * - User interaction (CTRL-C) + * Function succeeds only if all bits of masked register are set/cleared + * (depending on set option). + * + * @param reg Register that will be read (using read_x()) + * @param mask Bit(s) of register that must be active + * @param set Selects wait condition (bit set or clear) + * @param timeout_ms Timeout (in milliseconds) + * @param breakable Enables CTRL-C interruption + * @return 0 on success, -ETIMEDOUT or -EINTR on failure + */ + +#define BUILD_WAIT_FOR_BIT(sfx, type, read) \ + \ +static inline int wait_for_bit_##sfx(const void *reg, \ + const type mask, \ + const bool set, \ + const unsigned int timeout_ms, \ + const bool breakable) \ +{ \ + type val; \ + unsigned long start = get_timer(0); \ + \ + while (1) { \ + val = read(reg); \ + \ + if (!set) \ + val = ~val; \ + \ + if ((val & mask) == mask) \ + return 0; \ + \ + if (get_timer(start) > timeout_ms) \ + break; \ + \ + if (breakable && ctrlc()) { \ + puts("Abort\n"); \ + return -EINTR; \ + } \ + \ + udelay(1); \ + WATCHDOG_RESET(); \ + } \ + \ + debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \ + reg, mask, set); \ + \ + return -ETIMEDOUT; \ +} + +BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
#endif

On Thu, Jan 11, 2018 at 10:41 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes. v7: Introduce changes suggested by Daniel Schwierzeck:
- Use const void* reg for compatibility with 64 bit systems.
- Remove prefix and use __func__ instead.
v6: Introduce changes suggested by Jagan Teki:
- Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..bde6d2cfc3 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,66 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+/**
- wait_for_bit_x() waits for bit set/cleared in register
- Function polls register waiting for specific bit(s) change
- (either 0->1 or 1->0). It can fail under two conditions:
- Timeout
- User interaction (CTRL-C)
- Function succeeds only if all bits of masked register are set/cleared
- (depending on set option).
- @param reg Register that will be read (using read_x())
- @param mask Bit(s) of register that must be active
- @param set Selects wait condition (bit set or clear)
- @param timeout_ms Timeout (in milliseconds)
- @param breakable Enables CTRL-C interruption
- @return 0 on success, -ETIMEDOUT or -EINTR on failure
- */
+#define BUILD_WAIT_FOR_BIT(sfx, type, read) \
\
+static inline int wait_for_bit_##sfx(const void *reg, \
const type mask, \
const bool set, \
const unsigned int timeout_ms, \
const bool breakable) \
+{ \
type val; \
unsigned long start = get_timer(0); \
\
while (1) { \
val = read(reg); \
\
if (!set) \
val = ~val; \
\
if ((val & mask) == mask) \
return 0; \
\
if (get_timer(start) > timeout_ms) \
break; \
\
if (breakable && ctrlc()) { \
puts("Abort\n"); \
return -EINTR; \
} \
\
udelay(1); \
WATCHDOG_RESET(); \
} \
\
debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \
reg, mask, set); \
\
return -ETIMEDOUT; \
+}
+BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
I'm still thinking here, can't we try to use typeof and get rid of these endianess and macros? did you tried that?

El 11/01/2018 a las 18:17, Jagan Teki escribió:
On Thu, Jan 11, 2018 at 10:41 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes. v7: Introduce changes suggested by Daniel Schwierzeck:
- Use const void* reg for compatibility with 64 bit systems.
- Remove prefix and use __func__ instead.
v6: Introduce changes suggested by Jagan Teki:
- Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..bde6d2cfc3 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,66 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+/**
- wait_for_bit_x() waits for bit set/cleared in register
- Function polls register waiting for specific bit(s) change
- (either 0->1 or 1->0). It can fail under two conditions:
- Timeout
- User interaction (CTRL-C)
- Function succeeds only if all bits of masked register are set/cleared
- (depending on set option).
- @param reg Register that will be read (using read_x())
- @param mask Bit(s) of register that must be active
- @param set Selects wait condition (bit set or clear)
- @param timeout_ms Timeout (in milliseconds)
- @param breakable Enables CTRL-C interruption
- @return 0 on success, -ETIMEDOUT or -EINTR on failure
- */
+#define BUILD_WAIT_FOR_BIT(sfx, type, read) \
\
+static inline int wait_for_bit_##sfx(const void *reg, \
const type mask, \
const bool set, \
const unsigned int timeout_ms, \
const bool breakable) \
+{ \
type val; \
unsigned long start = get_timer(0); \
\
while (1) { \
val = read(reg); \
\
if (!set) \
val = ~val; \
\
if ((val & mask) == mask) \
return 0; \
\
if (get_timer(start) > timeout_ms) \
break; \
\
if (breakable && ctrlc()) { \
puts("Abort\n"); \
return -EINTR; \
} \
\
udelay(1); \
WATCHDOG_RESET(); \
} \
\
debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \
reg, mask, set); \
\
return -ETIMEDOUT; \
+}
+BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
I'm still thinking here, can't we try to use typeof and get rid of these endianess and macros? did you tried that?
No, I didn't try that. Can you provide an example of that?

wait_for_bit callers use the 32 bit LE version
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Squash wait_bit commits. - Remove register param castings. v7: Introduce changes suggested by Jagan Teki: - Remove wait_for_bit and update callers to wait_for_bit_le32.
arch/arm/mach-imx/mx6/ddr.c | 22 ++++----- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 6 +-- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +++++++-------- arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- drivers/clk/clk_pic32.c | 12 ++--- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 ++-- drivers/fpga/socfpga_arria10.c | 17 +++---- drivers/mmc/msm_sdhci.c | 8 ++-- drivers/mtd/pic32_flash.c | 4 +- drivers/net/ag7xxx.c | 16 +++---- drivers/net/dwc_eth_qos.c | 17 +++---- drivers/net/ethoc.c | 8 ++-- drivers/net/pic32_eth.c | 12 ++--- drivers/net/pic32_mdio.c | 28 ++++++------ drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 ++--- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/cadence_qspi_apb.c | 14 +++--- drivers/spi/fsl_qspi.c | 20 ++++----- drivers/spi/mvebu_a3700_spi.c | 20 +++++---- drivers/usb/host/dwc2.c | 24 +++++----- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +-- drivers/usb/host/ohci-lpc32xx.c | 12 ++--- drivers/usb/host/xhci-rcar.c | 12 ++--- drivers/video/atmel_hlcdfb.c | 64 +++++++++++++-------------- include/wait_bit.h | 54 ---------------------- 33 files changed, 205 insertions(+), 263 deletions(-)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 52a9a25904..f07f938c65 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -21,10 +21,10 @@ static void reset_read_data_fifos(void)
/* Reset data FIFOs twice. */ setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0);
setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); }
static void precharge_all(const bool cs0_enable, const bool cs1_enable) @@ -39,12 +39,12 @@ static void precharge_all(const bool cs0_enable, const bool cs1_enable) */ if (cs0_enable) { /* CS0 */ writel(0x04008050, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); }
if (cs1_enable) { /* CS1 */ writel(0x04008058, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); } }
@@ -146,7 +146,7 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) * 7. Upon completion of this process the MMDC de-asserts * the MPWLGCR[HW_WL_EN] */ - wait_for_bit("MMDC", &mmdc0->mpwlgcr, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwlgcr, 1 << 0, 0, 100, 0);
/* * 8. check for any errors: check both PHYs for x64 configuration, @@ -278,7 +278,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x00008028, &mmdc0->mdscr);
/* poll to make sure the con_ack bit was asserted */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0);
/* * Check MDMISC register CALIB_PER_CS to see which CS calibration @@ -312,7 +312,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * this bit until it clears to indicate completion of the write access. */ setbits_le32(&mmdc0->mpswdar0, 1); - wait_for_bit("MMDC", &mmdc0->mpswdar0, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpswdar0, 1 << 0, 0, 100, 0);
/* Set the RD_DL_ABS# bits to their default values * (will be calibrated later in the read delay-line calibration). @@ -359,7 +359,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) setbits_le32(&mmdc0->mpdgctrl0, 5 << 28);
/* Poll for completion. MPDGCTRL0[HW_DG_EN] should be 0 */ - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0);
/* * Check to see if any errors were encountered during calibration @@ -423,7 +423,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * setting MPRDDLHWCTL[HW_RD_DL_EN] = 0. Also, ensure that * no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0);
/* check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mprddlhwctl) & 0x0000000f) @@ -477,7 +477,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * by setting MPWRDLHWCTL[HW_WR_DL_EN] = 0. * Also, ensure that no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0);
/* Check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mpwrdlhwctl) & 0x0000000f) @@ -526,7 +526,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x0, &mmdc0->mdscr); /* CS0 */
/* Poll to make sure the con_ack bit is clear */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 0, 100, 0);
/* * Print out the registers that were updated as a result diff --git a/arch/arm/mach-socfpga/clock_manager.c b/arch/arm/mach-socfpga/clock_manager.c index cb6ae03696..c740c9f648 100644 --- a/arch/arm/mach-socfpga/clock_manager.c +++ b/arch/arm/mach-socfpga/clock_manager.c @@ -37,8 +37,8 @@ void cm_wait_for_lock(u32 mask) /* function to poll in the fsm busy bit */ int cm_wait_for_fsm(void) { - return wait_for_bit(__func__, (const u32 *)&clock_manager_base->stat, - CLKMGR_STAT_BUSY, false, 20000, false); + return wait_for_bit_le32(&clock_manager_base->stat, + CLKMGR_STAT_BUSY, false, 20000, false); }
int set_cpu_clk_info(void) diff --git a/arch/arm/mach-socfpga/clock_manager_gen5.c b/arch/arm/mach-socfpga/clock_manager_gen5.c index 31fd51097a..a23f3fc5d0 100644 --- a/arch/arm/mach-socfpga/clock_manager_gen5.c +++ b/arch/arm/mach-socfpga/clock_manager_gen5.c @@ -37,15 +37,13 @@ static int cm_write_with_phase(u32 value, u32 reg_address, u32 mask) int ret;
/* poll until phase is zero */ - ret = wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + ret = wait_for_bit_le32(reg_address, mask, false, 20000, false); if (ret) return ret;
writel(value, reg_address);
- return wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + return wait_for_bit_le32(reg_address, mask, false, 20000, false); }
/* diff --git a/arch/arm/mach-socfpga/reset_manager_arria10.c b/arch/arm/mach-socfpga/reset_manager_arria10.c index ae16897494..54f0ddb255 100644 --- a/arch/arm/mach-socfpga/reset_manager_arria10.c +++ b/arch/arm/mach-socfpga/reset_manager_arria10.c @@ -222,8 +222,8 @@ int socfpga_reset_deassert_bridges_handoff(void) clrbits_le32(&reset_manager_base->brgmodrst, mask_rstmgr);
/* Poll until all idleack to 0, timeout at 1000ms */ - return wait_for_bit(__func__, &sysmgr_regs->noc_idleack, mask_noc, - false, 1000, false); + return wait_for_bit_le32(&sysmgr_regs->noc_idleack, mask_noc, + false, 1000, false); }
void socfpga_reset_assert_fpga_connected_peripherals(void) @@ -343,26 +343,26 @@ int socfpga_bridges_reset(void) writel(ALT_SYSMGR_NOC_TMO_EN_SET_MSK, &sysmgr_regs->noc_timeout);
/* Poll until all idleack to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idleack, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idleack, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
/* Poll until all idlestatus to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idlestatus, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idlestatus, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
diff --git a/arch/mips/mach-ath79/ar934x/clk.c b/arch/mips/mach-ath79/ar934x/clk.c index 9b41d3de60..ba2243c9be 100644 --- a/arch/mips/mach-ath79/ar934x/clk.c +++ b/arch/mips/mach-ath79/ar934x/clk.c @@ -90,7 +90,7 @@ static void ar934x_srif_pll_cfg(void __iomem *pll_reg_base, const u32 srif_val) setbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5);
- wait_for_bit("clk", pll_reg_base + 0xc, BIT(3), 1, 10, 0); + wait_for_bit_le32(pll_reg_base + 0xc, BIT(3), 1, 10, 0);
clrbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5); diff --git a/board/samtec/vining_2000/vining_2000.c b/board/samtec/vining_2000/vining_2000.c index af1a3e75cb..cced08b8b8 100644 --- a/board/samtec/vining_2000/vining_2000.c +++ b/board/samtec/vining_2000/vining_2000.c @@ -378,7 +378,7 @@ static int read_adc(u32 *val)
/* start auto calibration */ setbits_le32(b + ADCx_GC, ADCx_GC_CAL); - ret = wait_for_bit("ADC", b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); + ret = wait_for_bit_le32(b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); if (ret) goto adc_exit;
@@ -386,7 +386,7 @@ static int read_adc(u32 *val) writel(0, b + ADCx_HC0);
/* wait for conversion */ - ret = wait_for_bit("ADC", b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); + ret = wait_for_bit_le32(b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); if (ret) goto adc_exit;
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c index f6eef314ec..177803943d 100644 --- a/drivers/clk/clk_pic32.c +++ b/drivers/clk/clk_pic32.c @@ -197,8 +197,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
/* wait till previous src change is active */ - wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE, + false, CONFIG_SYS_HZ, false);
/* parent_id */ v = readl(reg); @@ -223,8 +223,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
/* wait for divider switching to complete */ - return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false, - CONFIG_SYS_HZ, false); + return wait_for_bit_le32(reg, REFO_DIVSW_EN, false, + CONFIG_SYS_HZ, false); }
static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph) @@ -311,8 +311,8 @@ static int pic32_mpll_init(struct pic32_clk_priv *priv)
/* Wait for ready */ mask = MPLL_RDY | MPLL_VREG_RDY; - return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask, - true, get_tbclk(), false); + return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask, + true, get_tbclk(), false); }
static void pic32_clk_init(struct udevice *dev) diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index b26bbcc59f..22828fd470 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -1046,8 +1046,8 @@ static int gen3_clk_endisable(struct clk *clk, bool enable) if (ret) return ret; clrbits_le32(priv->base + SMSTPCR(reg), bitmask); - return wait_for_bit("MSTP", priv->base + MSTPSR(reg), - bitmask, 0, 100, 0); + return wait_for_bit_le32(priv->base + MSTPSR(reg), + bitmask, 0, 100, 0); } else { setbits_le32(priv->base + SMSTPCR(reg), bitmask); return 0; diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c index 6056418588..a52427c3d6 100644 --- a/drivers/ddr/microchip/ddr2.c +++ b/drivers/ddr/microchip/ddr2.c @@ -57,8 +57,8 @@ static int ddr2_phy_calib_start(void) writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
/* Wait for SCL for data byte to pass */ - return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS, - true, CONFIG_SYS_HZ, false); + return wait_for_bit_le32(&ddr2_phy->scl_start, SCL_LUBPASS, + true, CONFIG_SYS_HZ, false); }
/* DDR2 Controller initialization */ @@ -256,8 +256,8 @@ void ddr2_ctrl_init(void) writel(INIT_START, &ctrl->memcon);
/* wait for all host cmds to be transmitted */ - wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ctrl->cmdissue, CMD_VALID, false, + CONFIG_SYS_HZ, false);
/* inform all cmds issued, ready for normal operation */ writel(INIT_START | INIT_DONE, &ctrl->memcon); diff --git a/drivers/fpga/socfpga_arria10.c b/drivers/fpga/socfpga_arria10.c index 5c1a68a009..d5763965dd 100644 --- a/drivers/fpga/socfpga_arria10.c +++ b/drivers/fpga/socfpga_arria10.c @@ -62,8 +62,7 @@ int is_fpgamgr_user_mode(void)
static int wait_for_user_mode(void) { - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, ALT_FPGAMGR_IMGCFG_STAT_F2S_USERMODE_SET_MSK, 1, FPGA_TIMEOUT_MSEC, false); } @@ -115,19 +114,17 @@ static int wait_for_nconfig_pin_and_nstatus_pin(void) /* Poll until f2s_nconfig_pin and f2s_nstatus_pin; loop until de-asserted, * timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - mask, - false, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + mask, + false, FPGA_TIMEOUT_MSEC, false); }
static int wait_for_f2s_nstatus_pin(unsigned long value) { /* Poll until f2s to specific value, timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, - value, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, + value, FPGA_TIMEOUT_MSEC, false); }
/* set CD ratio */ diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 9117ab6bf9..f0661bd96c 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -109,15 +109,15 @@ static int msm_sdc_probe(struct udevice *dev)
/* Wait for reset to be written to register */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2, - SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2, + SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { printf("msm_sdhci: reset request failed\n"); return -EIO; }
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER, - SDCC_MCI_POWER_SW_RST, false, 2, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER, + SDCC_MCI_POWER_SW_RST, false, 2, false)) { printf("msm_sdhci: stuck in reset\n"); return -ETIMEDOUT; } diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c index e1a8d3bc4b..8bbf2fa9a2 100644 --- a/drivers/mtd/pic32_flash.c +++ b/drivers/mtd/pic32_flash.c @@ -66,8 +66,8 @@ static inline void flash_initiate_operation(u32 nvmop)
static int flash_wait_till_busy(const char *func, ulong timeout) { - int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw, - NVM_WR, false, timeout, false); + int ret = wait_for_bit_le32(&nvm_regs_p->ctrl.raw, + NVM_WR, false, timeout, false);
return ret ? ERR_TIMOUT : ERR_OK; } diff --git a/drivers/net/ag7xxx.c b/drivers/net/ag7xxx.c index 00e6806892..f28187058e 100644 --- a/drivers/net/ag7xxx.c +++ b/drivers/net/ag7xxx.c @@ -164,8 +164,8 @@ static int ag7xxx_switch_read(struct mii_dev *bus, int addr, int reg, u16 *val) writel(AG7XXX_ETH_MII_MGMT_CMD_READ, regs + AG7XXX_ETH_MII_MGMT_CMD);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); if (ret) return ret;
@@ -185,8 +185,8 @@ static int ag7xxx_switch_write(struct mii_dev *bus, int addr, int reg, u16 val) regs + AG7XXX_ETH_MII_MGMT_ADDRESS); writel(val, regs + AG7XXX_ETH_MII_MGMT_CTRL);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0);
return ret; } @@ -510,13 +510,13 @@ static void ag7xxx_eth_stop(struct udevice *dev)
/* Stop the TX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_TX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, + 1000, 0);
/* Stop the RX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_RX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, + 1000, 0); }
/* diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 00076cffbe..232e8034df 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -361,8 +361,9 @@ static void eqos_flush_buffer(void *buf, size_t size)
static int eqos_mdio_wait_idle(struct eqos_priv *eqos) { - return wait_for_bit(__func__, &eqos->mac_regs->mdio_address, - EQOS_MAC_MDIO_ADDRESS_GB, false, 1000000, true); + return wait_for_bit_le32(&eqos->mac_regs->mdio_address, + EQOS_MAC_MDIO_ADDRESS_GB, false, + 1000000, true); }
static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, @@ -588,15 +589,15 @@ static int eqos_calibrate_pads_tegra186(struct udevice *dev) setbits_le32(&eqos->tegra186_regs->auto_cal_config, EQOS_AUTO_CAL_CONFIG_START | EQOS_AUTO_CAL_CONFIG_ENABLE);
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); if (ret) { pr_err("calibrate didn't start"); goto failed; }
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); if (ret) { pr_err("calibrate didn't finish"); goto failed; @@ -862,8 +863,8 @@ static int eqos_start(struct udevice *dev)
eqos->reg_access_ok = true;
- ret = wait_for_bit(__func__, &eqos->dma_regs->mode, - EQOS_DMA_MODE_SWR, false, 10, false); + ret = wait_for_bit_le32(&eqos->dma_regs->mode, + EQOS_DMA_MODE_SWR, false, 10, false); if (ret) { pr_err("EQOS_DMA_MODE_SWR stuck"); goto err_stop_resets; diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a6df950081..51a6c97550 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -548,8 +548,8 @@ static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { u32 data = ethoc_read(priv, MIIRX_DATA); @@ -571,8 +571,8 @@ static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, ethoc_write(priv, MIITX_DATA, val); ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { /* reset MII command register */ diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c index 0b89911f04..7129372790 100644 --- a/drivers/net/pic32_eth.c +++ b/drivers/net/pic32_eth.c @@ -64,8 +64,8 @@ static int pic32_mii_init(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false);
/* turn controller ON to access PHY over MII */ writel(ETHCON_ON, &ectl_p->con1.set); @@ -239,8 +239,8 @@ static void pic32_ctrl_reset(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false); /* decrement received buffcnt to zero. */ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT) writel(ETHCON_BUFCDEC, &ectl_p->con1.set); @@ -375,8 +375,8 @@ static void pic32_eth_stop(struct udevice *dev) mdelay(10);
/* wait until everything is down */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - 2 * CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + 2 * CONFIG_SYS_HZ, false);
/* clear any existing interrupt event */ writel(0xffffffff, &ectl_p->irq.clr); diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c index 578fc96905..6ae5c40fa3 100644 --- a/drivers/net/pic32_mdio.c +++ b/drivers/net/pic32_mdio.c @@ -22,8 +22,8 @@ static int pic32_mdio_write(struct mii_dev *bus, struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -36,8 +36,8 @@ static int pic32_mdio_write(struct mii_dev *bus, udelay(12);
/* Wait for write to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
return 0; } @@ -48,8 +48,8 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -62,9 +62,9 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) udelay(12);
/* Wait for read to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, - MIIMIND_NOTVALID | MIIMIND_BUSY, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(&mii_regs->mind.raw, + MIIMIND_NOTVALID | MIIMIND_BUSY, + false, CONFIG_SYS_HZ, false);
/* Clear the command register */ writel(0, &mii_regs->mcmd.raw); @@ -82,22 +82,22 @@ static int pic32_mdio_reset(struct mii_dev *bus) writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, false, CONFIG_SYS_HZ, true);
/* Clear reset bit */ writel(0, &mii_regs->mcfg);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */ writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true); return 0; }
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index dc743e113d..26bd915291 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -222,8 +222,8 @@ static int ravb_reset(struct udevice *dev) writel(CCC_OPC_CONFIG, eth->iobase + RAVB_REG_CCC);
/* Check the operating mode is changed to the config mode. */ - return wait_for_bit(dev->name, (void *)eth->iobase + RAVB_REG_CSR, - CSR_OPS_CONFIG, true, 100, true); + return wait_for_bit_le32(eth->iobase + RAVB_REG_CSR, + CSR_OPS_CONFIG, true, 100, true); }
static void ravb_base_desc_init(struct ravb_priv *eth) diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 9a2a578ff9..70a2e95a8e 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -366,8 +366,8 @@ static int axi_ethernet_init(struct axidma_priv *priv) * processor mode and hence bypass in this mode */ if (!priv->eth_hasnobuf) { - err = wait_for_bit(__func__, (const u32 *)®s->is, - XAE_INT_MGTRDY_MASK, true, 200, false); + err = wait_for_bit_le32(®s->is, XAE_INT_MGTRDY_MASK, + true, 200, false); if (err) { printf("%s: Timeout\n", __func__); return 1; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 1dfd631e1a..2cc49bca92 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -192,8 +192,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, struct zynq_gem_regs *regs = priv->iobase; int err;
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -205,8 +205,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, /* Write mgtcr and wait for completion */ writel(mgtcr, ®s->phymntnc);
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -514,8 +514,8 @@ static int zynq_gem_send(struct udevice *dev, void *ptr, int len) if (priv->tx_bd->status & ZYNQ_GEM_TXBUF_EXHAUSTED) printf("TX buffers exhausted in mid frame\n");
- return wait_for_bit(__func__, ®s->txsr, ZYNQ_GEM_TSR_DONE, - true, 20000, true); + return wait_for_bit_le32(®s->txsr, ZYNQ_GEM_TSR_DONE, + true, 20000, true); }
/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ diff --git a/drivers/reset/sti-reset.c b/drivers/reset/sti-reset.c index 17786f976a..0fc5a28802 100644 --- a/drivers/reset/sti-reset.c +++ b/drivers/reset/sti-reset.c @@ -266,8 +266,8 @@ static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert) return 0;
reg = (void __iomem *)base + ch->ack_offset; - if (wait_for_bit(__func__, reg, BIT(ch->ack_bit), ctrl_val, - 1000, false)) { + if (wait_for_bit_le32(reg, BIT(ch->ack_bit), ctrl_val, + 1000, false)) { pr_err("Stuck on waiting ack reset_ctl=%p dev=%p id=%lu\n", reset_ctl, reset_ctl->dev, reset_ctl->id);
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c index b0e01aa0e5..0632d26211 100644 --- a/drivers/serial/serial_pic32.c +++ b/drivers/serial/serial_pic32.c @@ -51,8 +51,8 @@ static int pic32_serial_init(void __iomem *base, ulong clk, u32 baudrate) u32 div = DIV_ROUND_CLOSEST(clk, baudrate * 16);
/* wait for TX FIFO to empty */ - wait_for_bit(__func__, base + U_STA, UART_TX_EMPTY, - true, CONFIG_SYS_HZ, false); + wait_for_bit_le32(base + U_STA, UART_TX_EMPTY, + true, CONFIG_SYS_HZ, false);
/* send break */ writel(UART_TX_BRK, base + U_STASET); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 228e714e09..8010ab434c 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -394,8 +394,8 @@ out: * Wait until the transfer is completely done before * we deactivate CS. */ - wait_for_bit(__func__, ®_base->sr, - ATMEL_SPI_SR_TXEMPTY, true, 1000, false); + wait_for_bit_le32(®_base->sr, + ATMEL_SPI_SR_TXEMPTY, true, 1000, false);
atmel_spi_cs_deactivate(dev); } diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index e02f2217f4..dca3fdfdea 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -675,8 +675,8 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD, - CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD, + CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); if (ret) { printf("Indirect read completion error (%i)\n", ret); goto failrd; @@ -762,9 +762,9 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, bb_txbuf + rounddown(write_bytes, 4), write_bytes % 4);
- ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL, - CQSPI_REG_SDRAMLEVEL_WR_MASK << - CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, + CQSPI_REG_SDRAMLEVEL_WR_MASK << + CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); if (ret) { printf("Indirect write timed out (%i)\n", ret); goto failwr; @@ -775,8 +775,8 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR, - CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR, + CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); if (ret) { printf("Indirect write completion error (%i)\n", ret); goto failwr; diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 0f3f7d97f0..eed52c15c8 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -1011,11 +1011,11 @@ static int fsl_qspi_probe(struct udevice *bus) priv->num_chipselect = plat->num_chipselect;
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); @@ -1173,11 +1173,11 @@ static int fsl_qspi_claim_bus(struct udevice *dev) priv = dev_get_priv(bus);
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index ec4907391c..d1708a8d56 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -95,8 +95,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, din_8 = din;
while (bytelen) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true,100, false); if (ret) return ret;
@@ -109,9 +110,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, writel(pending_dout, ®->dout);
if (din) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, - true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -160,8 +161,9 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Deactivate CS */ if (flags & SPI_XFER_END) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -231,8 +233,8 @@ static int mvebu_spi_probe(struct udevice *bus) /* Flush read/write FIFO */ data = readl(®->cfg); writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg); - ret = wait_for_bit(__func__, ®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, - false, 1000, false); + ret = wait_for_bit_le32(®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, + false, 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 1293e18f75..540c016412 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -108,8 +108,8 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num)
writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_TXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -127,8 +127,8 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) int ret;
writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_RXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -145,15 +145,15 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) int ret;
/* Wait for AHB master IDLE state. */ - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_AHBIDLE, - true, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, + true, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
/* Core Soft Reset */ writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_CSFTRST, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -267,8 +267,8 @@ static void dwc_otg_core_host_init(struct udevice *dev, clrsetbits_le32(®s->hc_regs[i].hcchar, DWC2_HCCHAR_EPDIR, DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = wait_for_bit(__func__, ®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN, false, 1000, false); + ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_CHEN, false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__); } @@ -783,8 +783,8 @@ int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) int ret; uint32_t hcint, hctsiz;
- ret = wait_for_bit(__func__, &hc_regs->hcint, DWC2_HCINT_CHHLTD, true, - 1000, false); + ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, + 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 2c0c63322c..f5320ca298 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -133,8 +133,7 @@ static int ehci_usb_remove(struct udevice *dev) setbits_le32(&ehci->usbcmd, CMD_RESET);
/* Wait for reset */ - if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30, - false)) { + if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) { printf("Stuck on USB reset.\n"); return -ETIMEDOUT; } diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index fe2627ea93..2c8fc3c4b1 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -142,13 +142,12 @@ static int usb_phy_enable(int index, struct usb_ehci *ehci)
/* Stop then Reset */ clrbits_le32(usb_cmd, UCMD_RUN_STOP); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RUN_STOP, false, 10000, - false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RUN_STOP, false, 10000, false); if (ret) return ret;
setbits_le32(usb_cmd, UCMD_RESET); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RESET, false, 10000, false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RESET, false, 10000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ohci-lpc32xx.c b/drivers/usb/host/ohci-lpc32xx.c index 2f2b4b90de..44a49807a4 100644 --- a/drivers/usb/host/ohci-lpc32xx.c +++ b/drivers/usb/host/ohci-lpc32xx.c @@ -143,8 +143,8 @@ static int usbpll_setup(void) setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01)); setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);
- ret = wait_for_bit(__func__, &clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, - true, CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, + true, CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -178,8 +178,8 @@ int usb_cpu_init(void)
/* enable I2C clock */ writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl); - ret = wait_for_bit(__func__, &otg->otg_clk_sts, OTG_CLK_I2C_EN, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, OTG_CLK_I2C_EN, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -199,8 +199,8 @@ int usb_cpu_init(void) OTG_CLK_I2C_EN | OTG_CLK_HOST_EN; writel(mask, &otg->otg_clk_ctrl);
- ret = wait_for_bit(__func__, &otg->otg_clk_sts, mask, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, mask, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index d47c99644d..71202d7b03 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -55,18 +55,18 @@ static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data, setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SET_DATA0);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, + 10, false); if (ret) break; }
clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SUCCESS, true, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SUCCESS, true, + 10, false);
return ret; } diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c index f77da2ec97..c0dd689e7c 100644 --- a/drivers/video/atmel_hlcdfb.c +++ b/drivers/video/atmel_hlcdfb.c @@ -70,26 +70,26 @@ void lcd_ctrl_init(void *lcdbase)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -215,26 +215,26 @@ void lcd_ctrl_init(void *lcdbase) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -299,26 +299,26 @@ static void atmel_hlcdc_init(struct udevice *dev)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -451,26 +451,26 @@ static void atmel_hlcdc_init(struct udevice *dev) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); } diff --git a/include/wait_bit.h b/include/wait_bit.h index bde6d2cfc3..9f00e54e50 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -16,60 +16,6 @@ #include <asm/io.h>
/** - * wait_for_bit() waits for bit set/cleared in register - * - * Function polls register waiting for specific bit(s) change - * (either 0->1 or 1->0). It can fail under two conditions: - * - Timeout - * - User interaction (CTRL-C) - * Function succeeds only if all bits of masked register are set/cleared - * (depending on set option). - * - * @param prefix Prefix added to timeout messagge (message visible only - * with debug enabled) - * @param reg Register that will be read (using readl()) - * @param mask Bit(s) of register that must be active - * @param set Selects wait condition (bit set or clear) - * @param timeout_ms Timeout (in miliseconds) - * @param breakable Enables CTRL-C interruption - * @return 0 on success, -ETIMEDOUT or -EINTR on failure - */ -static inline int wait_for_bit(const char *prefix, const u32 *reg, - const u32 mask, const bool set, - const unsigned int timeout_ms, - const bool breakable) -{ - u32 val; - unsigned long start = get_timer(0); - - while (1) { - val = readl(reg); - - if (!set) - val = ~val; - - if ((val & mask) == mask) - return 0; - - if (get_timer(start) > timeout_ms) - break; - - if (breakable && ctrlc()) { - puts("Abort\n"); - return -EINTR; - } - - udelay(1); - WATCHDOG_RESET(); - } - - debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", prefix, reg, mask, - set); - - return -ETIMEDOUT; -} - -/** * wait_for_bit_x() waits for bit set/cleared in register * * Function polls register waiting for specific bit(s) change

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 51e28bf07b..e40e1c01de 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -516,6 +516,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 08c7480fda..4787454e59 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e40e1c01de..294d9f9d79 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -405,7 +405,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index 4787454e59..5a7df1c706 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: Introduce changes suggested by Daniel Schwierzeck: - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structure instead of a custom bmips_spi_hw structure. - Define constants for each SPI core. v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 494639fb01..ebc71c2e42 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3184db67f..5770b3f7cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..f0df6871d8 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + printf("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + printf("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + printf("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val, cmd; + int ret; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + printf("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + cmd = SPI_CMD_OP_START; + cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + cmd |= SPI_CMD_3WIRE_MASK; + writew_be(cmd, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + ret = wait_for_bit_8(priv->base + regs[SPI_IR_STAT], + SPI_IR_DONE_MASK, true, 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a4372b..0cab44cb8d 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643b5a..540b9fea5b 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf80e0..1662783279 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb048..f83a6ea8df 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96bef8..6e3d9c3820 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index cfc56cba37..5c091353e5 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -39,3 +39,11 @@ CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org
Please see below.
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi {
status = "okay";
spi-flash@0 {
compatible = "spi-flash";
reg = <0>;
#address-cells = <1>;
#size-cells = <1>;
spi-max-frequency = <20000000>;
};
+};
&uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index cfc56cba37..5c091353e5 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -39,3 +39,11 @@ CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_WINBOND=y
Are these definitely in the right order?
-- 2.11.0
Regards, Simon

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7616..5f85c7346f 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index 7665c78d3f..369c919ac7 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -41,3 +41,11 @@ CONFIG_RESET_BCM6345=y CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y CONFIG_WDT_BCM6345=y +CONFIG_BCM63XX_SPI=y +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y +CONFIG_DM_SPI=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_SPI_FLASH_SPANSION=y

On 11 January 2018 at 09:11, Álvaro Fernández Rojas noltari@gmail.com wrote:
It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com
v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v9: Introduce changes suggested by Simon Glass: - Fix defconfig order. v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Squash wait_bit commits. - Remove register param castings. v7: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. - Remove wait_for_bit and update callers to wait_for_bit_le32. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structs for registers v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (12): wait_bit: add 8/16/32 BE/LE versions of wait_for_bit wait_bit: use wait_for_bit_le32 and remove wait_for_bit drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/arm/mach-imx/mx6/ddr.c | 22 +- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 6 +- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +-- arch/mips/dts/brcm,bcm3380.dtsi | 17 + arch/mips/dts/brcm,bcm63268.dtsi | 17 + arch/mips/dts/brcm,bcm6338.dtsi | 17 + arch/mips/dts/brcm,bcm6348.dtsi | 17 + arch/mips/dts/brcm,bcm6358.dtsi | 17 + arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/clk/clk_pic32.c | 12 +- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 +- drivers/fpga/socfpga_arria10.c | 17 +- drivers/mmc/msm_sdhci.c | 8 +- drivers/mtd/pic32_flash.c | 4 +- drivers/mtd/spi/spi_flash.c | 5 +- drivers/net/ag7xxx.c | 16 +- drivers/net/dwc_eth_qos.c | 17 +- drivers/net/ethoc.c | 8 +- drivers/net/pic32_eth.c | 12 +- drivers/net/pic32_mdio.c | 28 +- drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 +- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/atmel_spi.c | 4 +- drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++ drivers/spi/cadence_qspi_apb.c | 14 +- drivers/spi/fsl_qspi.c | 20 +- drivers/spi/mvebu_a3700_spi.c | 20 +- drivers/usb/host/dwc2.c | 24 +- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +- drivers/usb/host/ohci-lpc32xx.c | 12 +- drivers/usb/host/xhci-rcar.c | 12 +- drivers/video/atmel_hlcdfb.c | 64 ++-- include/spi.h | 5 +- include/wait_bit.h | 81 ++--- 47 files changed, 824 insertions(+), 248 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v9: no changes. v8: no changes. v7: Introduce changes suggested by Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..bde6d2cfc3 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,66 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+/** + * wait_for_bit_x() waits for bit set/cleared in register + * + * Function polls register waiting for specific bit(s) change + * (either 0->1 or 1->0). It can fail under two conditions: + * - Timeout + * - User interaction (CTRL-C) + * Function succeeds only if all bits of masked register are set/cleared + * (depending on set option). + * + * @param reg Register that will be read (using read_x()) + * @param mask Bit(s) of register that must be active + * @param set Selects wait condition (bit set or clear) + * @param timeout_ms Timeout (in milliseconds) + * @param breakable Enables CTRL-C interruption + * @return 0 on success, -ETIMEDOUT or -EINTR on failure + */ + +#define BUILD_WAIT_FOR_BIT(sfx, type, read) \ + \ +static inline int wait_for_bit_##sfx(const void *reg, \ + const type mask, \ + const bool set, \ + const unsigned int timeout_ms, \ + const bool breakable) \ +{ \ + type val; \ + unsigned long start = get_timer(0); \ + \ + while (1) { \ + val = read(reg); \ + \ + if (!set) \ + val = ~val; \ + \ + if ((val & mask) == mask) \ + return 0; \ + \ + if (get_timer(start) > timeout_ms) \ + break; \ + \ + if (breakable && ctrlc()) { \ + puts("Abort\n"); \ + return -EINTR; \ + } \ + \ + udelay(1); \ + WATCHDOG_RESET(); \ + } \ + \ + debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \ + reg, mask, set); \ + \ + return -ETIMEDOUT; \ +} + +BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +BUILD_WAIT_FOR_BIT(le32, u32, readl) +BUILD_WAIT_FOR_BIT(be32, u32, readl_be)
#endif

wait_for_bit callers use the 32 bit LE version
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v9: no changes v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Squash wait_bit commits. - Remove register param castings. v7: Introduce changes suggested by Jagan Teki: - Remove wait_for_bit and update callers to wait_for_bit_le32.
arch/arm/mach-imx/mx6/ddr.c | 22 ++++----- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 6 +-- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +++++++-------- arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- drivers/clk/clk_pic32.c | 12 ++--- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 ++-- drivers/fpga/socfpga_arria10.c | 17 +++---- drivers/mmc/msm_sdhci.c | 8 ++-- drivers/mtd/pic32_flash.c | 4 +- drivers/net/ag7xxx.c | 16 +++---- drivers/net/dwc_eth_qos.c | 17 +++---- drivers/net/ethoc.c | 8 ++-- drivers/net/pic32_eth.c | 12 ++--- drivers/net/pic32_mdio.c | 28 ++++++------ drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 ++--- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/cadence_qspi_apb.c | 14 +++--- drivers/spi/fsl_qspi.c | 20 ++++----- drivers/spi/mvebu_a3700_spi.c | 20 +++++---- drivers/usb/host/dwc2.c | 24 +++++----- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +-- drivers/usb/host/ohci-lpc32xx.c | 12 ++--- drivers/usb/host/xhci-rcar.c | 12 ++--- drivers/video/atmel_hlcdfb.c | 64 +++++++++++++-------------- include/wait_bit.h | 54 ---------------------- 33 files changed, 205 insertions(+), 263 deletions(-)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 39dbd2f607..43b77cfa41 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -21,10 +21,10 @@ static void reset_read_data_fifos(void)
/* Reset data FIFOs twice. */ setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0);
setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); }
static void precharge_all(const bool cs0_enable, const bool cs1_enable) @@ -39,12 +39,12 @@ static void precharge_all(const bool cs0_enable, const bool cs1_enable) */ if (cs0_enable) { /* CS0 */ writel(0x04008050, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); }
if (cs1_enable) { /* CS1 */ writel(0x04008058, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); } }
@@ -146,7 +146,7 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) * 7. Upon completion of this process the MMDC de-asserts * the MPWLGCR[HW_WL_EN] */ - wait_for_bit("MMDC", &mmdc0->mpwlgcr, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwlgcr, 1 << 0, 0, 100, 0);
/* * 8. check for any errors: check both PHYs for x64 configuration, @@ -278,7 +278,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x00008028, &mmdc0->mdscr);
/* poll to make sure the con_ack bit was asserted */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0);
/* * Check MDMISC register CALIB_PER_CS to see which CS calibration @@ -312,7 +312,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * this bit until it clears to indicate completion of the write access. */ setbits_le32(&mmdc0->mpswdar0, 1); - wait_for_bit("MMDC", &mmdc0->mpswdar0, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpswdar0, 1 << 0, 0, 100, 0);
/* Set the RD_DL_ABS# bits to their default values * (will be calibrated later in the read delay-line calibration). @@ -359,7 +359,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) setbits_le32(&mmdc0->mpdgctrl0, 5 << 28);
/* Poll for completion. MPDGCTRL0[HW_DG_EN] should be 0 */ - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0);
/* * Check to see if any errors were encountered during calibration @@ -423,7 +423,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * setting MPRDDLHWCTL[HW_RD_DL_EN] = 0. Also, ensure that * no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0);
/* check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mprddlhwctl) & 0x0000000f) @@ -477,7 +477,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * by setting MPWRDLHWCTL[HW_WR_DL_EN] = 0. * Also, ensure that no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0);
/* Check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mpwrdlhwctl) & 0x0000000f) @@ -526,7 +526,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x0, &mmdc0->mdscr); /* CS0 */
/* Poll to make sure the con_ack bit is clear */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 0, 100, 0);
/* * Print out the registers that were updated as a result diff --git a/arch/arm/mach-socfpga/clock_manager.c b/arch/arm/mach-socfpga/clock_manager.c index 6b76221025..43e72a8b55 100644 --- a/arch/arm/mach-socfpga/clock_manager.c +++ b/arch/arm/mach-socfpga/clock_manager.c @@ -37,8 +37,8 @@ void cm_wait_for_lock(u32 mask) /* function to poll in the fsm busy bit */ int cm_wait_for_fsm(void) { - return wait_for_bit(__func__, (const u32 *)&clock_manager_base->stat, - CLKMGR_STAT_BUSY, false, 20000, false); + return wait_for_bit_le32(&clock_manager_base->stat, + CLKMGR_STAT_BUSY, false, 20000, false); }
int set_cpu_clk_info(void) diff --git a/arch/arm/mach-socfpga/clock_manager_gen5.c b/arch/arm/mach-socfpga/clock_manager_gen5.c index 31fd51097a..a23f3fc5d0 100644 --- a/arch/arm/mach-socfpga/clock_manager_gen5.c +++ b/arch/arm/mach-socfpga/clock_manager_gen5.c @@ -37,15 +37,13 @@ static int cm_write_with_phase(u32 value, u32 reg_address, u32 mask) int ret;
/* poll until phase is zero */ - ret = wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + ret = wait_for_bit_le32(reg_address, mask, false, 20000, false); if (ret) return ret;
writel(value, reg_address);
- return wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + return wait_for_bit_le32(reg_address, mask, false, 20000, false); }
/* diff --git a/arch/arm/mach-socfpga/reset_manager_arria10.c b/arch/arm/mach-socfpga/reset_manager_arria10.c index ae16897494..54f0ddb255 100644 --- a/arch/arm/mach-socfpga/reset_manager_arria10.c +++ b/arch/arm/mach-socfpga/reset_manager_arria10.c @@ -222,8 +222,8 @@ int socfpga_reset_deassert_bridges_handoff(void) clrbits_le32(&reset_manager_base->brgmodrst, mask_rstmgr);
/* Poll until all idleack to 0, timeout at 1000ms */ - return wait_for_bit(__func__, &sysmgr_regs->noc_idleack, mask_noc, - false, 1000, false); + return wait_for_bit_le32(&sysmgr_regs->noc_idleack, mask_noc, + false, 1000, false); }
void socfpga_reset_assert_fpga_connected_peripherals(void) @@ -343,26 +343,26 @@ int socfpga_bridges_reset(void) writel(ALT_SYSMGR_NOC_TMO_EN_SET_MSK, &sysmgr_regs->noc_timeout);
/* Poll until all idleack to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idleack, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idleack, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
/* Poll until all idlestatus to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idlestatus, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idlestatus, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
diff --git a/arch/mips/mach-ath79/ar934x/clk.c b/arch/mips/mach-ath79/ar934x/clk.c index 9b41d3de60..ba2243c9be 100644 --- a/arch/mips/mach-ath79/ar934x/clk.c +++ b/arch/mips/mach-ath79/ar934x/clk.c @@ -90,7 +90,7 @@ static void ar934x_srif_pll_cfg(void __iomem *pll_reg_base, const u32 srif_val) setbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5);
- wait_for_bit("clk", pll_reg_base + 0xc, BIT(3), 1, 10, 0); + wait_for_bit_le32(pll_reg_base + 0xc, BIT(3), 1, 10, 0);
clrbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5); diff --git a/board/samtec/vining_2000/vining_2000.c b/board/samtec/vining_2000/vining_2000.c index af1a3e75cb..cced08b8b8 100644 --- a/board/samtec/vining_2000/vining_2000.c +++ b/board/samtec/vining_2000/vining_2000.c @@ -378,7 +378,7 @@ static int read_adc(u32 *val)
/* start auto calibration */ setbits_le32(b + ADCx_GC, ADCx_GC_CAL); - ret = wait_for_bit("ADC", b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); + ret = wait_for_bit_le32(b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); if (ret) goto adc_exit;
@@ -386,7 +386,7 @@ static int read_adc(u32 *val) writel(0, b + ADCx_HC0);
/* wait for conversion */ - ret = wait_for_bit("ADC", b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); + ret = wait_for_bit_le32(b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); if (ret) goto adc_exit;
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c index f6eef314ec..177803943d 100644 --- a/drivers/clk/clk_pic32.c +++ b/drivers/clk/clk_pic32.c @@ -197,8 +197,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
/* wait till previous src change is active */ - wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE, + false, CONFIG_SYS_HZ, false);
/* parent_id */ v = readl(reg); @@ -223,8 +223,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
/* wait for divider switching to complete */ - return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false, - CONFIG_SYS_HZ, false); + return wait_for_bit_le32(reg, REFO_DIVSW_EN, false, + CONFIG_SYS_HZ, false); }
static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph) @@ -311,8 +311,8 @@ static int pic32_mpll_init(struct pic32_clk_priv *priv)
/* Wait for ready */ mask = MPLL_RDY | MPLL_VREG_RDY; - return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask, - true, get_tbclk(), false); + return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask, + true, get_tbclk(), false); }
static void pic32_clk_init(struct udevice *dev) diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index b26bbcc59f..22828fd470 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -1046,8 +1046,8 @@ static int gen3_clk_endisable(struct clk *clk, bool enable) if (ret) return ret; clrbits_le32(priv->base + SMSTPCR(reg), bitmask); - return wait_for_bit("MSTP", priv->base + MSTPSR(reg), - bitmask, 0, 100, 0); + return wait_for_bit_le32(priv->base + MSTPSR(reg), + bitmask, 0, 100, 0); } else { setbits_le32(priv->base + SMSTPCR(reg), bitmask); return 0; diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c index 6056418588..a52427c3d6 100644 --- a/drivers/ddr/microchip/ddr2.c +++ b/drivers/ddr/microchip/ddr2.c @@ -57,8 +57,8 @@ static int ddr2_phy_calib_start(void) writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
/* Wait for SCL for data byte to pass */ - return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS, - true, CONFIG_SYS_HZ, false); + return wait_for_bit_le32(&ddr2_phy->scl_start, SCL_LUBPASS, + true, CONFIG_SYS_HZ, false); }
/* DDR2 Controller initialization */ @@ -256,8 +256,8 @@ void ddr2_ctrl_init(void) writel(INIT_START, &ctrl->memcon);
/* wait for all host cmds to be transmitted */ - wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ctrl->cmdissue, CMD_VALID, false, + CONFIG_SYS_HZ, false);
/* inform all cmds issued, ready for normal operation */ writel(INIT_START | INIT_DONE, &ctrl->memcon); diff --git a/drivers/fpga/socfpga_arria10.c b/drivers/fpga/socfpga_arria10.c index 5c1a68a009..d5763965dd 100644 --- a/drivers/fpga/socfpga_arria10.c +++ b/drivers/fpga/socfpga_arria10.c @@ -62,8 +62,7 @@ int is_fpgamgr_user_mode(void)
static int wait_for_user_mode(void) { - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, ALT_FPGAMGR_IMGCFG_STAT_F2S_USERMODE_SET_MSK, 1, FPGA_TIMEOUT_MSEC, false); } @@ -115,19 +114,17 @@ static int wait_for_nconfig_pin_and_nstatus_pin(void) /* Poll until f2s_nconfig_pin and f2s_nstatus_pin; loop until de-asserted, * timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - mask, - false, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + mask, + false, FPGA_TIMEOUT_MSEC, false); }
static int wait_for_f2s_nstatus_pin(unsigned long value) { /* Poll until f2s to specific value, timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, - value, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, + value, FPGA_TIMEOUT_MSEC, false); }
/* set CD ratio */ diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 9117ab6bf9..f0661bd96c 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -109,15 +109,15 @@ static int msm_sdc_probe(struct udevice *dev)
/* Wait for reset to be written to register */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2, - SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2, + SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { printf("msm_sdhci: reset request failed\n"); return -EIO; }
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER, - SDCC_MCI_POWER_SW_RST, false, 2, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER, + SDCC_MCI_POWER_SW_RST, false, 2, false)) { printf("msm_sdhci: stuck in reset\n"); return -ETIMEDOUT; } diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c index e1a8d3bc4b..8bbf2fa9a2 100644 --- a/drivers/mtd/pic32_flash.c +++ b/drivers/mtd/pic32_flash.c @@ -66,8 +66,8 @@ static inline void flash_initiate_operation(u32 nvmop)
static int flash_wait_till_busy(const char *func, ulong timeout) { - int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw, - NVM_WR, false, timeout, false); + int ret = wait_for_bit_le32(&nvm_regs_p->ctrl.raw, + NVM_WR, false, timeout, false);
return ret ? ERR_TIMOUT : ERR_OK; } diff --git a/drivers/net/ag7xxx.c b/drivers/net/ag7xxx.c index 00e6806892..f28187058e 100644 --- a/drivers/net/ag7xxx.c +++ b/drivers/net/ag7xxx.c @@ -164,8 +164,8 @@ static int ag7xxx_switch_read(struct mii_dev *bus, int addr, int reg, u16 *val) writel(AG7XXX_ETH_MII_MGMT_CMD_READ, regs + AG7XXX_ETH_MII_MGMT_CMD);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); if (ret) return ret;
@@ -185,8 +185,8 @@ static int ag7xxx_switch_write(struct mii_dev *bus, int addr, int reg, u16 val) regs + AG7XXX_ETH_MII_MGMT_ADDRESS); writel(val, regs + AG7XXX_ETH_MII_MGMT_CTRL);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0);
return ret; } @@ -510,13 +510,13 @@ static void ag7xxx_eth_stop(struct udevice *dev)
/* Stop the TX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_TX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, + 1000, 0);
/* Stop the RX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_RX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, + 1000, 0); }
/* diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 00076cffbe..232e8034df 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -361,8 +361,9 @@ static void eqos_flush_buffer(void *buf, size_t size)
static int eqos_mdio_wait_idle(struct eqos_priv *eqos) { - return wait_for_bit(__func__, &eqos->mac_regs->mdio_address, - EQOS_MAC_MDIO_ADDRESS_GB, false, 1000000, true); + return wait_for_bit_le32(&eqos->mac_regs->mdio_address, + EQOS_MAC_MDIO_ADDRESS_GB, false, + 1000000, true); }
static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, @@ -588,15 +589,15 @@ static int eqos_calibrate_pads_tegra186(struct udevice *dev) setbits_le32(&eqos->tegra186_regs->auto_cal_config, EQOS_AUTO_CAL_CONFIG_START | EQOS_AUTO_CAL_CONFIG_ENABLE);
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); if (ret) { pr_err("calibrate didn't start"); goto failed; }
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); if (ret) { pr_err("calibrate didn't finish"); goto failed; @@ -862,8 +863,8 @@ static int eqos_start(struct udevice *dev)
eqos->reg_access_ok = true;
- ret = wait_for_bit(__func__, &eqos->dma_regs->mode, - EQOS_DMA_MODE_SWR, false, 10, false); + ret = wait_for_bit_le32(&eqos->dma_regs->mode, + EQOS_DMA_MODE_SWR, false, 10, false); if (ret) { pr_err("EQOS_DMA_MODE_SWR stuck"); goto err_stop_resets; diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a6df950081..51a6c97550 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -548,8 +548,8 @@ static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { u32 data = ethoc_read(priv, MIIRX_DATA); @@ -571,8 +571,8 @@ static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, ethoc_write(priv, MIITX_DATA, val); ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { /* reset MII command register */ diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c index 0b89911f04..7129372790 100644 --- a/drivers/net/pic32_eth.c +++ b/drivers/net/pic32_eth.c @@ -64,8 +64,8 @@ static int pic32_mii_init(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false);
/* turn controller ON to access PHY over MII */ writel(ETHCON_ON, &ectl_p->con1.set); @@ -239,8 +239,8 @@ static void pic32_ctrl_reset(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false); /* decrement received buffcnt to zero. */ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT) writel(ETHCON_BUFCDEC, &ectl_p->con1.set); @@ -375,8 +375,8 @@ static void pic32_eth_stop(struct udevice *dev) mdelay(10);
/* wait until everything is down */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - 2 * CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + 2 * CONFIG_SYS_HZ, false);
/* clear any existing interrupt event */ writel(0xffffffff, &ectl_p->irq.clr); diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c index 578fc96905..6ae5c40fa3 100644 --- a/drivers/net/pic32_mdio.c +++ b/drivers/net/pic32_mdio.c @@ -22,8 +22,8 @@ static int pic32_mdio_write(struct mii_dev *bus, struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -36,8 +36,8 @@ static int pic32_mdio_write(struct mii_dev *bus, udelay(12);
/* Wait for write to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
return 0; } @@ -48,8 +48,8 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -62,9 +62,9 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) udelay(12);
/* Wait for read to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, - MIIMIND_NOTVALID | MIIMIND_BUSY, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(&mii_regs->mind.raw, + MIIMIND_NOTVALID | MIIMIND_BUSY, + false, CONFIG_SYS_HZ, false);
/* Clear the command register */ writel(0, &mii_regs->mcmd.raw); @@ -82,22 +82,22 @@ static int pic32_mdio_reset(struct mii_dev *bus) writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, false, CONFIG_SYS_HZ, true);
/* Clear reset bit */ writel(0, &mii_regs->mcfg);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */ writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true); return 0; }
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index dc743e113d..26bd915291 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -222,8 +222,8 @@ static int ravb_reset(struct udevice *dev) writel(CCC_OPC_CONFIG, eth->iobase + RAVB_REG_CCC);
/* Check the operating mode is changed to the config mode. */ - return wait_for_bit(dev->name, (void *)eth->iobase + RAVB_REG_CSR, - CSR_OPS_CONFIG, true, 100, true); + return wait_for_bit_le32(eth->iobase + RAVB_REG_CSR, + CSR_OPS_CONFIG, true, 100, true); }
static void ravb_base_desc_init(struct ravb_priv *eth) diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 9a2a578ff9..70a2e95a8e 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -366,8 +366,8 @@ static int axi_ethernet_init(struct axidma_priv *priv) * processor mode and hence bypass in this mode */ if (!priv->eth_hasnobuf) { - err = wait_for_bit(__func__, (const u32 *)®s->is, - XAE_INT_MGTRDY_MASK, true, 200, false); + err = wait_for_bit_le32(®s->is, XAE_INT_MGTRDY_MASK, + true, 200, false); if (err) { printf("%s: Timeout\n", __func__); return 1; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 1dfd631e1a..2cc49bca92 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -192,8 +192,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, struct zynq_gem_regs *regs = priv->iobase; int err;
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -205,8 +205,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, /* Write mgtcr and wait for completion */ writel(mgtcr, ®s->phymntnc);
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -514,8 +514,8 @@ static int zynq_gem_send(struct udevice *dev, void *ptr, int len) if (priv->tx_bd->status & ZYNQ_GEM_TXBUF_EXHAUSTED) printf("TX buffers exhausted in mid frame\n");
- return wait_for_bit(__func__, ®s->txsr, ZYNQ_GEM_TSR_DONE, - true, 20000, true); + return wait_for_bit_le32(®s->txsr, ZYNQ_GEM_TSR_DONE, + true, 20000, true); }
/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ diff --git a/drivers/reset/sti-reset.c b/drivers/reset/sti-reset.c index 17786f976a..0fc5a28802 100644 --- a/drivers/reset/sti-reset.c +++ b/drivers/reset/sti-reset.c @@ -266,8 +266,8 @@ static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert) return 0;
reg = (void __iomem *)base + ch->ack_offset; - if (wait_for_bit(__func__, reg, BIT(ch->ack_bit), ctrl_val, - 1000, false)) { + if (wait_for_bit_le32(reg, BIT(ch->ack_bit), ctrl_val, + 1000, false)) { pr_err("Stuck on waiting ack reset_ctl=%p dev=%p id=%lu\n", reset_ctl, reset_ctl->dev, reset_ctl->id);
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c index b0e01aa0e5..0632d26211 100644 --- a/drivers/serial/serial_pic32.c +++ b/drivers/serial/serial_pic32.c @@ -51,8 +51,8 @@ static int pic32_serial_init(void __iomem *base, ulong clk, u32 baudrate) u32 div = DIV_ROUND_CLOSEST(clk, baudrate * 16);
/* wait for TX FIFO to empty */ - wait_for_bit(__func__, base + U_STA, UART_TX_EMPTY, - true, CONFIG_SYS_HZ, false); + wait_for_bit_le32(base + U_STA, UART_TX_EMPTY, + true, CONFIG_SYS_HZ, false);
/* send break */ writel(UART_TX_BRK, base + U_STASET); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 228e714e09..8010ab434c 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -394,8 +394,8 @@ out: * Wait until the transfer is completely done before * we deactivate CS. */ - wait_for_bit(__func__, ®_base->sr, - ATMEL_SPI_SR_TXEMPTY, true, 1000, false); + wait_for_bit_le32(®_base->sr, + ATMEL_SPI_SR_TXEMPTY, true, 1000, false);
atmel_spi_cs_deactivate(dev); } diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index e02f2217f4..dca3fdfdea 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -675,8 +675,8 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD, - CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD, + CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); if (ret) { printf("Indirect read completion error (%i)\n", ret); goto failrd; @@ -762,9 +762,9 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, bb_txbuf + rounddown(write_bytes, 4), write_bytes % 4);
- ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL, - CQSPI_REG_SDRAMLEVEL_WR_MASK << - CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, + CQSPI_REG_SDRAMLEVEL_WR_MASK << + CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); if (ret) { printf("Indirect write timed out (%i)\n", ret); goto failwr; @@ -775,8 +775,8 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR, - CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR, + CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); if (ret) { printf("Indirect write completion error (%i)\n", ret); goto failwr; diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 2f5345f1cf..5dc69a6865 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -1018,11 +1018,11 @@ static int fsl_qspi_probe(struct udevice *bus) priv->num_chipselect = plat->num_chipselect;
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); @@ -1185,11 +1185,11 @@ static int fsl_qspi_claim_bus(struct udevice *dev) priv = dev_get_priv(bus);
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index ec4907391c..d1708a8d56 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -95,8 +95,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, din_8 = din;
while (bytelen) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true,100, false); if (ret) return ret;
@@ -109,9 +110,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, writel(pending_dout, ®->dout);
if (din) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, - true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -160,8 +161,9 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Deactivate CS */ if (flags & SPI_XFER_END) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -231,8 +233,8 @@ static int mvebu_spi_probe(struct udevice *bus) /* Flush read/write FIFO */ data = readl(®->cfg); writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg); - ret = wait_for_bit(__func__, ®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, - false, 1000, false); + ret = wait_for_bit_le32(®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, + false, 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 1293e18f75..540c016412 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -108,8 +108,8 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num)
writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_TXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -127,8 +127,8 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) int ret;
writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_RXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -145,15 +145,15 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) int ret;
/* Wait for AHB master IDLE state. */ - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_AHBIDLE, - true, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, + true, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
/* Core Soft Reset */ writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_CSFTRST, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -267,8 +267,8 @@ static void dwc_otg_core_host_init(struct udevice *dev, clrsetbits_le32(®s->hc_regs[i].hcchar, DWC2_HCCHAR_EPDIR, DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = wait_for_bit(__func__, ®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN, false, 1000, false); + ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_CHEN, false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__); } @@ -783,8 +783,8 @@ int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) int ret; uint32_t hcint, hctsiz;
- ret = wait_for_bit(__func__, &hc_regs->hcint, DWC2_HCINT_CHHLTD, true, - 1000, false); + ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, + 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 2c0c63322c..f5320ca298 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -133,8 +133,7 @@ static int ehci_usb_remove(struct udevice *dev) setbits_le32(&ehci->usbcmd, CMD_RESET);
/* Wait for reset */ - if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30, - false)) { + if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) { printf("Stuck on USB reset.\n"); return -ETIMEDOUT; } diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index fe2627ea93..2c8fc3c4b1 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -142,13 +142,12 @@ static int usb_phy_enable(int index, struct usb_ehci *ehci)
/* Stop then Reset */ clrbits_le32(usb_cmd, UCMD_RUN_STOP); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RUN_STOP, false, 10000, - false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RUN_STOP, false, 10000, false); if (ret) return ret;
setbits_le32(usb_cmd, UCMD_RESET); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RESET, false, 10000, false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RESET, false, 10000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ohci-lpc32xx.c b/drivers/usb/host/ohci-lpc32xx.c index 2f2b4b90de..44a49807a4 100644 --- a/drivers/usb/host/ohci-lpc32xx.c +++ b/drivers/usb/host/ohci-lpc32xx.c @@ -143,8 +143,8 @@ static int usbpll_setup(void) setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01)); setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);
- ret = wait_for_bit(__func__, &clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, - true, CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, + true, CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -178,8 +178,8 @@ int usb_cpu_init(void)
/* enable I2C clock */ writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl); - ret = wait_for_bit(__func__, &otg->otg_clk_sts, OTG_CLK_I2C_EN, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, OTG_CLK_I2C_EN, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -199,8 +199,8 @@ int usb_cpu_init(void) OTG_CLK_I2C_EN | OTG_CLK_HOST_EN; writel(mask, &otg->otg_clk_ctrl);
- ret = wait_for_bit(__func__, &otg->otg_clk_sts, mask, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, mask, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index d47c99644d..71202d7b03 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -55,18 +55,18 @@ static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data, setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SET_DATA0);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, + 10, false); if (ret) break; }
clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SUCCESS, true, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SUCCESS, true, + 10, false);
return ret; } diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c index f77da2ec97..c0dd689e7c 100644 --- a/drivers/video/atmel_hlcdfb.c +++ b/drivers/video/atmel_hlcdfb.c @@ -70,26 +70,26 @@ void lcd_ctrl_init(void *lcdbase)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -215,26 +215,26 @@ void lcd_ctrl_init(void *lcdbase) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -299,26 +299,26 @@ static void atmel_hlcdc_init(struct udevice *dev)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -451,26 +451,26 @@ static void atmel_hlcdc_init(struct udevice *dev) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); } diff --git a/include/wait_bit.h b/include/wait_bit.h index bde6d2cfc3..9f00e54e50 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -16,60 +16,6 @@ #include <asm/io.h>
/** - * wait_for_bit() waits for bit set/cleared in register - * - * Function polls register waiting for specific bit(s) change - * (either 0->1 or 1->0). It can fail under two conditions: - * - Timeout - * - User interaction (CTRL-C) - * Function succeeds only if all bits of masked register are set/cleared - * (depending on set option). - * - * @param prefix Prefix added to timeout messagge (message visible only - * with debug enabled) - * @param reg Register that will be read (using readl()) - * @param mask Bit(s) of register that must be active - * @param set Selects wait condition (bit set or clear) - * @param timeout_ms Timeout (in miliseconds) - * @param breakable Enables CTRL-C interruption - * @return 0 on success, -ETIMEDOUT or -EINTR on failure - */ -static inline int wait_for_bit(const char *prefix, const u32 *reg, - const u32 mask, const bool set, - const unsigned int timeout_ms, - const bool breakable) -{ - u32 val; - unsigned long start = get_timer(0); - - while (1) { - val = readl(reg); - - if (!set) - val = ~val; - - if ((val & mask) == mask) - return 0; - - if (get_timer(start) > timeout_ms) - break; - - if (breakable && ctrlc()) { - puts("Abort\n"); - return -EINTR; - } - - udelay(1); - WATCHDOG_RESET(); - } - - debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", prefix, reg, mask, - set); - - return -ETIMEDOUT; -} - -/** * wait_for_bit_x() waits for bit set/cleared in register * * Function polls register waiting for specific bit(s) change

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 51e28bf07b..e40e1c01de 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -516,6 +516,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 08c7480fda..4787454e59 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e40e1c01de..294d9f9d79 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -405,7 +405,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index 4787454e59..5a7df1c706 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v9: no changes v8: no changes v7: Introduce changes suggested by Daniel Schwierzeck: - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structure instead of a custom bmips_spi_hw structure. - Define constants for each SPI core. v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 494639fb01..ebc71c2e42 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3184db67f..5770b3f7cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..f0df6871d8 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + printf("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + printf("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + printf("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val, cmd; + int ret; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + printf("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + cmd = SPI_CMD_OP_START; + cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + cmd |= SPI_CMD_3WIRE_MASK; + writew_be(cmd, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + ret = wait_for_bit_8(priv->base + regs[SPI_IR_STAT], + SPI_IR_DONE_MASK, true, 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a4372b..0cab44cb8d 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643b5a..540b9fea5b 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf80e0..1662783279 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb048..f83a6ea8df 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96bef8..6e3d9c3820 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: Introduce changes suggested by Simon Glass: - Fix defconfig order. v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index cfc56cba37..07a125cec6 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -26,6 +26,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set # CONFIG_CMD_LOADS is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_MISC is not set @@ -34,8 +36,14 @@ CONFIG_DM_GPIO=y CONFIG_BCM6345_GPIO=y CONFIG_LED=y CONFIG_LED_GPIO=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_SPI_FLASH_MTD=y CONFIG_DM_RESET=y CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_BCM63XX_SPI=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v9: Introduce changes suggested by Simon Glass: - Fix defconfig order. v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7616..5f85c7346f 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index 7665c78d3f..fb998f03bf 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -25,6 +25,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set # CONFIG_CMD_LOADS is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_MISC is not set @@ -35,9 +37,15 @@ CONFIG_LED=y CONFIG_LED_BCM6328=y CONFIG_LED_BLINK=y CONFIG_LED_GPIO=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_MTD=y CONFIG_DM_RESET=y CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_BCM63XX_SPI=y CONFIG_WDT_BCM6345=y

On Sat, Jan 20, 2018 at 6:41 AM, Álvaro Fernández Rojas noltari@gmail.com wrote:
BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v9: Introduce changes suggested by Simon Glass:
- Fix defconfig order.
v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck:
- Squash wait_bit commits.
- Remove register param castings.
v7: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck:
- Use const void* reg for compatibility with 64 bit systems.
- Remove prefix and use __func__ instead.
- Remove wait_for_bit and update callers to wait_for_bit_le32.
v6: Introduce changes suggested by Jagan Teki:
- Use cmd instead of val to avoid confusions.
- Switch to wait_for_bit instead of infinite loop.
v5: Introduce changes suggested by Jagan Teki:
- Use long structs for registers
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (12): wait_bit: add 8/16/32 BE/LE versions of wait_for_bit wait_bit: use wait_for_bit_le32 and remove wait_for_bit drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
Applied to u-boot-spi/master, thanks!

BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v10: Introduce changes reported by Tom Rini & Daniel Schwierzeck: - Fix undefined BE read functions v9: Introduce changes suggested by Simon Glass: - Fix defconfig order. v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Squash wait_bit commits. - Remove register param castings. v7: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. - Remove wait_for_bit and update callers to wait_for_bit_le32. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structs for registers v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size. Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (12): wait_bit: add 8/16/32 BE/LE versions of wait_for_bit wait_bit: use wait_for_bit_le32 and remove wait_for_bit drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
arch/arm/mach-imx/mx6/ddr.c | 22 +- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 6 +- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +-- arch/mips/dts/brcm,bcm3380.dtsi | 17 + arch/mips/dts/brcm,bcm63268.dtsi | 17 + arch/mips/dts/brcm,bcm6338.dtsi | 17 + arch/mips/dts/brcm,bcm6348.dtsi | 17 + arch/mips/dts/brcm,bcm6358.dtsi | 17 + arch/mips/dts/netgear,cg3100d.dts | 12 + arch/mips/dts/sagem,f@st1704.dts | 12 + arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- configs/netgear_cg3100d_ram_defconfig | 8 + configs/sagem_f@st1704_ram_defconfig | 8 + drivers/clk/clk_pic32.c | 12 +- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 +- drivers/fpga/socfpga_arria10.c | 17 +- drivers/mmc/msm_sdhci.c | 8 +- drivers/mtd/pic32_flash.c | 4 +- drivers/mtd/spi/spi_flash.c | 5 +- drivers/net/ag7xxx.c | 16 +- drivers/net/dwc_eth_qos.c | 17 +- drivers/net/ethoc.c | 8 +- drivers/net/pic32_eth.c | 12 +- drivers/net/pic32_mdio.c | 28 +- drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 +- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/atmel_spi.c | 4 +- drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++ drivers/spi/cadence_qspi_apb.c | 14 +- drivers/spi/fsl_qspi.c | 20 +- drivers/spi/mvebu_a3700_spi.c | 20 +- drivers/usb/host/dwc2.c | 24 +- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +- drivers/usb/host/ohci-lpc32xx.c | 12 +- drivers/usb/host/xhci-rcar.c | 12 +- drivers/video/atmel_hlcdfb.c | 64 ++-- include/spi.h | 5 +- include/wait_bit.h | 85 ++--- 47 files changed, 828 insertions(+), 248 deletions(-) create mode 100644 drivers/spi/bcm63xx_spi.c

Add 8/16/32 bits and BE/LE versions of wait_for_bit. This is needed for reading registers that are not aligned to 32 bits, and for Big Endian platforms.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v10: Introduce changes reported by Tom Rini & Daniel Schwierzeck: - Fix undefined BE read functions v9: no changes. v8: no changes. v7: Introduce changes suggested by Daniel Schwierzeck: - Use const void* reg for compatibility with 64 bit systems. - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Switch to wait_for_bit instead of infinite loop.
include/wait_bit.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/include/wait_bit.h b/include/wait_bit.h index 06ad43a122..e8acfa5776 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -69,5 +69,70 @@ static inline int wait_for_bit(const char *prefix, const u32 *reg, return -ETIMEDOUT; }
+/** + * wait_for_bit_x() waits for bit set/cleared in register + * + * Function polls register waiting for specific bit(s) change + * (either 0->1 or 1->0). It can fail under two conditions: + * - Timeout + * - User interaction (CTRL-C) + * Function succeeds only if all bits of masked register are set/cleared + * (depending on set option). + * + * @param reg Register that will be read (using read_x()) + * @param mask Bit(s) of register that must be active + * @param set Selects wait condition (bit set or clear) + * @param timeout_ms Timeout (in milliseconds) + * @param breakable Enables CTRL-C interruption + * @return 0 on success, -ETIMEDOUT or -EINTR on failure + */ + +#define BUILD_WAIT_FOR_BIT(sfx, type, read) \ + \ +static inline int wait_for_bit_##sfx(const void *reg, \ + const type mask, \ + const bool set, \ + const unsigned int timeout_ms, \ + const bool breakable) \ +{ \ + type val; \ + unsigned long start = get_timer(0); \ + \ + while (1) { \ + val = read(reg); \ + \ + if (!set) \ + val = ~val; \ + \ + if ((val & mask) == mask) \ + return 0; \ + \ + if (get_timer(start) > timeout_ms) \ + break; \ + \ + if (breakable && ctrlc()) { \ + puts("Abort\n"); \ + return -EINTR; \ + } \ + \ + udelay(1); \ + WATCHDOG_RESET(); \ + } \ + \ + debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \ + reg, mask, set); \ + \ + return -ETIMEDOUT; \ +} + +BUILD_WAIT_FOR_BIT(8, u8, readb) +BUILD_WAIT_FOR_BIT(le16, u16, readw) +#ifdef readw_be +BUILD_WAIT_FOR_BIT(be16, u16, readw_be) +#endif +BUILD_WAIT_FOR_BIT(le32, u32, readl) +#ifdef readl_be +BUILD_WAIT_FOR_BIT(be32, u32, readl_be) +#endif
#endif

wait_for_bit callers use the 32 bit LE version
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v10: no changes v9: no changes v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck: - Squash wait_bit commits. - Remove register param castings. v7: Introduce changes suggested by Jagan Teki: - Remove wait_for_bit and update callers to wait_for_bit_le32.
arch/arm/mach-imx/mx6/ddr.c | 22 ++++----- arch/arm/mach-socfpga/clock_manager.c | 4 +- arch/arm/mach-socfpga/clock_manager_gen5.c | 6 +-- arch/arm/mach-socfpga/reset_manager_arria10.c | 36 +++++++-------- arch/mips/mach-ath79/ar934x/clk.c | 2 +- board/samtec/vining_2000/vining_2000.c | 4 +- drivers/clk/clk_pic32.c | 12 ++--- drivers/clk/renesas/clk-rcar-gen3.c | 4 +- drivers/ddr/microchip/ddr2.c | 8 ++-- drivers/fpga/socfpga_arria10.c | 17 +++---- drivers/mmc/msm_sdhci.c | 8 ++-- drivers/mtd/pic32_flash.c | 4 +- drivers/net/ag7xxx.c | 16 +++---- drivers/net/dwc_eth_qos.c | 17 +++---- drivers/net/ethoc.c | 8 ++-- drivers/net/pic32_eth.c | 12 ++--- drivers/net/pic32_mdio.c | 28 ++++++------ drivers/net/ravb.c | 4 +- drivers/net/xilinx_axi_emac.c | 4 +- drivers/net/zynq_gem.c | 12 ++--- drivers/reset/sti-reset.c | 4 +- drivers/serial/serial_pic32.c | 4 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/cadence_qspi_apb.c | 14 +++--- drivers/spi/fsl_qspi.c | 20 ++++----- drivers/spi/mvebu_a3700_spi.c | 20 +++++---- drivers/usb/host/dwc2.c | 24 +++++----- drivers/usb/host/ehci-msm.c | 3 +- drivers/usb/host/ehci-mx6.c | 5 +-- drivers/usb/host/ohci-lpc32xx.c | 12 ++--- drivers/usb/host/xhci-rcar.c | 12 ++--- drivers/video/atmel_hlcdfb.c | 64 +++++++++++++-------------- include/wait_bit.h | 54 ---------------------- 33 files changed, 205 insertions(+), 263 deletions(-)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 39dbd2f607..43b77cfa41 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -21,10 +21,10 @@ static void reset_read_data_fifos(void)
/* Reset data FIFOs twice. */ setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0);
setbits_le32(&mmdc0->mpdgctrl0, 1 << 31); - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0); }
static void precharge_all(const bool cs0_enable, const bool cs1_enable) @@ -39,12 +39,12 @@ static void precharge_all(const bool cs0_enable, const bool cs1_enable) */ if (cs0_enable) { /* CS0 */ writel(0x04008050, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); }
if (cs1_enable) { /* CS1 */ writel(0x04008058, &mmdc0->mdscr); - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0); } }
@@ -146,7 +146,7 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) * 7. Upon completion of this process the MMDC de-asserts * the MPWLGCR[HW_WL_EN] */ - wait_for_bit("MMDC", &mmdc0->mpwlgcr, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwlgcr, 1 << 0, 0, 100, 0);
/* * 8. check for any errors: check both PHYs for x64 configuration, @@ -278,7 +278,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x00008028, &mmdc0->mdscr);
/* poll to make sure the con_ack bit was asserted */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 1, 100, 0);
/* * Check MDMISC register CALIB_PER_CS to see which CS calibration @@ -312,7 +312,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * this bit until it clears to indicate completion of the write access. */ setbits_le32(&mmdc0->mpswdar0, 1); - wait_for_bit("MMDC", &mmdc0->mpswdar0, 1 << 0, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpswdar0, 1 << 0, 0, 100, 0);
/* Set the RD_DL_ABS# bits to their default values * (will be calibrated later in the read delay-line calibration). @@ -359,7 +359,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) setbits_le32(&mmdc0->mpdgctrl0, 5 << 28);
/* Poll for completion. MPDGCTRL0[HW_DG_EN] should be 0 */ - wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpdgctrl0, 1 << 28, 0, 100, 0);
/* * Check to see if any errors were encountered during calibration @@ -423,7 +423,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * setting MPRDDLHWCTL[HW_RD_DL_EN] = 0. Also, ensure that * no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mprddlhwctl, 1 << 4, 0, 100, 0);
/* check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mprddlhwctl) & 0x0000000f) @@ -477,7 +477,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) * by setting MPWRDLHWCTL[HW_WR_DL_EN] = 0. * Also, ensure that no error bits were set. */ - wait_for_bit("MMDC", &mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mpwrdlhwctl, 1 << 4, 0, 100, 0);
/* Check both PHYs for x64 configuration, if x32, check only PHY0 */ if (readl(&mmdc0->mpwrdlhwctl) & 0x0000000f) @@ -526,7 +526,7 @@ int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(0x0, &mmdc0->mdscr); /* CS0 */
/* Poll to make sure the con_ack bit is clear */ - wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 0, 100, 0); + wait_for_bit_le32(&mmdc0->mdscr, 1 << 14, 0, 100, 0);
/* * Print out the registers that were updated as a result diff --git a/arch/arm/mach-socfpga/clock_manager.c b/arch/arm/mach-socfpga/clock_manager.c index 6b76221025..43e72a8b55 100644 --- a/arch/arm/mach-socfpga/clock_manager.c +++ b/arch/arm/mach-socfpga/clock_manager.c @@ -37,8 +37,8 @@ void cm_wait_for_lock(u32 mask) /* function to poll in the fsm busy bit */ int cm_wait_for_fsm(void) { - return wait_for_bit(__func__, (const u32 *)&clock_manager_base->stat, - CLKMGR_STAT_BUSY, false, 20000, false); + return wait_for_bit_le32(&clock_manager_base->stat, + CLKMGR_STAT_BUSY, false, 20000, false); }
int set_cpu_clk_info(void) diff --git a/arch/arm/mach-socfpga/clock_manager_gen5.c b/arch/arm/mach-socfpga/clock_manager_gen5.c index 31fd51097a..a23f3fc5d0 100644 --- a/arch/arm/mach-socfpga/clock_manager_gen5.c +++ b/arch/arm/mach-socfpga/clock_manager_gen5.c @@ -37,15 +37,13 @@ static int cm_write_with_phase(u32 value, u32 reg_address, u32 mask) int ret;
/* poll until phase is zero */ - ret = wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + ret = wait_for_bit_le32(reg_address, mask, false, 20000, false); if (ret) return ret;
writel(value, reg_address);
- return wait_for_bit(__func__, (const u32 *)reg_address, mask, - false, 20000, false); + return wait_for_bit_le32(reg_address, mask, false, 20000, false); }
/* diff --git a/arch/arm/mach-socfpga/reset_manager_arria10.c b/arch/arm/mach-socfpga/reset_manager_arria10.c index ae16897494..54f0ddb255 100644 --- a/arch/arm/mach-socfpga/reset_manager_arria10.c +++ b/arch/arm/mach-socfpga/reset_manager_arria10.c @@ -222,8 +222,8 @@ int socfpga_reset_deassert_bridges_handoff(void) clrbits_le32(&reset_manager_base->brgmodrst, mask_rstmgr);
/* Poll until all idleack to 0, timeout at 1000ms */ - return wait_for_bit(__func__, &sysmgr_regs->noc_idleack, mask_noc, - false, 1000, false); + return wait_for_bit_le32(&sysmgr_regs->noc_idleack, mask_noc, + false, 1000, false); }
void socfpga_reset_assert_fpga_connected_peripherals(void) @@ -343,26 +343,26 @@ int socfpga_bridges_reset(void) writel(ALT_SYSMGR_NOC_TMO_EN_SET_MSK, &sysmgr_regs->noc_timeout);
/* Poll until all idleack to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idleack, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idleack, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
/* Poll until all idlestatus to 1 */ - ret = wait_for_bit(__func__, &sysmgr_regs->noc_idlestatus, - ALT_SYSMGR_NOC_H2F_SET_MSK | - ALT_SYSMGR_NOC_LWH2F_SET_MSK | - ALT_SYSMGR_NOC_F2H_SET_MSK | - ALT_SYSMGR_NOC_F2SDR0_SET_MSK | - ALT_SYSMGR_NOC_F2SDR1_SET_MSK | - ALT_SYSMGR_NOC_F2SDR2_SET_MSK, - true, 10000, false); + ret = wait_for_bit_le32(&sysmgr_regs->noc_idlestatus, + ALT_SYSMGR_NOC_H2F_SET_MSK | + ALT_SYSMGR_NOC_LWH2F_SET_MSK | + ALT_SYSMGR_NOC_F2H_SET_MSK | + ALT_SYSMGR_NOC_F2SDR0_SET_MSK | + ALT_SYSMGR_NOC_F2SDR1_SET_MSK | + ALT_SYSMGR_NOC_F2SDR2_SET_MSK, + true, 10000, false); if (ret) return ret;
diff --git a/arch/mips/mach-ath79/ar934x/clk.c b/arch/mips/mach-ath79/ar934x/clk.c index 9b41d3de60..ba2243c9be 100644 --- a/arch/mips/mach-ath79/ar934x/clk.c +++ b/arch/mips/mach-ath79/ar934x/clk.c @@ -90,7 +90,7 @@ static void ar934x_srif_pll_cfg(void __iomem *pll_reg_base, const u32 srif_val) setbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5);
- wait_for_bit("clk", pll_reg_base + 0xc, BIT(3), 1, 10, 0); + wait_for_bit_le32(pll_reg_base + 0xc, BIT(3), 1, 10, 0);
clrbits_be32(pll_reg_base + 0x8, BIT(30)); udelay(5); diff --git a/board/samtec/vining_2000/vining_2000.c b/board/samtec/vining_2000/vining_2000.c index af1a3e75cb..cced08b8b8 100644 --- a/board/samtec/vining_2000/vining_2000.c +++ b/board/samtec/vining_2000/vining_2000.c @@ -378,7 +378,7 @@ static int read_adc(u32 *val)
/* start auto calibration */ setbits_le32(b + ADCx_GC, ADCx_GC_CAL); - ret = wait_for_bit("ADC", b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); + ret = wait_for_bit_le32(b + ADCx_GC, ADCx_GC_CAL, ADCx_GC_CAL, 10, 0); if (ret) goto adc_exit;
@@ -386,7 +386,7 @@ static int read_adc(u32 *val) writel(0, b + ADCx_HC0);
/* wait for conversion */ - ret = wait_for_bit("ADC", b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); + ret = wait_for_bit_le32(b + ADCx_HS, ADCx_HS_C0, ADCx_HS_C0, 10, 0); if (ret) goto adc_exit;
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c index f6eef314ec..177803943d 100644 --- a/drivers/clk/clk_pic32.c +++ b/drivers/clk/clk_pic32.c @@ -197,8 +197,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
/* wait till previous src change is active */ - wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE, + false, CONFIG_SYS_HZ, false);
/* parent_id */ v = readl(reg); @@ -223,8 +223,8 @@ static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
/* wait for divider switching to complete */ - return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false, - CONFIG_SYS_HZ, false); + return wait_for_bit_le32(reg, REFO_DIVSW_EN, false, + CONFIG_SYS_HZ, false); }
static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph) @@ -311,8 +311,8 @@ static int pic32_mpll_init(struct pic32_clk_priv *priv)
/* Wait for ready */ mask = MPLL_RDY | MPLL_VREG_RDY; - return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask, - true, get_tbclk(), false); + return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask, + true, get_tbclk(), false); }
static void pic32_clk_init(struct udevice *dev) diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index b26bbcc59f..22828fd470 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -1046,8 +1046,8 @@ static int gen3_clk_endisable(struct clk *clk, bool enable) if (ret) return ret; clrbits_le32(priv->base + SMSTPCR(reg), bitmask); - return wait_for_bit("MSTP", priv->base + MSTPSR(reg), - bitmask, 0, 100, 0); + return wait_for_bit_le32(priv->base + MSTPSR(reg), + bitmask, 0, 100, 0); } else { setbits_le32(priv->base + SMSTPCR(reg), bitmask); return 0; diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c index 6056418588..a52427c3d6 100644 --- a/drivers/ddr/microchip/ddr2.c +++ b/drivers/ddr/microchip/ddr2.c @@ -57,8 +57,8 @@ static int ddr2_phy_calib_start(void) writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
/* Wait for SCL for data byte to pass */ - return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS, - true, CONFIG_SYS_HZ, false); + return wait_for_bit_le32(&ddr2_phy->scl_start, SCL_LUBPASS, + true, CONFIG_SYS_HZ, false); }
/* DDR2 Controller initialization */ @@ -256,8 +256,8 @@ void ddr2_ctrl_init(void) writel(INIT_START, &ctrl->memcon);
/* wait for all host cmds to be transmitted */ - wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ctrl->cmdissue, CMD_VALID, false, + CONFIG_SYS_HZ, false);
/* inform all cmds issued, ready for normal operation */ writel(INIT_START | INIT_DONE, &ctrl->memcon); diff --git a/drivers/fpga/socfpga_arria10.c b/drivers/fpga/socfpga_arria10.c index 5c1a68a009..d5763965dd 100644 --- a/drivers/fpga/socfpga_arria10.c +++ b/drivers/fpga/socfpga_arria10.c @@ -62,8 +62,7 @@ int is_fpgamgr_user_mode(void)
static int wait_for_user_mode(void) { - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, ALT_FPGAMGR_IMGCFG_STAT_F2S_USERMODE_SET_MSK, 1, FPGA_TIMEOUT_MSEC, false); } @@ -115,19 +114,17 @@ static int wait_for_nconfig_pin_and_nstatus_pin(void) /* Poll until f2s_nconfig_pin and f2s_nstatus_pin; loop until de-asserted, * timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - mask, - false, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + mask, + false, FPGA_TIMEOUT_MSEC, false); }
static int wait_for_f2s_nstatus_pin(unsigned long value) { /* Poll until f2s to specific value, timeout at 1000ms */ - return wait_for_bit(__func__, - &fpga_manager_base->imgcfg_stat, - ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, - value, FPGA_TIMEOUT_MSEC, false); + return wait_for_bit_le32(&fpga_manager_base->imgcfg_stat, + ALT_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN_SET_MSK, + value, FPGA_TIMEOUT_MSEC, false); }
/* set CD ratio */ diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 9117ab6bf9..f0661bd96c 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -109,15 +109,15 @@ static int msm_sdc_probe(struct udevice *dev)
/* Wait for reset to be written to register */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2, - SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2, + SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { printf("msm_sdhci: reset request failed\n"); return -EIO; }
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ - if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER, - SDCC_MCI_POWER_SW_RST, false, 2, false)) { + if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER, + SDCC_MCI_POWER_SW_RST, false, 2, false)) { printf("msm_sdhci: stuck in reset\n"); return -ETIMEDOUT; } diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c index e1a8d3bc4b..8bbf2fa9a2 100644 --- a/drivers/mtd/pic32_flash.c +++ b/drivers/mtd/pic32_flash.c @@ -66,8 +66,8 @@ static inline void flash_initiate_operation(u32 nvmop)
static int flash_wait_till_busy(const char *func, ulong timeout) { - int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw, - NVM_WR, false, timeout, false); + int ret = wait_for_bit_le32(&nvm_regs_p->ctrl.raw, + NVM_WR, false, timeout, false);
return ret ? ERR_TIMOUT : ERR_OK; } diff --git a/drivers/net/ag7xxx.c b/drivers/net/ag7xxx.c index 00e6806892..f28187058e 100644 --- a/drivers/net/ag7xxx.c +++ b/drivers/net/ag7xxx.c @@ -164,8 +164,8 @@ static int ag7xxx_switch_read(struct mii_dev *bus, int addr, int reg, u16 *val) writel(AG7XXX_ETH_MII_MGMT_CMD_READ, regs + AG7XXX_ETH_MII_MGMT_CMD);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); if (ret) return ret;
@@ -185,8 +185,8 @@ static int ag7xxx_switch_write(struct mii_dev *bus, int addr, int reg, u16 val) regs + AG7XXX_ETH_MII_MGMT_ADDRESS); writel(val, regs + AG7XXX_ETH_MII_MGMT_CTRL);
- ret = wait_for_bit("ag7xxx", regs + AG7XXX_ETH_MII_MGMT_IND, - AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0); + ret = wait_for_bit_le32(regs + AG7XXX_ETH_MII_MGMT_IND, + AG7XXX_ETH_MII_MGMT_IND_BUSY, 0, 1000, 0);
return ret; } @@ -510,13 +510,13 @@ static void ag7xxx_eth_stop(struct udevice *dev)
/* Stop the TX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_TX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_TX_CTRL, ~0, 0, + 1000, 0);
/* Stop the RX DMA. */ writel(0, priv->regs + AG7XXX_ETH_DMA_RX_CTRL); - wait_for_bit("ag7xxx", priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, - 1000, 0); + wait_for_bit_le32(priv->regs + AG7XXX_ETH_DMA_RX_CTRL, ~0, 0, + 1000, 0); }
/* diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 00076cffbe..232e8034df 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -361,8 +361,9 @@ static void eqos_flush_buffer(void *buf, size_t size)
static int eqos_mdio_wait_idle(struct eqos_priv *eqos) { - return wait_for_bit(__func__, &eqos->mac_regs->mdio_address, - EQOS_MAC_MDIO_ADDRESS_GB, false, 1000000, true); + return wait_for_bit_le32(&eqos->mac_regs->mdio_address, + EQOS_MAC_MDIO_ADDRESS_GB, false, + 1000000, true); }
static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, @@ -588,15 +589,15 @@ static int eqos_calibrate_pads_tegra186(struct udevice *dev) setbits_le32(&eqos->tegra186_regs->auto_cal_config, EQOS_AUTO_CAL_CONFIG_START | EQOS_AUTO_CAL_CONFIG_ENABLE);
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); if (ret) { pr_err("calibrate didn't start"); goto failed; }
- ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, - EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); + ret = wait_for_bit_le32(&eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); if (ret) { pr_err("calibrate didn't finish"); goto failed; @@ -862,8 +863,8 @@ static int eqos_start(struct udevice *dev)
eqos->reg_access_ok = true;
- ret = wait_for_bit(__func__, &eqos->dma_regs->mode, - EQOS_DMA_MODE_SWR, false, 10, false); + ret = wait_for_bit_le32(&eqos->dma_regs->mode, + EQOS_DMA_MODE_SWR, false, 10, false); if (ret) { pr_err("EQOS_DMA_MODE_SWR stuck"); goto err_stop_resets; diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a6df950081..51a6c97550 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -548,8 +548,8 @@ static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg)); ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { u32 data = ethoc_read(priv, MIIRX_DATA); @@ -571,8 +571,8 @@ static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, ethoc_write(priv, MIITX_DATA, val); ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
- rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS), - MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false); + rc = wait_for_bit_le32(ethoc_reg(priv, MIISTATUS), + MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
if (rc == 0) { /* reset MII command register */ diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c index 0b89911f04..7129372790 100644 --- a/drivers/net/pic32_eth.c +++ b/drivers/net/pic32_eth.c @@ -64,8 +64,8 @@ static int pic32_mii_init(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false);
/* turn controller ON to access PHY over MII */ writel(ETHCON_ON, &ectl_p->con1.set); @@ -239,8 +239,8 @@ static void pic32_ctrl_reset(struct pic32eth_dev *priv) writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
/* wait till busy */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + CONFIG_SYS_HZ, false); /* decrement received buffcnt to zero. */ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT) writel(ETHCON_BUFCDEC, &ectl_p->con1.set); @@ -375,8 +375,8 @@ static void pic32_eth_stop(struct udevice *dev) mdelay(10);
/* wait until everything is down */ - wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, - 2 * CONFIG_SYS_HZ, false); + wait_for_bit_le32(&ectl_p->stat.raw, ETHSTAT_BUSY, false, + 2 * CONFIG_SYS_HZ, false);
/* clear any existing interrupt event */ writel(0xffffffff, &ectl_p->irq.clr); diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c index 578fc96905..6ae5c40fa3 100644 --- a/drivers/net/pic32_mdio.c +++ b/drivers/net/pic32_mdio.c @@ -22,8 +22,8 @@ static int pic32_mdio_write(struct mii_dev *bus, struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -36,8 +36,8 @@ static int pic32_mdio_write(struct mii_dev *bus, udelay(12);
/* Wait for write to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
return 0; } @@ -48,8 +48,8 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) struct pic32_mii_regs *mii_regs = bus->priv;
/* Wait for the previous operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Put phyaddr and regaddr into MIIMADD */ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); @@ -62,9 +62,9 @@ static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) udelay(12);
/* Wait for read to complete */ - wait_for_bit(__func__, &mii_regs->mind.raw, - MIIMIND_NOTVALID | MIIMIND_BUSY, - false, CONFIG_SYS_HZ, false); + wait_for_bit_le32(&mii_regs->mind.raw, + MIIMIND_NOTVALID | MIIMIND_BUSY, + false, CONFIG_SYS_HZ, false);
/* Clear the command register */ writel(0, &mii_regs->mcmd.raw); @@ -82,22 +82,22 @@ static int pic32_mdio_reset(struct mii_dev *bus) writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, false, CONFIG_SYS_HZ, true);
/* Clear reset bit */ writel(0, &mii_regs->mcfg);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true);
/* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */ writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
/* Wait for the operation to finish */ - wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY, - false, CONFIG_SYS_HZ, true); + wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, + false, CONFIG_SYS_HZ, true); return 0; }
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index dc743e113d..26bd915291 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -222,8 +222,8 @@ static int ravb_reset(struct udevice *dev) writel(CCC_OPC_CONFIG, eth->iobase + RAVB_REG_CCC);
/* Check the operating mode is changed to the config mode. */ - return wait_for_bit(dev->name, (void *)eth->iobase + RAVB_REG_CSR, - CSR_OPS_CONFIG, true, 100, true); + return wait_for_bit_le32(eth->iobase + RAVB_REG_CSR, + CSR_OPS_CONFIG, true, 100, true); }
static void ravb_base_desc_init(struct ravb_priv *eth) diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 9a2a578ff9..70a2e95a8e 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -366,8 +366,8 @@ static int axi_ethernet_init(struct axidma_priv *priv) * processor mode and hence bypass in this mode */ if (!priv->eth_hasnobuf) { - err = wait_for_bit(__func__, (const u32 *)®s->is, - XAE_INT_MGTRDY_MASK, true, 200, false); + err = wait_for_bit_le32(®s->is, XAE_INT_MGTRDY_MASK, + true, 200, false); if (err) { printf("%s: Timeout\n", __func__); return 1; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 1dfd631e1a..2cc49bca92 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -192,8 +192,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, struct zynq_gem_regs *regs = priv->iobase; int err;
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -205,8 +205,8 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, /* Write mgtcr and wait for completion */ writel(mgtcr, ®s->phymntnc);
- err = wait_for_bit(__func__, ®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, - true, 20000, false); + err = wait_for_bit_le32(®s->nwsr, ZYNQ_GEM_NWSR_MDIOIDLE_MASK, + true, 20000, false); if (err) return err;
@@ -514,8 +514,8 @@ static int zynq_gem_send(struct udevice *dev, void *ptr, int len) if (priv->tx_bd->status & ZYNQ_GEM_TXBUF_EXHAUSTED) printf("TX buffers exhausted in mid frame\n");
- return wait_for_bit(__func__, ®s->txsr, ZYNQ_GEM_TSR_DONE, - true, 20000, true); + return wait_for_bit_le32(®s->txsr, ZYNQ_GEM_TSR_DONE, + true, 20000, true); }
/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ diff --git a/drivers/reset/sti-reset.c b/drivers/reset/sti-reset.c index 17786f976a..0fc5a28802 100644 --- a/drivers/reset/sti-reset.c +++ b/drivers/reset/sti-reset.c @@ -266,8 +266,8 @@ static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert) return 0;
reg = (void __iomem *)base + ch->ack_offset; - if (wait_for_bit(__func__, reg, BIT(ch->ack_bit), ctrl_val, - 1000, false)) { + if (wait_for_bit_le32(reg, BIT(ch->ack_bit), ctrl_val, + 1000, false)) { pr_err("Stuck on waiting ack reset_ctl=%p dev=%p id=%lu\n", reset_ctl, reset_ctl->dev, reset_ctl->id);
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c index b0e01aa0e5..0632d26211 100644 --- a/drivers/serial/serial_pic32.c +++ b/drivers/serial/serial_pic32.c @@ -51,8 +51,8 @@ static int pic32_serial_init(void __iomem *base, ulong clk, u32 baudrate) u32 div = DIV_ROUND_CLOSEST(clk, baudrate * 16);
/* wait for TX FIFO to empty */ - wait_for_bit(__func__, base + U_STA, UART_TX_EMPTY, - true, CONFIG_SYS_HZ, false); + wait_for_bit_le32(base + U_STA, UART_TX_EMPTY, + true, CONFIG_SYS_HZ, false);
/* send break */ writel(UART_TX_BRK, base + U_STASET); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 228e714e09..8010ab434c 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -394,8 +394,8 @@ out: * Wait until the transfer is completely done before * we deactivate CS. */ - wait_for_bit(__func__, ®_base->sr, - ATMEL_SPI_SR_TXEMPTY, true, 1000, false); + wait_for_bit_le32(®_base->sr, + ATMEL_SPI_SR_TXEMPTY, true, 1000, false);
atmel_spi_cs_deactivate(dev); } diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index e02f2217f4..dca3fdfdea 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -675,8 +675,8 @@ int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD, - CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD, + CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0); if (ret) { printf("Indirect read completion error (%i)\n", ret); goto failrd; @@ -762,9 +762,9 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, bb_txbuf + rounddown(write_bytes, 4), write_bytes % 4);
- ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL, - CQSPI_REG_SDRAMLEVEL_WR_MASK << - CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL, + CQSPI_REG_SDRAMLEVEL_WR_MASK << + CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0); if (ret) { printf("Indirect write timed out (%i)\n", ret); goto failwr; @@ -775,8 +775,8 @@ int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, }
/* Check indirect done status */ - ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR, - CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); + ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR, + CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0); if (ret) { printf("Indirect write completion error (%i)\n", ret); goto failwr; diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 2f5345f1cf..5dc69a6865 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -1018,11 +1018,11 @@ static int fsl_qspi_probe(struct udevice *bus) priv->num_chipselect = plat->num_chipselect;
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); @@ -1185,11 +1185,11 @@ static int fsl_qspi_claim_bus(struct udevice *dev) priv = dev_get_priv(bus);
/* make sure controller is not busy anywhere */ - ret = wait_for_bit(__func__, &priv->regs->sr, - QSPI_SR_BUSY_MASK | - QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK, - false, 100, false); + ret = wait_for_bit_le32(&priv->regs->sr, + QSPI_SR_BUSY_MASK | + QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK, + false, 100, false);
if (ret) { debug("ERROR : The controller is busy\n"); diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index ec4907391c..d1708a8d56 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -95,8 +95,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, din_8 = din;
while (bytelen) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true,100, false); if (ret) return ret;
@@ -109,9 +110,9 @@ static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen, writel(pending_dout, ®->dout);
if (din) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, - true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -160,8 +161,9 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Deactivate CS */ if (flags & SPI_XFER_END) { - ret = wait_for_bit(__func__, ®->ctrl, - MVEBU_SPI_A3700_XFER_RDY, true, 100, false); + ret = wait_for_bit_le32(®->ctrl, + MVEBU_SPI_A3700_XFER_RDY, + true, 100, false); if (ret) return ret;
@@ -231,8 +233,8 @@ static int mvebu_spi_probe(struct udevice *bus) /* Flush read/write FIFO */ data = readl(®->cfg); writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, ®->cfg); - ret = wait_for_bit(__func__, ®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, - false, 1000, false); + ret = wait_for_bit_le32(®->cfg, MVEBU_SPI_A3700_FIFO_FLUSH, + false, 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 1293e18f75..540c016412 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -108,8 +108,8 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num)
writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_TXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -127,8 +127,8 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) int ret;
writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_RXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -145,15 +145,15 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) int ret;
/* Wait for AHB master IDLE state. */ - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_AHBIDLE, - true, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, + true, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
/* Core Soft Reset */ writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_CSFTRST, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, + false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__);
@@ -267,8 +267,8 @@ static void dwc_otg_core_host_init(struct udevice *dev, clrsetbits_le32(®s->hc_regs[i].hcchar, DWC2_HCCHAR_EPDIR, DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = wait_for_bit(__func__, ®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN, false, 1000, false); + ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_CHEN, false, 1000, false); if (ret) printf("%s: Timeout!\n", __func__); } @@ -783,8 +783,8 @@ int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) int ret; uint32_t hcint, hctsiz;
- ret = wait_for_bit(__func__, &hc_regs->hcint, DWC2_HCINT_CHHLTD, true, - 1000, false); + ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, + 1000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 2c0c63322c..f5320ca298 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -133,8 +133,7 @@ static int ehci_usb_remove(struct udevice *dev) setbits_le32(&ehci->usbcmd, CMD_RESET);
/* Wait for reset */ - if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30, - false)) { + if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) { printf("Stuck on USB reset.\n"); return -ETIMEDOUT; } diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index fe2627ea93..2c8fc3c4b1 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -142,13 +142,12 @@ static int usb_phy_enable(int index, struct usb_ehci *ehci)
/* Stop then Reset */ clrbits_le32(usb_cmd, UCMD_RUN_STOP); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RUN_STOP, false, 10000, - false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RUN_STOP, false, 10000, false); if (ret) return ret;
setbits_le32(usb_cmd, UCMD_RESET); - ret = wait_for_bit(__func__, usb_cmd, UCMD_RESET, false, 10000, false); + ret = wait_for_bit_le32(usb_cmd, UCMD_RESET, false, 10000, false); if (ret) return ret;
diff --git a/drivers/usb/host/ohci-lpc32xx.c b/drivers/usb/host/ohci-lpc32xx.c index 2f2b4b90de..44a49807a4 100644 --- a/drivers/usb/host/ohci-lpc32xx.c +++ b/drivers/usb/host/ohci-lpc32xx.c @@ -143,8 +143,8 @@ static int usbpll_setup(void) setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01)); setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);
- ret = wait_for_bit(__func__, &clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, - true, CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, + true, CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -178,8 +178,8 @@ int usb_cpu_init(void)
/* enable I2C clock */ writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl); - ret = wait_for_bit(__func__, &otg->otg_clk_sts, OTG_CLK_I2C_EN, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, OTG_CLK_I2C_EN, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
@@ -199,8 +199,8 @@ int usb_cpu_init(void) OTG_CLK_I2C_EN | OTG_CLK_HOST_EN; writel(mask, &otg->otg_clk_ctrl);
- ret = wait_for_bit(__func__, &otg->otg_clk_sts, mask, true, - CONFIG_SYS_HZ, false); + ret = wait_for_bit_le32(&otg->otg_clk_sts, mask, true, + CONFIG_SYS_HZ, false); if (ret) return ret;
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index d47c99644d..71202d7b03 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -55,18 +55,18 @@ static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data, setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_FW_SET_DATA0);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SET_DATA0, false, + 10, false); if (ret) break; }
clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE);
- ret = wait_for_bit("xhci-rcar", regs + RCAR_USB3_DL_CTRL, - RCAR_USB3_DL_CTRL_FW_SUCCESS, true, - 10, false); + ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL, + RCAR_USB3_DL_CTRL_FW_SUCCESS, true, + 10, false);
return ret; } diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c index f77da2ec97..c0dd689e7c 100644 --- a/drivers/video/atmel_hlcdfb.c +++ b/drivers/video/atmel_hlcdfb.c @@ -70,26 +70,26 @@ void lcd_ctrl_init(void *lcdbase)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -215,26 +215,26 @@ void lcd_ctrl_init(void *lcdbase) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -299,26 +299,26 @@ static void atmel_hlcdc_init(struct udevice *dev)
/* Disable DISP signal */ writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable synchronization */ writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable pixel clock */ writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); /* Disable PWM */ writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - false, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + false, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__);
@@ -451,26 +451,26 @@ static void atmel_hlcdc_init(struct udevice *dev) /* Enable LCD */ value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); value = readl(®s->lcdc_lcden); writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); - ret = wait_for_bit(__func__, ®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, - true, 1000, false); + ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, + true, 1000, false); if (ret) printf("%s: %d: Timeout!\n", __func__, __LINE__); } diff --git a/include/wait_bit.h b/include/wait_bit.h index e8acfa5776..bd021baa48 100644 --- a/include/wait_bit.h +++ b/include/wait_bit.h @@ -16,60 +16,6 @@ #include <asm/io.h>
/** - * wait_for_bit() waits for bit set/cleared in register - * - * Function polls register waiting for specific bit(s) change - * (either 0->1 or 1->0). It can fail under two conditions: - * - Timeout - * - User interaction (CTRL-C) - * Function succeeds only if all bits of masked register are set/cleared - * (depending on set option). - * - * @param prefix Prefix added to timeout messagge (message visible only - * with debug enabled) - * @param reg Register that will be read (using readl()) - * @param mask Bit(s) of register that must be active - * @param set Selects wait condition (bit set or clear) - * @param timeout_ms Timeout (in miliseconds) - * @param breakable Enables CTRL-C interruption - * @return 0 on success, -ETIMEDOUT or -EINTR on failure - */ -static inline int wait_for_bit(const char *prefix, const u32 *reg, - const u32 mask, const bool set, - const unsigned int timeout_ms, - const bool breakable) -{ - u32 val; - unsigned long start = get_timer(0); - - while (1) { - val = readl(reg); - - if (!set) - val = ~val; - - if ((val & mask) == mask) - return 0; - - if (get_timer(start) > timeout_ms) - break; - - if (breakable && ctrlc()) { - puts("Abort\n"); - return -EINTR; - } - - udelay(1); - WATCHDOG_RESET(); - } - - debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", prefix, reg, mask, - set); - - return -ETIMEDOUT; -} - -/** * wait_for_bit_x() waits for bit set/cleared in register * * Function polls register waiting for specific bit(s) change

For some SPI controllers it's not possible to keep the CS active between transfers and they are limited to a known number of bytes. This splits spi_flash reads into different iterations in order to respect the SPI controller limits.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: no changes
drivers/mtd/spi/spi_flash.c | 3 +++ include/spi.h | 3 +++ 2 files changed, 6 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 51e28bf07b..e40e1c01de 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -516,6 +516,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
+ if (spi->max_read_size) + read_len = min(read_len, spi->max_read_size); + spi_flash_addr(read_addr, cmd);
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); diff --git a/include/spi.h b/include/spi.h index 08c7480fda..4787454e59 100644 --- a/include/spi.h +++ b/include/spi.h @@ -86,6 +86,8 @@ struct dm_spi_slave_platdata { * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) * @wordlen: Size of SPI word in number of bits + * @max_read_size: If non-zero, the maximum number of bytes which can + * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. * @memory_map: Address of read-only SPI flash access. @@ -102,6 +104,7 @@ struct spi_slave { #endif uint mode; unsigned int wordlen; + unsigned int max_read_size; unsigned int max_write_size; void *memory_map;

Command bytes are part of the written bytes and they should be taken into account when sending a spi transfer.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: Fix bug introduced in v2: sizeof(cmd) vs len v2: Introduce changes requested by Simon Glass: - Always include command bytes when determining max write size.
drivers/mtd/spi/spi_flash.c | 2 +- include/spi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e40e1c01de..294d9f9d79 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -405,7 +405,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (spi->max_write_size) chunk_len = min(chunk_len, - (size_t)spi->max_write_size); + spi->max_write_size - sizeof(cmd));
spi_flash_addr(write_addr, cmd);
diff --git a/include/spi.h b/include/spi.h index 4787454e59..5a7df1c706 100644 --- a/include/spi.h +++ b/include/spi.h @@ -89,7 +89,7 @@ struct dm_spi_slave_platdata { * @max_read_size: If non-zero, the maximum number of bytes which can * be read at once. * @max_write_size: If non-zero, the maximum number of bytes which can - * be written at once, excluding command bytes. + * be written at once. * @memory_map: Address of read-only SPI flash access. * @flags: Indication of SPI flags. */

This driver is a simplified version of linux/drivers/spi/spi-bcm63xx.c
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com --- v10: no changes v9: no changes v8: no changes v7: Introduce changes suggested by Daniel Schwierzeck: - Remove prefix and use __func__ instead. v6: Introduce changes suggested by Jagan Teki: - Use cmd instead of val to avoid confusions. - Switch to wait_for_bit instead of infinite loop. v5: Introduce changes suggested by Jagan Teki: - Use long structure instead of a custom bmips_spi_hw structure. - Define constants for each SPI core. v4: Introduce changes suggested by Jagan Teki: - Add data for each HW controller instead of having two separate configs. - Also check clock and reset returns as suggested by Simon Glass for HSSPI. v3: rename BCM6338 SPI driver to BCM6348 switch to devfdt_get_addr_size_index() v2: no changes
drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/bcm63xx_spi.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 drivers/spi/bcm63xx_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 494639fb01..ebc71c2e42 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -40,6 +40,14 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321.
+config BCM63XX_SPI + bool "BCM6348 SPI driver" + depends on ARCH_BMIPS + help + Enable the BCM6348/BCM6358 SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding these Broadcom + SPI cores. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3184db67f..5770b3f7cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c new file mode 100644 index 0000000000..f0df6871d8 --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas noltari@gmail.com + * + * Derived from linux/drivers/spi/spi-bcm63xx.c: + * Copyright (C) 2009-2012 Florian Fainelli florian@openwrt.org + * Copyright (C) 2010 Tanguy Bouzeloc tanguy.bouzeloc@efixo.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <spi.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* BCM6348 SPI core */ +#define SPI_6348_CLK 0x06 +#define SPI_6348_CMD 0x00 +#define SPI_6348_CTL 0x40 +#define SPI_6348_CTL_SHIFT 6 +#define SPI_6348_FILL 0x07 +#define SPI_6348_IR_MASK 0x04 +#define SPI_6348_IR_STAT 0x02 +#define SPI_6348_RX 0x80 +#define SPI_6348_RX_SIZE 0x3f +#define SPI_6348_TX 0x41 +#define SPI_6348_TX_SIZE 0x3f + +/* BCM6358 SPI core */ +#define SPI_6358_CLK 0x706 +#define SPI_6358_CMD 0x700 +#define SPI_6358_CTL 0x000 +#define SPI_6358_CTL_SHIFT 14 +#define SPI_6358_FILL 0x707 +#define SPI_6358_IR_MASK 0x702 +#define SPI_6358_IR_STAT 0x704 +#define SPI_6358_RX 0x400 +#define SPI_6358_RX_SIZE 0x220 +#define SPI_6358_TX 0x002 +#define SPI_6358_TX_SIZE 0x21e + +/* SPI Clock register */ +#define SPI_CLK_SHIFT 0 +#define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) +#define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) +#define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) +#define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) +#define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) +#define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) +#define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) +#define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) +#define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) +#define SPI_CLK_SSOFF_SHIFT 3 +#define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) +#define SPI_CLK_BSWAP_SHIFT 7 +#define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) + +/* SPI Command register */ +#define SPI_CMD_OP_SHIFT 0 +#define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) +#define SPI_CMD_SLAVE_SHIFT 4 +#define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) +#define SPI_CMD_PREPEND_SHIFT 8 +#define SPI_CMD_PREPEND_BYTES 0xf +#define SPI_CMD_3WIRE_SHIFT 12 +#define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) + +/* SPI Control register */ +#define SPI_CTL_TYPE_FD_RW 0 +#define SPI_CTL_TYPE_HD_W 1 +#define SPI_CTL_TYPE_HD_R 2 + +/* SPI Interrupt registers */ +#define SPI_IR_DONE_SHIFT 0 +#define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) +#define SPI_IR_RXOVER_SHIFT 1 +#define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) +#define SPI_IR_TXUNDER_SHIFT 2 +#define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) +#define SPI_IR_TXOVER_SHIFT 3 +#define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) +#define SPI_IR_RXUNDER_SHIFT 4 +#define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) +#define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ + SPI_IR_RXOVER_MASK |\ + SPI_IR_TXUNDER_MASK |\ + SPI_IR_TXOVER_MASK |\ + SPI_IR_RXUNDER_MASK) + +enum bcm63xx_regs_spi { + SPI_CLK, + SPI_CMD, + SPI_CTL, + SPI_CTL_SHIFT, + SPI_FILL, + SPI_IR_MASK, + SPI_IR_STAT, + SPI_RX, + SPI_RX_SIZE, + SPI_TX, + SPI_TX_SIZE, +}; + +struct bcm63xx_spi_priv { + const unsigned long *regs; + void __iomem *base; + size_t tx_bytes; + uint8_t num_cs; +}; + +#define SPI_CLK_CNT 8 +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { + { 25000000, SPI_CLK_25MHZ }, + { 20000000, SPI_CLK_20MHZ }, + { 12500000, SPI_CLK_12_50MHZ }, + { 6250000, SPI_CLK_6_250MHZ }, + { 3125000, SPI_CLK_3_125MHZ }, + { 1563000, SPI_CLK_1_563MHZ }, + { 781000, SPI_CLK_0_781MHZ }, + { 391000, SPI_CLK_0_391MHZ } +}; + +static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + + if (cs >= priv->num_cs) { + printf("no cs %u\n", cs); + return -ENODEV; + } + + return 0; +} + +static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + + if (mode & SPI_LSB_FIRST) + setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + else + clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); + + return 0; +} + +static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(bus); + const unsigned long *regs = priv->regs; + uint8_t clk_cfg; + int i; + + /* default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + + /* find the closest clock configuration */ + for (i = 0; i < SPI_CLK_CNT; i++) { + if (speed >= bcm63xx_spi_freq_table[i][0]) { + clk_cfg = bcm63xx_spi_freq_table[i][1]; + break; + } + } + + /* write clock configuration */ + clrsetbits_8(priv->base + regs[SPI_CLK], + SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, + clk_cfg | SPI_CLK_SSOFF_2); + + return 0; +} + +/* + * BCM63xx SPI driver doesn't allow keeping CS active between transfers since + * they are HW controlled. + * However, it provides a mechanism to prepend write transfers prior to read + * transfers (with a maximum prepend of 15 bytes), which is usually enough for + * SPI-connected flashes since reading requires prepending a write transfer of + * 5 bytes. + * + * This implementation takes advantage of the prepend mechanism and combines + * multiple transfers into a single one where possible (single/multiple write + * transfer(s) followed by a final read/write transfer). + * However, it's not possible to buffer reads, which means that read transfers + * should always be done as the final ones. + * On the other hand, take into account that combining write transfers into + * a single one is just buffering and doesn't require prepend mechanism. + */ +static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + size_t data_bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* clear prepends */ + priv->tx_bytes = 0; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + } + + if (din) { + /* buffering reads not possible since cs is hw controlled */ + if (!(flags & SPI_XFER_END)) { + printf("unable to buffer reads\n"); + return -EINVAL; + } + + /* check rx size */ + if (data_bytes > regs[SPI_RX_SIZE]) { + printf("max rx bytes exceeded\n"); + return -EMSGSIZE; + } + } + + if (dout) { + /* check tx size */ + if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { + printf("max tx bytes exceeded\n"); + return -EMSGSIZE; + } + + /* copy tx data */ + memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, + dout, data_bytes); + priv->tx_bytes += data_bytes; + } + + if (flags & SPI_XFER_END) { + struct dm_spi_slave_platdata *plat = + dev_get_parent_platdata(dev); + uint16_t val, cmd; + int ret; + + /* determine control config */ + if (dout && !din) { + /* buffered write transfers */ + val = priv->tx_bytes; + val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + if (dout && din && (flags & SPI_XFER_ONCE)) { + /* full duplex read/write */ + val = data_bytes; + val |= (SPI_CTL_TYPE_FD_RW << + regs[SPI_CTL_SHIFT]); + priv->tx_bytes = 0; + } else { + /* prepended write transfer */ + val = data_bytes; + val |= (SPI_CTL_TYPE_HD_R << + regs[SPI_CTL_SHIFT]); + if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { + printf("max prepend bytes exceeded\n"); + return -EMSGSIZE; + } + } + } + + if (regs[SPI_CTL_SHIFT] >= 8) + writew_be(val, priv->base + regs[SPI_CTL]); + else + writeb_be(val, priv->base + regs[SPI_CTL]); + + /* clear interrupts */ + writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); + + /* issue the transfer */ + cmd = SPI_CMD_OP_START; + cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; + cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); + if (plat->mode & SPI_3WIRE) + cmd |= SPI_CMD_3WIRE_MASK; + writew_be(cmd, priv->base + regs[SPI_CMD]); + + /* enable interrupts */ + writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); + + ret = wait_for_bit_8(priv->base + regs[SPI_IR_STAT], + SPI_IR_DONE_MASK, true, 1000, false); + if (ret) { + printf("interrupt timeout\n"); + return ret; + } + + /* copy rx data */ + if (din) + memcpy_fromio(din, priv->base + regs[SPI_RX], + data_bytes); + } + + return 0; +} + +static const struct dm_spi_ops bcm63xx_spi_ops = { + .cs_info = bcm63xx_spi_cs_info, + .set_mode = bcm63xx_spi_set_mode, + .set_speed = bcm63xx_spi_set_speed, + .xfer = bcm63xx_spi_xfer, +}; + +static const unsigned long bcm6348_spi_regs[] = { + [SPI_CLK] = SPI_6348_CLK, + [SPI_CMD] = SPI_6348_CMD, + [SPI_CTL] = SPI_6348_CTL, + [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, + [SPI_FILL] = SPI_6348_FILL, + [SPI_IR_MASK] = SPI_6348_IR_MASK, + [SPI_IR_STAT] = SPI_6348_IR_STAT, + [SPI_RX] = SPI_6348_RX, + [SPI_RX_SIZE] = SPI_6348_RX_SIZE, + [SPI_TX] = SPI_6348_TX, + [SPI_TX_SIZE] = SPI_6348_TX_SIZE, +}; + +static const unsigned long bcm6358_spi_regs[] = { + [SPI_CLK] = SPI_6358_CLK, + [SPI_CMD] = SPI_6358_CMD, + [SPI_CTL] = SPI_6358_CTL, + [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, + [SPI_FILL] = SPI_6358_FILL, + [SPI_IR_MASK] = SPI_6358_IR_MASK, + [SPI_IR_STAT] = SPI_6358_IR_STAT, + [SPI_RX] = SPI_6358_RX, + [SPI_RX_SIZE] = SPI_6358_RX_SIZE, + [SPI_TX] = SPI_6358_TX, + [SPI_TX_SIZE] = SPI_6358_TX_SIZE, +}; + +static const struct udevice_id bcm63xx_spi_ids[] = { + { + .compatible = "brcm,bcm6348-spi", + .data = (ulong)&bcm6348_spi_regs, + }, { + .compatible = "brcm,bcm6358-spi", + .data = (ulong)&bcm6358_spi_regs, + }, { /* sentinel */ } +}; + +static int bcm63xx_spi_child_pre_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); + const unsigned long *regs = priv->regs; + struct spi_slave *slave = dev_get_parent_priv(dev); + struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); + + /* check cs */ + if (plat->cs >= priv->num_cs) { + printf("no cs %u\n", plat->cs); + return -ENODEV; + } + + /* max read/write sizes */ + slave->max_read_size = regs[SPI_RX_SIZE]; + slave->max_write_size = regs[SPI_TX_SIZE]; + + return 0; +} + +static int bcm63xx_spi_probe(struct udevice *dev) +{ + struct bcm63xx_spi_priv *priv = dev_get_priv(dev); + const unsigned long *regs = + (const unsigned long *)dev_get_driver_data(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = regs; + priv->base = ioremap(addr, size); + priv->num_cs = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "num-cs", 8); + + /* enable clock */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* initialize hardware */ + writeb_be(0, priv->base + regs[SPI_IR_MASK]); + + /* set fill register */ + writeb_be(0xff, priv->base + regs[SPI_FILL]); + + return 0; +} + +U_BOOT_DRIVER(bcm63xx_spi) = { + .name = "bcm63xx_spi", + .id = UCLASS_SPI, + .of_match = bcm63xx_spi_ids, + .ops = &bcm63xx_spi_ops, + .priv_auto_alloc_size = sizeof(struct bcm63xx_spi_priv), + .child_pre_probe = bcm63xx_spi_child_pre_probe, + .probe = bcm63xx_spi_probe, +};

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6338.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6338.dtsi b/arch/mips/dts/brcm,bcm6338.dtsi index eb51a4372b..0cab44cb8d 100644 --- a/arch/mips/dts/brcm,bcm6338.dtsi +++ b/arch/mips/dts/brcm,bcm6338.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6338";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -109,6 +113,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6338_CLK_SPI>; + resets = <&periph_rst BCM6338_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe3100 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe3100 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: rename BCM6338 SPI driver to BCM6348 v2: add spi alias
arch/mips/dts/brcm,bcm6348.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6348.dtsi b/arch/mips/dts/brcm,bcm6348.dtsi index 711b643b5a..540b9fea5b 100644 --- a/arch/mips/dts/brcm,bcm6348.dtsi +++ b/arch/mips/dts/brcm,bcm6348.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6348";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -118,6 +122,19 @@ status = "disabled"; };
+ spi: spi@fffe0c00 { + compatible = "brcm,bcm6348-spi"; + reg = <0xfffe0c00 0xc0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6348_CLK_SPI>; + resets = <&periph_rst BCM6348_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe2300 { compatible = "brcm,bcm6338-mc"; reg = <0xfffe2300 0x38>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm6358.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm6358.dtsi b/arch/mips/dts/brcm,bcm6358.dtsi index 4f63cf80e0..1662783279 100644 --- a/arch/mips/dts/brcm,bcm6358.dtsi +++ b/arch/mips/dts/brcm,bcm6358.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm6358";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0xfffe0000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@fffe0800 { + compatible = "brcm,bcm6358-spi"; + reg = <0xfffe0800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM6358_CLK_SPI>; + resets = <&periph_rst BCM6358_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <4>; + + status = "disabled"; + }; + memory-controller@fffe1200 { compatible = "brcm,bcm6358-mc"; reg = <0xfffe1200 0x4c>;

This driver manages the SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm3380.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm3380.dtsi b/arch/mips/dts/brcm,bcm3380.dtsi index 64245eb048..f83a6ea8df 100644 --- a/arch/mips/dts/brcm,bcm3380.dtsi +++ b/arch/mips/dts/brcm,bcm3380.dtsi @@ -12,6 +12,10 @@ / { compatible = "brcm,bcm3380";
+ aliases { + spi0 = &spi; + }; + cpus { reg = <0x14e00000 0x4>; #address-cells = <1>; @@ -142,6 +146,19 @@ status = "disabled"; };
+ spi: spi@14e02000 { + compatible = "brcm,bcm6358-spi"; + reg = <0x14e02000 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk0 BCM3380_CLK0_SPI>; + resets = <&periph_rst0 BCM3380_RST0_SPI>; + spi-max-frequency = <25000000>; + num-cs = <6>; + + status = "disabled"; + }; + leds: led-controller@14e00f00 { compatible = "brcm,bcm6328-leds"; reg = <0x14e00f00 0x1c>;

This driver manages the low speed SPI controller present on this SoC.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: no changes v8: no changes v7: no changes v6: no changes v5: no changes v4: no changes v3: no changes v2: add spi alias
arch/mips/dts/brcm,bcm63268.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/mips/dts/brcm,bcm63268.dtsi b/arch/mips/dts/brcm,bcm63268.dtsi index 113a96bef8..6e3d9c3820 100644 --- a/arch/mips/dts/brcm,bcm63268.dtsi +++ b/arch/mips/dts/brcm,bcm63268.dtsi @@ -13,6 +13,10 @@ / { compatible = "brcm,bcm63268";
+ aliases { + spi0 = &lsspi; + }; + cpus { reg = <0x10000000 0x4>; #address-cells = <1>; @@ -136,6 +140,19 @@ #power-domain-cells = <1>; };
+ lsspi: spi@10000800 { + compatible = "brcm,bcm6358-spi"; + reg = <0x10000800 0x70c>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&periph_clk BCM63268_CLK_SPI>; + resets = <&periph_rst BCM63268_RST_SPI>; + spi-max-frequency = <20000000>; + num-cs = <8>; + + status = "disabled"; + }; + leds: led-controller@10001900 { compatible = "brcm,bcm6328-leds"; reg = <0x10001900 0x24>;

It's a Winbond (w25x32) 4 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: Introduce changes suggested by Simon Glass: - Fix defconfig order. v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: rename BCM6338 SPI driver to BCM6348 v2: remove spi alias
arch/mips/dts/sagem,f@st1704.dts | 12 ++++++++++++ configs/sagem_f@st1704_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/sagem,f@st1704.dts b/arch/mips/dts/sagem,f@st1704.dts index be15fe5551..dd0e5b8b7c 100644 --- a/arch/mips/dts/sagem,f@st1704.dts +++ b/arch/mips/dts/sagem,f@st1704.dts @@ -44,6 +44,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/sagem_f@st1704_ram_defconfig b/configs/sagem_f@st1704_ram_defconfig index cfc56cba37..07a125cec6 100644 --- a/configs/sagem_f@st1704_ram_defconfig +++ b/configs/sagem_f@st1704_ram_defconfig @@ -26,6 +26,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set # CONFIG_CMD_LOADS is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_MISC is not set @@ -34,8 +36,14 @@ CONFIG_DM_GPIO=y CONFIG_BCM6345_GPIO=y CONFIG_LED=y CONFIG_LED_GPIO=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_SPI_FLASH_MTD=y CONFIG_DM_RESET=y CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_BCM63XX_SPI=y

It's a Spansion (s25fl064a) 8 MB SPI flash.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Daniel Schwierzeck daniel.schwierzeck@gmail.com Reviewed-by: Jagan Teki jagan@openedev.com Reviewed-by: Simon Glass sjg@chromium.org --- v10: no changes v9: Introduce changes suggested by Simon Glass: - Fix defconfig order. v8: no changes v7: no changes v6: no changes v5: sync with master v4: switch to CONFIG_BCM63XX_SPI v3: no changes v2: remove spi alias
arch/mips/dts/netgear,cg3100d.dts | 12 ++++++++++++ configs/netgear_cg3100d_ram_defconfig | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/mips/dts/netgear,cg3100d.dts b/arch/mips/dts/netgear,cg3100d.dts index db1e2e7616..5f85c7346f 100644 --- a/arch/mips/dts/netgear,cg3100d.dts +++ b/arch/mips/dts/netgear,cg3100d.dts @@ -90,6 +90,18 @@ status = "okay"; };
+&spi { + status = "okay"; + + spi-flash@0 { + compatible = "spi-flash"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <25000000>; + }; +}; + &uart0 { u-boot,dm-pre-reloc; status = "okay"; diff --git a/configs/netgear_cg3100d_ram_defconfig b/configs/netgear_cg3100d_ram_defconfig index 7665c78d3f..fb998f03bf 100644 --- a/configs/netgear_cg3100d_ram_defconfig +++ b/configs/netgear_cg3100d_ram_defconfig @@ -25,6 +25,8 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set # CONFIG_CMD_LOADS is not set +CONFIG_CMD_SF=y +CONFIG_CMD_SPI=y # CONFIG_CMD_NET is not set # CONFIG_CMD_NFS is not set # CONFIG_CMD_MISC is not set @@ -35,9 +37,15 @@ CONFIG_LED=y CONFIG_LED_BCM6328=y CONFIG_LED_BLINK=y CONFIG_LED_GPIO=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_MTD=y CONFIG_DM_RESET=y CONFIG_RESET_BCM6345=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_DM_SERIAL=y CONFIG_BCM6345_SERIAL=y +CONFIG_DM_SPI=y +CONFIG_BCM63XX_SPI=y CONFIG_WDT_BCM6345=y

On Tue, Jan 23, 2018 at 9:44 PM, Álvaro Fernández Rojas noltari@gmail.com wrote:
BCM63xx SPI controller is a bit tricky since it doesn't allow keeping CS active between transfers, so I had to modify the spi_flash driver in order to allow limiting reads.
v10: Introduce changes reported by Tom Rini & Daniel Schwierzeck:
- Fix undefined BE read functions
v9: Introduce changes suggested by Simon Glass:
- Fix defconfig order.
v8: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck:
- Squash wait_bit commits.
- Remove register param castings.
v7: Introduce changes suggested by Jagan Teki & Daniel Schwierzeck:
- Use const void* reg for compatibility with 64 bit systems.
- Remove prefix and use __func__ instead.
- Remove wait_for_bit and update callers to wait_for_bit_le32.
v6: Introduce changes suggested by Jagan Teki:
- Use cmd instead of val to avoid confusions.
- Switch to wait_for_bit instead of infinite loop.
v5: Introduce changes suggested by Jagan Teki:
- Use long structs for registers
v4: Introduce changes suggested by Jagan Teki:
- Add data for each HW controller instead of having two separate configs.
v3: Fix bug introduced in v2: sizeof(cmd) vs len. Also rename BCM6338 SPI driver to BCM6348 SPI since BCM6338 is a stripped down version of the BCM6348. Switch to devfdt_get_addr_size_index(). v2: Introduce changes requested by Simon Glass:
- Always include command bytes when determining max write size.
Also move SPI aliases from .dts to .dtsi files.
Álvaro Fernández Rojas (12): wait_bit: add 8/16/32 BE/LE versions of wait_for_bit wait_bit: use wait_for_bit_le32 and remove wait_for_bit drivers: spi: allow limiting reads drivers: spi: consider command bytes when sending transfers dm: spi: add BCM63xx SPI driver mips: bmips: add bcm63xx-spi driver support for BCM6338 mips: bmips: add bcm63xx-spi driver support for BCM6348 mips: bmips: add bcm63xx-spi driver support for BCM6358 mips: bmips: add bcm63xx-spi driver support for BCM3380 mips: bmips: add bcm63xx-spi driver support for BCM63268 mips: bmips: enable the SPI flash on the Sagem F@ST1704 mips: bmips: enable the SPI flash on the Netgear CG3100D
Applied to u-boot-spi/master, thanks!
participants (4)
-
Daniel Schwierzeck
-
Jagan Teki
-
Simon Glass
-
Álvaro Fernández Rojas