[PATCH 1/2] spi: apple: Add driver for Apple SPI controller
Simon Glass
sjg at chromium.org
Sat Jan 22 02:40:25 CET 2022
Hi Mark,
On Sun, 16 Jan 2022 at 10:06, Mark Kettenis <kettenis at openbsd.org> wrote:
>
> Add a driver for the SPI controller integrated on Apple SoCs.
> This is necessary to support the keyboard on Apple Silicon laopts
> since their keyboard uses an Apple-specific HID over SPI protocol.
>
> Signed-off-by: Mark Kettenis <kettenis at openbsd.org>
> ---
> arch/arm/Kconfig | 2 +
> drivers/spi/Kconfig | 7 +
> drivers/spi/Makefile | 1 +
> drivers/spi/apple_spi.c | 283 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 293 insertions(+)
> create mode 100644 drivers/spi/apple_spi.c
Reviewed-by: Simon Glass <sjg at chromium.org>
Tested on: Macbook Air M1
Tested-by: Simon Glass <sjg at chromium.org>
with nits below
Shouldn't you enable SPI flash and 'sspi' and 'sf' commands as well?
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 04b4a20211..fed993a53d 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -937,6 +937,7 @@ config ARCH_APPLE
> select DM_MAILBOX
> select DM_RESET
> select DM_SERIAL
> + select DM_SPI
> select DM_USB
> select DM_VIDEO
> select IOMMU
> @@ -946,6 +947,7 @@ config ARCH_APPLE
> select POSITION_INDEPENDENT
> select POWER_DOMAIN
> select REGMAP
> + select SPI
> select SYSCON
> select SYSRESET
> select SYSRESET_WATCHDOG
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index d07e9a28af..0a6a85f9c4 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 d2f24bccef..bea746f3e3 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 0000000000..3ae89dc638
> --- /dev/null
> +++ b/drivers/spi/apple_spi.c
> @@ -0,0 +1,283 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2021 Mark Kettenis <kettenis at 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;
of the bus or the input peripheral (need scomment)
> +};
> +
> +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);
> +}
> +
> +static void apple_spi_tx(struct apple_spi_priv *priv, uint *len,
> + const void **dout)
needs comment
> +{
> + 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;
> +}
> +
> +static void apple_spi_rx(struct apple_spi_priv *priv, uint *len,
> + void **din)
same
> +{
> + 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)
s/!= 0//
> + return -EINVAL;
> + txlen = rxlen = bitlen / 8;
> +
> + if (flags & SPI_XFER_BEGIN)
> + apple_spi_set_cs(priv, 1);
> +
> + if (txlen > 0) {
Can it be less than zero?
> + /* 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)) {
same question
> + 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,
> +};
> --
> 2.34.1
>
Regards,
Simon
More information about the U-Boot
mailing list