mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			285 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			285 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 <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,
 | 
						|
};
 |