diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 7 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/apple_spi.c | 285 | ||||
-rw-r--r-- | drivers/spi/mxc_spi.c | 2 | ||||
-rw-r--r-- | drivers/spi/zynq_qspi.c | 91 |
5 files changed, 376 insertions, 10 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d07e9a28af8..0a6a85f9c48 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -50,6 +50,13 @@ config ALTERA_SPI IP core. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config APPLE_SPI + bool "Apple SPI driver" + default y if ARCH_APPLE + help + Enable the Apple SPI driver. This driver can be used to + access the SPI flash and keyboard on machines based on Apple SoCs. + config ATCSPI200_SPI bool "Andestech ATCSPI200 SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d2f24bccefd..bea746f3e3e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SPI_MEM) += spi-mem-nodm.o endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o +obj-$(CONFIG_APPLE_SPI) += apple_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o diff --git a/drivers/spi/apple_spi.c b/drivers/spi/apple_spi.c new file mode 100644 index 00000000000..f35f5af1f6f --- /dev/null +++ b/drivers/spi/apple_spi.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> + * Copyright The Asahi Linux Contributors + */ + +#include <common.h> +#include <dm.h> +#include <clk.h> +#include <spi.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/delay.h> + +#define APPLE_SPI_CTRL 0x000 +#define APPLE_SPI_CTRL_RUN BIT(0) +#define APPLE_SPI_CTRL_TX_RESET BIT(2) +#define APPLE_SPI_CTRL_RX_RESET BIT(3) + +#define APPLE_SPI_CFG 0x004 +#define APPLE_SPI_CFG_CPHA BIT(1) +#define APPLE_SPI_CFG_CPOL BIT(2) +#define APPLE_SPI_CFG_MODE GENMASK(6, 5) +#define APPLE_SPI_CFG_MODE_POLLED 0 +#define APPLE_SPI_CFG_MODE_IRQ 1 +#define APPLE_SPI_CFG_MODE_DMA 2 +#define APPLE_SPI_CFG_IE_RXCOMPLETE BIT(7) +#define APPLE_SPI_CFG_IE_TXRXTHRESH BIT(8) +#define APPLE_SPI_CFG_LSB_FIRST BIT(13) +#define APPLE_SPI_CFG_WORD_SIZE GENMASK(16, 15) +#define APPLE_SPI_CFG_WORD_SIZE_8B 0 +#define APPLE_SPI_CFG_WORD_SIZE_16B 1 +#define APPLE_SPI_CFG_WORD_SIZE_32B 2 +#define APPLE_SPI_CFG_FIFO_THRESH GENMASK(18, 17) +#define APPLE_SPI_CFG_FIFO_THRESH_8B 0 +#define APPLE_SPI_CFG_FIFO_THRESH_4B 1 +#define APPLE_SPI_CFG_FIFO_THRESH_1B 2 +#define APPLE_SPI_CFG_IE_TXCOMPLETE BIT(21) + +#define APPLE_SPI_STATUS 0x008 +#define APPLE_SPI_STATUS_RXCOMPLETE BIT(0) +#define APPLE_SPI_STATUS_TXRXTHRESH BIT(1) +#define APPLE_SPI_STATUS_TXCOMPLETE BIT(2) + +#define APPLE_SPI_PIN 0x00c +#define APPLE_SPI_PIN_KEEP_MOSI BIT(0) +#define APPLE_SPI_PIN_CS BIT(1) + +#define APPLE_SPI_TXDATA 0x010 +#define APPLE_SPI_RXDATA 0x020 +#define APPLE_SPI_CLKDIV 0x030 +#define APPLE_SPI_CLKDIV_MIN 0x002 +#define APPLE_SPI_CLKDIV_MAX 0x7ff +#define APPLE_SPI_RXCNT 0x034 +#define APPLE_SPI_WORD_DELAY 0x038 +#define APPLE_SPI_TXCNT 0x04c + +#define APPLE_SPI_FIFOSTAT 0x10c +#define APPLE_SPI_FIFOSTAT_TXFULL BIT(4) +#define APPLE_SPI_FIFOSTAT_LEVEL_TX GENMASK(15, 8) +#define APPLE_SPI_FIFOSTAT_RXEMPTY BIT(20) +#define APPLE_SPI_FIFOSTAT_LEVEL_RX GENMASK(31, 24) + +#define APPLE_SPI_IE_XFER 0x130 +#define APPLE_SPI_IF_XFER 0x134 +#define APPLE_SPI_XFER_RXCOMPLETE BIT(0) +#define APPLE_SPI_XFER_TXCOMPLETE BIT(1) + +#define APPLE_SPI_IE_FIFO 0x138 +#define APPLE_SPI_IF_FIFO 0x13c +#define APPLE_SPI_FIFO_RXTHRESH BIT(4) +#define APPLE_SPI_FIFO_TXTHRESH BIT(5) +#define APPLE_SPI_FIFO_RXFULL BIT(8) +#define APPLE_SPI_FIFO_TXEMPTY BIT(9) +#define APPLE_SPI_FIFO_RXUNDERRUN BIT(16) +#define APPLE_SPI_FIFO_TXOVERFLOW BIT(17) + +#define APPLE_SPI_SHIFTCFG 0x150 +#define APPLE_SPI_SHIFTCFG_CLK_ENABLE BIT(0) +#define APPLE_SPI_SHIFTCFG_CS_ENABLE BIT(1) +#define APPLE_SPI_SHIFTCFG_AND_CLK_DATA BIT(8) +#define APPLE_SPI_SHIFTCFG_CS_AS_DATA BIT(9) +#define APPLE_SPI_SHIFTCFG_TX_ENABLE BIT(10) +#define APPLE_SPI_SHIFTCFG_RX_ENABLE BIT(11) +#define APPLE_SPI_SHIFTCFG_BITS GENMASK(21, 16) +#define APPLE_SPI_SHIFTCFG_OVERRIDE_CS BIT(24) + +#define APPLE_SPI_PINCFG 0x154 +#define APPLE_SPI_PINCFG_KEEP_CLK BIT(0) +#define APPLE_SPI_PINCFG_KEEP_CS BIT(1) +#define APPLE_SPI_PINCFG_KEEP_MOSI BIT(2) +#define APPLE_SPI_PINCFG_CLK_IDLE_VAL BIT(8) +#define APPLE_SPI_PINCFG_CS_IDLE_VAL BIT(9) +#define APPLE_SPI_PINCFG_MOSI_IDLE_VAL BIT(10) + +#define APPLE_SPI_DELAY_PRE 0x160 +#define APPLE_SPI_DELAY_POST 0x168 +#define APPLE_SPI_DELAY_ENABLE BIT(0) +#define APPLE_SPI_DELAY_NO_INTERBYTE BIT(1) +#define APPLE_SPI_DELAY_SET_SCK BIT(4) +#define APPLE_SPI_DELAY_SET_MOSI BIT(6) +#define APPLE_SPI_DELAY_SCK_VAL BIT(8) +#define APPLE_SPI_DELAY_MOSI_VAL BIT(12) + +#define APPLE_SPI_FIFO_DEPTH 16 + +#define APPLE_SPI_TIMEOUT_MS 200 + +struct apple_spi_priv { + void *base; + u32 clkfreq; /* Input clock frequency */ +}; + +static void apple_spi_set_cs(struct apple_spi_priv *priv, int on) +{ + writel(on ? 0 : APPLE_SPI_PIN_CS, priv->base + APPLE_SPI_PIN); +} + +/* Fill Tx FIFO. */ +static void apple_spi_tx(struct apple_spi_priv *priv, uint *len, + const void **dout) +{ + const u8 *out = *dout; + u32 data, fifostat; + uint count; + + fifostat = readl(priv->base + APPLE_SPI_FIFOSTAT); + count = APPLE_SPI_FIFO_DEPTH - + FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_TX, fifostat); + while (*len > 0 && count > 0) { + data = out ? *out++ : 0; + writel(data, priv->base + APPLE_SPI_TXDATA); + (*len)--; + count--; + } + + *dout = out; +} + +/* Empty Rx FIFO. */ +static void apple_spi_rx(struct apple_spi_priv *priv, uint *len, + void **din) +{ + u8 *in = *din; + u32 data, fifostat; + uint count; + + fifostat = readl(priv->base + APPLE_SPI_FIFOSTAT); + count = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_RX, fifostat); + while (*len > 0 && count > 0) { + data = readl(priv->base + APPLE_SPI_RXDATA); + if (in) + *in++ = data; + (*len)--; + count--; + } + + *din = in; +} + +static int apple_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct apple_spi_priv *priv = dev_get_priv(dev->parent); + unsigned long start = get_timer(0); + uint txlen, rxlen; + int ret = 0; + + if ((bitlen % 8) != 0) + return -EINVAL; + txlen = rxlen = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) + apple_spi_set_cs(priv, 1); + + if (txlen > 0) { + /* Reset FIFOs */ + writel(APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET, + priv->base + APPLE_SPI_CTRL); + + /* Set the transfer length */ + writel(txlen, priv->base + APPLE_SPI_TXCNT); + writel(rxlen, priv->base + APPLE_SPI_RXCNT); + + /* Prime transmit FIFO */ + apple_spi_tx(priv, &txlen, &dout); + + /* Start transfer */ + writel(APPLE_SPI_CTRL_RUN, priv->base + APPLE_SPI_CTRL); + + while ((txlen > 0 || rxlen > 0)) { + apple_spi_rx(priv, &rxlen, &din); + apple_spi_tx(priv, &txlen, &dout); + + if (get_timer(start) > APPLE_SPI_TIMEOUT_MS) { + ret = -ETIMEDOUT; + break; + } + } + + /* Stop transfer. */ + writel(0, priv->base + APPLE_SPI_CTRL); + } + + if (flags & SPI_XFER_END) + apple_spi_set_cs(priv, 0); + + return ret; +} + +static int apple_spi_set_speed(struct udevice *dev, uint speed) +{ + struct apple_spi_priv *priv = dev_get_priv(dev); + u32 div; + + div = DIV_ROUND_UP(priv->clkfreq, speed); + if (div < APPLE_SPI_CLKDIV_MIN) + div = APPLE_SPI_CLKDIV_MIN; + if (div > APPLE_SPI_CLKDIV_MAX) + div = APPLE_SPI_CLKDIV_MAX; + + writel(div, priv->base + APPLE_SPI_CLKDIV); + + return 0; +} + +static int apple_spi_set_mode(struct udevice *bus, uint mode) +{ + return 0; +} + +struct dm_spi_ops apple_spi_ops = { + .xfer = apple_spi_xfer, + .set_speed = apple_spi_set_speed, + .set_mode = apple_spi_set_mode, +}; + +static int apple_spi_probe(struct udevice *dev) +{ + struct apple_spi_priv *priv = dev_get_priv(dev); + struct clk clkdev; + int ret; + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &clkdev); + if (ret) + return ret; + priv->clkfreq = clk_get_rate(&clkdev); + + /* Set CS high (inactive) and disable override and auto-CS */ + writel(APPLE_SPI_PIN_CS, priv->base + APPLE_SPI_PIN); + writel(readl(priv->base + APPLE_SPI_SHIFTCFG) & ~APPLE_SPI_SHIFTCFG_OVERRIDE_CS, + priv->base + APPLE_SPI_SHIFTCFG); + writel((readl(priv->base + APPLE_SPI_PINCFG) & ~APPLE_SPI_PINCFG_CS_IDLE_VAL) | + APPLE_SPI_PINCFG_KEEP_CS, priv->base + APPLE_SPI_PINCFG); + + /* Reset FIFOs */ + writel(APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET, + priv->base + APPLE_SPI_CTRL); + + /* Configure defaults */ + writel(FIELD_PREP(APPLE_SPI_CFG_MODE, APPLE_SPI_CFG_MODE_IRQ) | + FIELD_PREP(APPLE_SPI_CFG_WORD_SIZE, APPLE_SPI_CFG_WORD_SIZE_8B) | + FIELD_PREP(APPLE_SPI_CFG_FIFO_THRESH, APPLE_SPI_CFG_FIFO_THRESH_8B), + priv->base + APPLE_SPI_CFG); + + return 0; +} + +static const struct udevice_id apple_spi_of_match[] = { + { .compatible = "apple,spi" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(apple_spi) = { + .name = "apple_spi", + .id = UCLASS_SPI, + .of_match = apple_spi_of_match, + .probe = apple_spi_probe, + .priv_auto = sizeof(struct apple_spi_priv), + .ops = &apple_spi_ops, +}; diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index 3c53de165dd..5adfdf8b5fc 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -391,8 +391,6 @@ int spi_xchg_single(struct mxc_spi_slave *mxcs, unsigned int bitlen, nbytes = DIV_ROUND_UP(bitlen, 8); - cnt = nbytes % 32; - if (bitlen % 32) { data = reg_read(®s->rxdata); cnt = (bitlen % 32) / 8; diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c index cf6da5340a7..b69d992b28a 100644 --- a/drivers/spi/zynq_qspi.c +++ b/drivers/spi/zynq_qspi.c @@ -16,6 +16,7 @@ #include <asm/global_data.h> #include <asm/io.h> #include <linux/bitops.h> +#include <spi-mem.h> DECLARE_GLOBAL_DATA_PTR; @@ -49,6 +50,9 @@ DECLARE_GLOBAL_DATA_PTR; #define ZYNQ_QSPI_CR_BAUD_SHIFT 3 /* Baud rate divisor shift */ #define ZYNQ_QSPI_CR_SS_SHIFT 10 /* Slave select shift */ +#define ZYNQ_QSPI_MAX_BAUD_RATE 0x7 +#define ZYNQ_QSPI_DEFAULT_BAUD_RATE 0x2 + #define ZYNQ_QSPI_FIFO_DEPTH 63 #define ZYNQ_QSPI_WAIT (CONFIG_SYS_HZ / 100) /* 10 ms */ @@ -230,12 +234,16 @@ static void zynq_qspi_read_data(struct zynq_qspi_priv *priv, u32 data, u8 size) priv->rx_buf += 1; break; case 2: - *((u16 *)priv->rx_buf) = data; - priv->rx_buf += 2; + *((u8 *)priv->rx_buf) = data; + priv->rx_buf += 1; + *((u8 *)priv->rx_buf) = (u8)(data >> 8); + priv->rx_buf += 1; break; case 3: - *((u16 *)priv->rx_buf) = data; - priv->rx_buf += 2; + *((u8 *)priv->rx_buf) = data; + priv->rx_buf += 1; + *((u8 *)priv->rx_buf) = (u8)(data >> 8); + priv->rx_buf += 1; byte3 = (u8)(data >> 16); *((u8 *)priv->rx_buf) = byte3; priv->rx_buf += 1; @@ -272,13 +280,17 @@ static void zynq_qspi_write_data(struct zynq_qspi_priv *priv, *data |= 0xFFFFFF00; break; case 2: - *data = *((u16 *)priv->tx_buf); - priv->tx_buf += 2; + *data = *((u8 *)priv->tx_buf); + priv->tx_buf += 1; + *data |= (*((u8 *)priv->tx_buf) << 8); + priv->tx_buf += 1; *data |= 0xFFFF0000; break; case 3: - *data = *((u16 *)priv->tx_buf); - priv->tx_buf += 2; + *data = *((u8 *)priv->tx_buf); + priv->tx_buf += 1; + *data |= (*((u8 *)priv->tx_buf) << 8); + priv->tx_buf += 1; *data |= (*((u8 *)priv->tx_buf) << 16); priv->tx_buf += 1; *data |= 0xFF000000; @@ -613,6 +625,9 @@ static int zynq_qspi_set_speed(struct udevice *bus, uint speed) (2 << baud_rate_val)) > speed)) baud_rate_val++; + if (baud_rate_val > ZYNQ_QSPI_MAX_BAUD_RATE) + baud_rate_val = ZYNQ_QSPI_DEFAULT_BAUD_RATE; + plat->speed_hz = speed / (2 << baud_rate_val); } confr &= ~ZYNQ_QSPI_CR_BAUD_MASK; @@ -649,12 +664,72 @@ static int zynq_qspi_set_mode(struct udevice *bus, uint mode) return 0; } +static int zynq_qspi_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + int op_len, pos = 0, ret, i; + unsigned int flag = 0; + const u8 *tx_buf = NULL; + u8 *rx_buf = NULL; + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) + rx_buf = op->data.buf.in; + else + tx_buf = op->data.buf.out; + } + + op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; + + u8 op_buf[op_len]; + + op_buf[pos++] = op->cmd.opcode; + + if (op->addr.nbytes) { + for (i = 0; i < op->addr.nbytes; i++) + op_buf[pos + i] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + + pos += op->addr.nbytes; + } + + if (op->dummy.nbytes) + memset(op_buf + pos, 0xff, op->dummy.nbytes); + + /* 1st transfer: opcode + address + dummy cycles */ + /* Make sure to set END bit if no tx or rx data messages follow */ + if (!tx_buf && !rx_buf) + flag |= SPI_XFER_END; + + ret = zynq_qspi_xfer(slave->dev, op_len * 8, op_buf, NULL, + flag | SPI_XFER_BEGIN); + if (ret) + return ret; + + /* 2nd transfer: rx or tx data path */ + if (tx_buf || rx_buf) { + ret = zynq_qspi_xfer(slave->dev, op->data.nbytes * 8, tx_buf, + rx_buf, flag | SPI_XFER_END); + if (ret) + return ret; + } + + spi_release_bus(slave); + + return 0; +} + +static const struct spi_controller_mem_ops zynq_qspi_mem_ops = { + .exec_op = zynq_qspi_exec_op, +}; + static const struct dm_spi_ops zynq_qspi_ops = { .claim_bus = zynq_qspi_claim_bus, .release_bus = zynq_qspi_release_bus, .xfer = zynq_qspi_xfer, .set_speed = zynq_qspi_set_speed, .set_mode = zynq_qspi_set_mode, + .mem_ops = &zynq_qspi_mem_ops, }; static const struct udevice_id zynq_qspi_ids[] = { |