286 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
| // 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,
 | |
| };
 |