// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2021 Mark Kettenis * Copyright The Asahi Linux Contributors */ #include #include #include #include #include #include #include #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, };