|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | // | 
|  | // Apple SoC SPI device driver | 
|  | // | 
|  | // Copyright The Asahi Linux Contributors | 
|  | // | 
|  | // Based on spi-sifive.c, Copyright 2018 SiFive, Inc. | 
|  |  | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/bits.h> | 
|  | #include <linux/clk.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/pm_runtime.h> | 
|  | #include <linux/spi/spi.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_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 | 
|  |  | 
|  | /* | 
|  | * The slowest refclock available is 24MHz, the highest divider is 0x7ff, | 
|  | * the largest word size is 32 bits, the FIFO depth is 16, the maximum | 
|  | * intra-word delay is 0xffff refclocks. So the maximum time a transfer | 
|  | * cycle can take is: | 
|  | * | 
|  | * (0x7ff * 32 + 0xffff) * 16 / 24e6 Hz ~= 87ms | 
|  | * | 
|  | * Double it and round it up to 200ms for good measure. | 
|  | */ | 
|  | #define APPLE_SPI_TIMEOUT_MS		200 | 
|  |  | 
|  | struct apple_spi { | 
|  | void __iomem      *regs;        /* MMIO register address */ | 
|  | struct clk        *clk;         /* bus clock */ | 
|  | struct completion done;         /* wake-up from interrupt */ | 
|  | }; | 
|  |  | 
|  | static inline void reg_write(struct apple_spi *spi, int offset, u32 value) | 
|  | { | 
|  | writel_relaxed(value, spi->regs + offset); | 
|  | } | 
|  |  | 
|  | static inline u32 reg_read(struct apple_spi *spi, int offset) | 
|  | { | 
|  | return readl_relaxed(spi->regs + offset); | 
|  | } | 
|  |  | 
|  | static inline void reg_mask(struct apple_spi *spi, int offset, u32 clear, u32 set) | 
|  | { | 
|  | u32 val = reg_read(spi, offset); | 
|  |  | 
|  | val &= ~clear; | 
|  | val |= set; | 
|  | reg_write(spi, offset, val); | 
|  | } | 
|  |  | 
|  | static void apple_spi_init(struct apple_spi *spi) | 
|  | { | 
|  | /* Set CS high (inactive) and disable override and auto-CS */ | 
|  | reg_write(spi, APPLE_SPI_PIN, APPLE_SPI_PIN_CS); | 
|  | reg_mask(spi, APPLE_SPI_SHIFTCFG, APPLE_SPI_SHIFTCFG_OVERRIDE_CS, 0); | 
|  | reg_mask(spi, APPLE_SPI_PINCFG, APPLE_SPI_PINCFG_CS_IDLE_VAL, APPLE_SPI_PINCFG_KEEP_CS); | 
|  |  | 
|  | /* Reset FIFOs */ | 
|  | reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET); | 
|  |  | 
|  | /* Configure defaults */ | 
|  | reg_write(spi, APPLE_SPI_CFG, | 
|  | FIELD_PREP(APPLE_SPI_CFG_FIFO_THRESH, APPLE_SPI_CFG_FIFO_THRESH_8B) | | 
|  | FIELD_PREP(APPLE_SPI_CFG_MODE, APPLE_SPI_CFG_MODE_IRQ) | | 
|  | FIELD_PREP(APPLE_SPI_CFG_WORD_SIZE, APPLE_SPI_CFG_WORD_SIZE_8B)); | 
|  |  | 
|  | /* Disable IRQs */ | 
|  | reg_write(spi, APPLE_SPI_IE_FIFO, 0); | 
|  | reg_write(spi, APPLE_SPI_IE_XFER, 0); | 
|  |  | 
|  | /* Disable delays */ | 
|  | reg_write(spi, APPLE_SPI_DELAY_PRE, 0); | 
|  | reg_write(spi, APPLE_SPI_DELAY_POST, 0); | 
|  | } | 
|  |  | 
|  | static int apple_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *msg) | 
|  | { | 
|  | struct apple_spi *spi = spi_controller_get_devdata(ctlr); | 
|  | struct spi_device *device = msg->spi; | 
|  |  | 
|  | u32 cfg = ((device->mode & SPI_CPHA ? APPLE_SPI_CFG_CPHA : 0) | | 
|  | (device->mode & SPI_CPOL ? APPLE_SPI_CFG_CPOL : 0) | | 
|  | (device->mode & SPI_LSB_FIRST ? APPLE_SPI_CFG_LSB_FIRST : 0)); | 
|  |  | 
|  | /* Update core config */ | 
|  | reg_mask(spi, APPLE_SPI_CFG, | 
|  | APPLE_SPI_CFG_CPHA | APPLE_SPI_CFG_CPOL | APPLE_SPI_CFG_LSB_FIRST, cfg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void apple_spi_set_cs(struct spi_device *device, bool is_high) | 
|  | { | 
|  | struct apple_spi *spi = spi_controller_get_devdata(device->controller); | 
|  |  | 
|  | reg_mask(spi, APPLE_SPI_PIN, APPLE_SPI_PIN_CS, is_high ? APPLE_SPI_PIN_CS : 0); | 
|  | } | 
|  |  | 
|  | static bool apple_spi_prep_transfer(struct apple_spi *spi, struct spi_transfer *t) | 
|  | { | 
|  | u32 cr, fifo_threshold; | 
|  |  | 
|  | /* Calculate and program the clock rate */ | 
|  | cr = DIV_ROUND_UP(clk_get_rate(spi->clk), t->speed_hz); | 
|  | reg_write(spi, APPLE_SPI_CLKDIV, min_t(u32, cr, APPLE_SPI_CLKDIV_MAX)); | 
|  |  | 
|  | /* Update bits per word */ | 
|  | reg_mask(spi, APPLE_SPI_SHIFTCFG, APPLE_SPI_SHIFTCFG_BITS, | 
|  | FIELD_PREP(APPLE_SPI_SHIFTCFG_BITS, t->bits_per_word)); | 
|  |  | 
|  | /* We will want to poll if the time we need to wait is | 
|  | * less than the context switching time. | 
|  | * Let's call that threshold 5us. The operation will take: | 
|  | *    bits_per_word * fifo_threshold / hz <= 5 * 10^-6 | 
|  | *    200000 * bits_per_word * fifo_threshold <= hz | 
|  | */ | 
|  | fifo_threshold = APPLE_SPI_FIFO_DEPTH / 2; | 
|  | return (200000 * t->bits_per_word * fifo_threshold) <= t->speed_hz; | 
|  | } | 
|  |  | 
|  | static irqreturn_t apple_spi_irq(int irq, void *dev_id) | 
|  | { | 
|  | struct apple_spi *spi = dev_id; | 
|  | u32 fifo = reg_read(spi, APPLE_SPI_IF_FIFO) & reg_read(spi, APPLE_SPI_IE_FIFO); | 
|  | u32 xfer = reg_read(spi, APPLE_SPI_IF_XFER) & reg_read(spi, APPLE_SPI_IE_XFER); | 
|  |  | 
|  | if (fifo || xfer) { | 
|  | /* Disable interrupts until next transfer */ | 
|  | reg_write(spi, APPLE_SPI_IE_XFER, 0); | 
|  | reg_write(spi, APPLE_SPI_IE_FIFO, 0); | 
|  | complete(&spi->done); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | return IRQ_NONE; | 
|  | } | 
|  |  | 
|  | static int apple_spi_wait(struct apple_spi *spi, u32 fifo_bit, u32 xfer_bit, int poll) | 
|  | { | 
|  | int ret = 0; | 
|  |  | 
|  | if (poll) { | 
|  | u32 fifo, xfer; | 
|  | unsigned long timeout = jiffies + APPLE_SPI_TIMEOUT_MS * HZ / 1000; | 
|  |  | 
|  | do { | 
|  | fifo = reg_read(spi, APPLE_SPI_IF_FIFO); | 
|  | xfer = reg_read(spi, APPLE_SPI_IF_XFER); | 
|  | if (time_after(jiffies, timeout)) { | 
|  | ret = -ETIMEDOUT; | 
|  | break; | 
|  | } | 
|  | } while (!((fifo & fifo_bit) || (xfer & xfer_bit))); | 
|  | } else { | 
|  | reinit_completion(&spi->done); | 
|  | reg_write(spi, APPLE_SPI_IE_XFER, xfer_bit); | 
|  | reg_write(spi, APPLE_SPI_IE_FIFO, fifo_bit); | 
|  |  | 
|  | if (!wait_for_completion_timeout(&spi->done, | 
|  | msecs_to_jiffies(APPLE_SPI_TIMEOUT_MS))) | 
|  | ret = -ETIMEDOUT; | 
|  |  | 
|  | reg_write(spi, APPLE_SPI_IE_XFER, 0); | 
|  | reg_write(spi, APPLE_SPI_IE_FIFO, 0); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void apple_spi_tx(struct apple_spi *spi, const void **tx_ptr, u32 *left, | 
|  | unsigned int bytes_per_word) | 
|  | { | 
|  | u32 inuse, words, wrote; | 
|  |  | 
|  | if (!*tx_ptr) | 
|  | return; | 
|  |  | 
|  | inuse = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_TX, reg_read(spi, APPLE_SPI_FIFOSTAT)); | 
|  | words = wrote = min_t(u32, *left, APPLE_SPI_FIFO_DEPTH - inuse); | 
|  |  | 
|  | if (!words) | 
|  | return; | 
|  |  | 
|  | *left -= words; | 
|  |  | 
|  | switch (bytes_per_word) { | 
|  | case 1: { | 
|  | const u8 *p = *tx_ptr; | 
|  |  | 
|  | while (words--) | 
|  | reg_write(spi, APPLE_SPI_TXDATA, *p++); | 
|  | break; | 
|  | } | 
|  | case 2: { | 
|  | const u16 *p = *tx_ptr; | 
|  |  | 
|  | while (words--) | 
|  | reg_write(spi, APPLE_SPI_TXDATA, *p++); | 
|  | break; | 
|  | } | 
|  | case 4: { | 
|  | const u32 *p = *tx_ptr; | 
|  |  | 
|  | while (words--) | 
|  | reg_write(spi, APPLE_SPI_TXDATA, *p++); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | WARN_ON(1); | 
|  | } | 
|  |  | 
|  | *tx_ptr = ((u8 *)*tx_ptr) + bytes_per_word * wrote; | 
|  | } | 
|  |  | 
|  | static void apple_spi_rx(struct apple_spi *spi, void **rx_ptr, u32 *left, | 
|  | unsigned int bytes_per_word) | 
|  | { | 
|  | u32 words, read; | 
|  |  | 
|  | if (!*rx_ptr) | 
|  | return; | 
|  |  | 
|  | words = read = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_RX, reg_read(spi, APPLE_SPI_FIFOSTAT)); | 
|  | WARN_ON(words > *left); | 
|  |  | 
|  | if (!words) | 
|  | return; | 
|  |  | 
|  | *left -= min_t(u32, *left, words); | 
|  |  | 
|  | switch (bytes_per_word) { | 
|  | case 1: { | 
|  | u8 *p = *rx_ptr; | 
|  |  | 
|  | while (words--) | 
|  | *p++ = reg_read(spi, APPLE_SPI_RXDATA); | 
|  | break; | 
|  | } | 
|  | case 2: { | 
|  | u16 *p = *rx_ptr; | 
|  |  | 
|  | while (words--) | 
|  | *p++ = reg_read(spi, APPLE_SPI_RXDATA); | 
|  | break; | 
|  | } | 
|  | case 4: { | 
|  | u32 *p = *rx_ptr; | 
|  |  | 
|  | while (words--) | 
|  | *p++ = reg_read(spi, APPLE_SPI_RXDATA); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | WARN_ON(1); | 
|  | } | 
|  |  | 
|  | *rx_ptr = ((u8 *)*rx_ptr) + bytes_per_word * read; | 
|  | } | 
|  |  | 
|  | static int apple_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *device, | 
|  | struct spi_transfer *t) | 
|  | { | 
|  | struct apple_spi *spi = spi_controller_get_devdata(ctlr); | 
|  | bool poll = apple_spi_prep_transfer(spi, t); | 
|  | const void *tx_ptr = t->tx_buf; | 
|  | void *rx_ptr = t->rx_buf; | 
|  | unsigned int bytes_per_word; | 
|  | u32 words, remaining_tx, remaining_rx; | 
|  | u32 xfer_flags = 0; | 
|  | u32 fifo_flags; | 
|  | int retries = 100; | 
|  | int ret = 0; | 
|  |  | 
|  | if (t->bits_per_word > 16) | 
|  | bytes_per_word = 4; | 
|  | else if (t->bits_per_word > 8) | 
|  | bytes_per_word = 2; | 
|  | else | 
|  | bytes_per_word = 1; | 
|  |  | 
|  | words = t->len / bytes_per_word; | 
|  | remaining_tx = tx_ptr ? words : 0; | 
|  | remaining_rx = rx_ptr ? words : 0; | 
|  |  | 
|  | /* Reset FIFOs */ | 
|  | reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET); | 
|  |  | 
|  | /* Clear IRQ flags */ | 
|  | reg_write(spi, APPLE_SPI_IF_XFER, ~0); | 
|  | reg_write(spi, APPLE_SPI_IF_FIFO, ~0); | 
|  |  | 
|  | /* Determine transfer completion flags we wait for */ | 
|  | if (tx_ptr) | 
|  | xfer_flags |= APPLE_SPI_XFER_TXCOMPLETE; | 
|  | if (rx_ptr) | 
|  | xfer_flags |= APPLE_SPI_XFER_RXCOMPLETE; | 
|  |  | 
|  | /* Set transfer length */ | 
|  | reg_write(spi, APPLE_SPI_TXCNT, remaining_tx); | 
|  | reg_write(spi, APPLE_SPI_RXCNT, remaining_rx); | 
|  |  | 
|  | /* Prime transmit FIFO */ | 
|  | apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word); | 
|  |  | 
|  | /* Start transfer */ | 
|  | reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RUN); | 
|  |  | 
|  | /* TX again since a few words get popped off immediately */ | 
|  | apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word); | 
|  |  | 
|  | while (xfer_flags) { | 
|  | fifo_flags = 0; | 
|  |  | 
|  | if (remaining_tx) | 
|  | fifo_flags |= APPLE_SPI_FIFO_TXTHRESH; | 
|  | if (remaining_rx) | 
|  | fifo_flags |= APPLE_SPI_FIFO_RXTHRESH; | 
|  |  | 
|  | /* Wait for anything to happen */ | 
|  | ret = apple_spi_wait(spi, fifo_flags, xfer_flags, poll); | 
|  | if (ret) { | 
|  | dev_err(&ctlr->dev, "transfer timed out (remaining %d tx, %d rx)\n", | 
|  | remaining_tx, remaining_rx); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | /* Stop waiting on transfer halves once they complete */ | 
|  | xfer_flags &= ~reg_read(spi, APPLE_SPI_IF_XFER); | 
|  |  | 
|  | /* Transmit and receive everything we can */ | 
|  | apple_spi_tx(spi, &tx_ptr, &remaining_tx, bytes_per_word); | 
|  | apple_spi_rx(spi, &rx_ptr, &remaining_rx, bytes_per_word); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sometimes the transfer completes before the last word is in the RX FIFO. | 
|  | * Normally one retry is all it takes to get the last word out. | 
|  | */ | 
|  | while (remaining_rx && retries--) | 
|  | apple_spi_rx(spi, &rx_ptr, &remaining_rx, bytes_per_word); | 
|  |  | 
|  | if (remaining_tx) | 
|  | dev_err(&ctlr->dev, "transfer completed with %d words left to transmit\n", | 
|  | remaining_tx); | 
|  | if (remaining_rx) | 
|  | dev_err(&ctlr->dev, "transfer completed with %d words left to receive\n", | 
|  | remaining_rx); | 
|  |  | 
|  | err: | 
|  | fifo_flags = reg_read(spi, APPLE_SPI_IF_FIFO); | 
|  | WARN_ON(fifo_flags & APPLE_SPI_FIFO_TXOVERFLOW); | 
|  | WARN_ON(fifo_flags & APPLE_SPI_FIFO_RXUNDERRUN); | 
|  |  | 
|  | /* Stop transfer */ | 
|  | reg_write(spi, APPLE_SPI_CTRL, 0); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int apple_spi_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct apple_spi *spi; | 
|  | int ret, irq; | 
|  | struct spi_controller *ctlr; | 
|  |  | 
|  | ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(struct apple_spi)); | 
|  | if (!ctlr) | 
|  | return -ENOMEM; | 
|  |  | 
|  | spi = spi_controller_get_devdata(ctlr); | 
|  | init_completion(&spi->done); | 
|  |  | 
|  | spi->regs = devm_platform_ioremap_resource(pdev, 0); | 
|  | if (IS_ERR(spi->regs)) | 
|  | return PTR_ERR(spi->regs); | 
|  |  | 
|  | spi->clk = devm_clk_get_enabled(&pdev->dev, NULL); | 
|  | if (IS_ERR(spi->clk)) | 
|  | return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk), | 
|  | "Unable to find or enable bus clock\n"); | 
|  |  | 
|  | irq = platform_get_irq(pdev, 0); | 
|  | if (irq < 0) | 
|  | return irq; | 
|  |  | 
|  | ret = devm_request_irq(&pdev->dev, irq, apple_spi_irq, 0, | 
|  | dev_name(&pdev->dev), spi); | 
|  | if (ret) | 
|  | return dev_err_probe(&pdev->dev, ret, "Unable to bind to interrupt\n"); | 
|  |  | 
|  | ctlr->dev.of_node = pdev->dev.of_node; | 
|  | ctlr->bus_num = pdev->id; | 
|  | ctlr->num_chipselect = 1; | 
|  | ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; | 
|  | ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); | 
|  | ctlr->prepare_message = apple_spi_prepare_message; | 
|  | ctlr->set_cs = apple_spi_set_cs; | 
|  | ctlr->transfer_one = apple_spi_transfer_one; | 
|  | ctlr->use_gpio_descriptors = true; | 
|  | ctlr->auto_runtime_pm = true; | 
|  |  | 
|  | pm_runtime_set_active(&pdev->dev); | 
|  | ret = devm_pm_runtime_enable(&pdev->dev); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  |  | 
|  | apple_spi_init(spi); | 
|  |  | 
|  | ret = devm_spi_register_controller(&pdev->dev, ctlr); | 
|  | if (ret < 0) | 
|  | return dev_err_probe(&pdev->dev, ret, "devm_spi_register_controller failed\n"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id apple_spi_of_match[] = { | 
|  | { .compatible = "apple,t8103-spi", }, | 
|  | { .compatible = "apple,spi", }, | 
|  | {} | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, apple_spi_of_match); | 
|  |  | 
|  | static struct platform_driver apple_spi_driver = { | 
|  | .probe = apple_spi_probe, | 
|  | .driver = { | 
|  | .name = "apple-spi", | 
|  | .of_match_table = apple_spi_of_match, | 
|  | }, | 
|  | }; | 
|  | module_platform_driver(apple_spi_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); | 
|  | MODULE_DESCRIPTION("Apple SoC SPI driver"); | 
|  | MODULE_LICENSE("GPL"); |