]>
Commit | Line | Data |
---|---|---|
7184e299 MK |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2021 Mark Kettenis <[email protected]> | |
4 | * Copyright The Asahi Linux Contributors | |
5 | */ | |
6 | ||
7184e299 MK |
7 | #include <dm.h> |
8 | #include <clk.h> | |
9 | #include <spi.h> | |
10 | #include <asm/io.h> | |
11 | #include <linux/bitfield.h> | |
12 | #include <linux/delay.h> | |
13 | ||
14 | #define APPLE_SPI_CTRL 0x000 | |
15 | #define APPLE_SPI_CTRL_RUN BIT(0) | |
16 | #define APPLE_SPI_CTRL_TX_RESET BIT(2) | |
17 | #define APPLE_SPI_CTRL_RX_RESET BIT(3) | |
18 | ||
19 | #define APPLE_SPI_CFG 0x004 | |
20 | #define APPLE_SPI_CFG_CPHA BIT(1) | |
21 | #define APPLE_SPI_CFG_CPOL BIT(2) | |
22 | #define APPLE_SPI_CFG_MODE GENMASK(6, 5) | |
23 | #define APPLE_SPI_CFG_MODE_POLLED 0 | |
24 | #define APPLE_SPI_CFG_MODE_IRQ 1 | |
25 | #define APPLE_SPI_CFG_MODE_DMA 2 | |
26 | #define APPLE_SPI_CFG_IE_RXCOMPLETE BIT(7) | |
27 | #define APPLE_SPI_CFG_IE_TXRXTHRESH BIT(8) | |
28 | #define APPLE_SPI_CFG_LSB_FIRST BIT(13) | |
29 | #define APPLE_SPI_CFG_WORD_SIZE GENMASK(16, 15) | |
30 | #define APPLE_SPI_CFG_WORD_SIZE_8B 0 | |
31 | #define APPLE_SPI_CFG_WORD_SIZE_16B 1 | |
32 | #define APPLE_SPI_CFG_WORD_SIZE_32B 2 | |
33 | #define APPLE_SPI_CFG_FIFO_THRESH GENMASK(18, 17) | |
34 | #define APPLE_SPI_CFG_FIFO_THRESH_8B 0 | |
35 | #define APPLE_SPI_CFG_FIFO_THRESH_4B 1 | |
36 | #define APPLE_SPI_CFG_FIFO_THRESH_1B 2 | |
37 | #define APPLE_SPI_CFG_IE_TXCOMPLETE BIT(21) | |
38 | ||
39 | #define APPLE_SPI_STATUS 0x008 | |
40 | #define APPLE_SPI_STATUS_RXCOMPLETE BIT(0) | |
41 | #define APPLE_SPI_STATUS_TXRXTHRESH BIT(1) | |
42 | #define APPLE_SPI_STATUS_TXCOMPLETE BIT(2) | |
43 | ||
44 | #define APPLE_SPI_PIN 0x00c | |
45 | #define APPLE_SPI_PIN_KEEP_MOSI BIT(0) | |
46 | #define APPLE_SPI_PIN_CS BIT(1) | |
47 | ||
48 | #define APPLE_SPI_TXDATA 0x010 | |
49 | #define APPLE_SPI_RXDATA 0x020 | |
50 | #define APPLE_SPI_CLKDIV 0x030 | |
51 | #define APPLE_SPI_CLKDIV_MIN 0x002 | |
52 | #define APPLE_SPI_CLKDIV_MAX 0x7ff | |
53 | #define APPLE_SPI_RXCNT 0x034 | |
54 | #define APPLE_SPI_WORD_DELAY 0x038 | |
55 | #define APPLE_SPI_TXCNT 0x04c | |
56 | ||
57 | #define APPLE_SPI_FIFOSTAT 0x10c | |
58 | #define APPLE_SPI_FIFOSTAT_TXFULL BIT(4) | |
59 | #define APPLE_SPI_FIFOSTAT_LEVEL_TX GENMASK(15, 8) | |
60 | #define APPLE_SPI_FIFOSTAT_RXEMPTY BIT(20) | |
61 | #define APPLE_SPI_FIFOSTAT_LEVEL_RX GENMASK(31, 24) | |
62 | ||
63 | #define APPLE_SPI_IE_XFER 0x130 | |
64 | #define APPLE_SPI_IF_XFER 0x134 | |
65 | #define APPLE_SPI_XFER_RXCOMPLETE BIT(0) | |
66 | #define APPLE_SPI_XFER_TXCOMPLETE BIT(1) | |
67 | ||
68 | #define APPLE_SPI_IE_FIFO 0x138 | |
69 | #define APPLE_SPI_IF_FIFO 0x13c | |
70 | #define APPLE_SPI_FIFO_RXTHRESH BIT(4) | |
71 | #define APPLE_SPI_FIFO_TXTHRESH BIT(5) | |
72 | #define APPLE_SPI_FIFO_RXFULL BIT(8) | |
73 | #define APPLE_SPI_FIFO_TXEMPTY BIT(9) | |
74 | #define APPLE_SPI_FIFO_RXUNDERRUN BIT(16) | |
75 | #define APPLE_SPI_FIFO_TXOVERFLOW BIT(17) | |
76 | ||
77 | #define APPLE_SPI_SHIFTCFG 0x150 | |
78 | #define APPLE_SPI_SHIFTCFG_CLK_ENABLE BIT(0) | |
79 | #define APPLE_SPI_SHIFTCFG_CS_ENABLE BIT(1) | |
80 | #define APPLE_SPI_SHIFTCFG_AND_CLK_DATA BIT(8) | |
81 | #define APPLE_SPI_SHIFTCFG_CS_AS_DATA BIT(9) | |
82 | #define APPLE_SPI_SHIFTCFG_TX_ENABLE BIT(10) | |
83 | #define APPLE_SPI_SHIFTCFG_RX_ENABLE BIT(11) | |
84 | #define APPLE_SPI_SHIFTCFG_BITS GENMASK(21, 16) | |
85 | #define APPLE_SPI_SHIFTCFG_OVERRIDE_CS BIT(24) | |
86 | ||
87 | #define APPLE_SPI_PINCFG 0x154 | |
88 | #define APPLE_SPI_PINCFG_KEEP_CLK BIT(0) | |
89 | #define APPLE_SPI_PINCFG_KEEP_CS BIT(1) | |
90 | #define APPLE_SPI_PINCFG_KEEP_MOSI BIT(2) | |
91 | #define APPLE_SPI_PINCFG_CLK_IDLE_VAL BIT(8) | |
92 | #define APPLE_SPI_PINCFG_CS_IDLE_VAL BIT(9) | |
93 | #define APPLE_SPI_PINCFG_MOSI_IDLE_VAL BIT(10) | |
94 | ||
95 | #define APPLE_SPI_DELAY_PRE 0x160 | |
96 | #define APPLE_SPI_DELAY_POST 0x168 | |
97 | #define APPLE_SPI_DELAY_ENABLE BIT(0) | |
98 | #define APPLE_SPI_DELAY_NO_INTERBYTE BIT(1) | |
99 | #define APPLE_SPI_DELAY_SET_SCK BIT(4) | |
100 | #define APPLE_SPI_DELAY_SET_MOSI BIT(6) | |
101 | #define APPLE_SPI_DELAY_SCK_VAL BIT(8) | |
102 | #define APPLE_SPI_DELAY_MOSI_VAL BIT(12) | |
103 | ||
104 | #define APPLE_SPI_FIFO_DEPTH 16 | |
105 | ||
106 | #define APPLE_SPI_TIMEOUT_MS 200 | |
107 | ||
108 | struct apple_spi_priv { | |
109 | void *base; | |
110 | u32 clkfreq; /* Input clock frequency */ | |
111 | }; | |
112 | ||
113 | static void apple_spi_set_cs(struct apple_spi_priv *priv, int on) | |
114 | { | |
115 | writel(on ? 0 : APPLE_SPI_PIN_CS, priv->base + APPLE_SPI_PIN); | |
116 | } | |
117 | ||
118 | /* Fill Tx FIFO. */ | |
119 | static void apple_spi_tx(struct apple_spi_priv *priv, uint *len, | |
120 | const void **dout) | |
121 | { | |
122 | const u8 *out = *dout; | |
123 | u32 data, fifostat; | |
124 | uint count; | |
125 | ||
126 | fifostat = readl(priv->base + APPLE_SPI_FIFOSTAT); | |
127 | count = APPLE_SPI_FIFO_DEPTH - | |
128 | FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_TX, fifostat); | |
129 | while (*len > 0 && count > 0) { | |
130 | data = out ? *out++ : 0; | |
131 | writel(data, priv->base + APPLE_SPI_TXDATA); | |
132 | (*len)--; | |
133 | count--; | |
134 | } | |
135 | ||
136 | *dout = out; | |
137 | } | |
138 | ||
139 | /* Empty Rx FIFO. */ | |
140 | static void apple_spi_rx(struct apple_spi_priv *priv, uint *len, | |
141 | void **din) | |
142 | { | |
143 | u8 *in = *din; | |
144 | u32 data, fifostat; | |
145 | uint count; | |
146 | ||
147 | fifostat = readl(priv->base + APPLE_SPI_FIFOSTAT); | |
148 | count = FIELD_GET(APPLE_SPI_FIFOSTAT_LEVEL_RX, fifostat); | |
149 | while (*len > 0 && count > 0) { | |
150 | data = readl(priv->base + APPLE_SPI_RXDATA); | |
151 | if (in) | |
152 | *in++ = data; | |
153 | (*len)--; | |
154 | count--; | |
155 | } | |
156 | ||
157 | *din = in; | |
158 | } | |
159 | ||
160 | static int apple_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
161 | const void *dout, void *din, unsigned long flags) | |
162 | { | |
163 | struct apple_spi_priv *priv = dev_get_priv(dev->parent); | |
164 | unsigned long start = get_timer(0); | |
165 | uint txlen, rxlen; | |
166 | int ret = 0; | |
167 | ||
168 | if ((bitlen % 8) != 0) | |
169 | return -EINVAL; | |
170 | txlen = rxlen = bitlen / 8; | |
171 | ||
172 | if (flags & SPI_XFER_BEGIN) | |
173 | apple_spi_set_cs(priv, 1); | |
174 | ||
175 | if (txlen > 0) { | |
176 | /* Reset FIFOs */ | |
177 | writel(APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET, | |
178 | priv->base + APPLE_SPI_CTRL); | |
179 | ||
180 | /* Set the transfer length */ | |
181 | writel(txlen, priv->base + APPLE_SPI_TXCNT); | |
182 | writel(rxlen, priv->base + APPLE_SPI_RXCNT); | |
183 | ||
184 | /* Prime transmit FIFO */ | |
185 | apple_spi_tx(priv, &txlen, &dout); | |
186 | ||
187 | /* Start transfer */ | |
188 | writel(APPLE_SPI_CTRL_RUN, priv->base + APPLE_SPI_CTRL); | |
189 | ||
190 | while ((txlen > 0 || rxlen > 0)) { | |
191 | apple_spi_rx(priv, &rxlen, &din); | |
192 | apple_spi_tx(priv, &txlen, &dout); | |
193 | ||
194 | if (get_timer(start) > APPLE_SPI_TIMEOUT_MS) { | |
195 | ret = -ETIMEDOUT; | |
196 | break; | |
197 | } | |
198 | } | |
199 | ||
200 | /* Stop transfer. */ | |
201 | writel(0, priv->base + APPLE_SPI_CTRL); | |
202 | } | |
203 | ||
204 | if (flags & SPI_XFER_END) | |
205 | apple_spi_set_cs(priv, 0); | |
206 | ||
207 | return ret; | |
208 | } | |
209 | ||
210 | static int apple_spi_set_speed(struct udevice *dev, uint speed) | |
211 | { | |
212 | struct apple_spi_priv *priv = dev_get_priv(dev); | |
213 | u32 div; | |
214 | ||
215 | div = DIV_ROUND_UP(priv->clkfreq, speed); | |
216 | if (div < APPLE_SPI_CLKDIV_MIN) | |
217 | div = APPLE_SPI_CLKDIV_MIN; | |
218 | if (div > APPLE_SPI_CLKDIV_MAX) | |
219 | div = APPLE_SPI_CLKDIV_MAX; | |
220 | ||
221 | writel(div, priv->base + APPLE_SPI_CLKDIV); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int apple_spi_set_mode(struct udevice *bus, uint mode) | |
227 | { | |
228 | return 0; | |
229 | } | |
230 | ||
231 | struct dm_spi_ops apple_spi_ops = { | |
232 | .xfer = apple_spi_xfer, | |
233 | .set_speed = apple_spi_set_speed, | |
234 | .set_mode = apple_spi_set_mode, | |
235 | }; | |
236 | ||
237 | static int apple_spi_probe(struct udevice *dev) | |
238 | { | |
239 | struct apple_spi_priv *priv = dev_get_priv(dev); | |
240 | struct clk clkdev; | |
241 | int ret; | |
242 | ||
243 | priv->base = dev_read_addr_ptr(dev); | |
244 | if (!priv->base) | |
245 | return -EINVAL; | |
246 | ||
247 | ret = clk_get_by_index(dev, 0, &clkdev); | |
248 | if (ret) | |
249 | return ret; | |
250 | priv->clkfreq = clk_get_rate(&clkdev); | |
251 | ||
252 | /* Set CS high (inactive) and disable override and auto-CS */ | |
253 | writel(APPLE_SPI_PIN_CS, priv->base + APPLE_SPI_PIN); | |
254 | writel(readl(priv->base + APPLE_SPI_SHIFTCFG) & ~APPLE_SPI_SHIFTCFG_OVERRIDE_CS, | |
255 | priv->base + APPLE_SPI_SHIFTCFG); | |
256 | writel((readl(priv->base + APPLE_SPI_PINCFG) & ~APPLE_SPI_PINCFG_CS_IDLE_VAL) | | |
257 | APPLE_SPI_PINCFG_KEEP_CS, priv->base + APPLE_SPI_PINCFG); | |
258 | ||
259 | /* Reset FIFOs */ | |
260 | writel(APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET, | |
261 | priv->base + APPLE_SPI_CTRL); | |
262 | ||
263 | /* Configure defaults */ | |
264 | writel(FIELD_PREP(APPLE_SPI_CFG_MODE, APPLE_SPI_CFG_MODE_IRQ) | | |
265 | FIELD_PREP(APPLE_SPI_CFG_WORD_SIZE, APPLE_SPI_CFG_WORD_SIZE_8B) | | |
266 | FIELD_PREP(APPLE_SPI_CFG_FIFO_THRESH, APPLE_SPI_CFG_FIFO_THRESH_8B), | |
267 | priv->base + APPLE_SPI_CFG); | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
272 | static const struct udevice_id apple_spi_of_match[] = { | |
273 | { .compatible = "apple,spi" }, | |
274 | { /* sentinel */ } | |
275 | }; | |
276 | ||
277 | U_BOOT_DRIVER(apple_spi) = { | |
278 | .name = "apple_spi", | |
279 | .id = UCLASS_SPI, | |
280 | .of_match = apple_spi_of_match, | |
281 | .probe = apple_spi_probe, | |
282 | .priv_auto = sizeof(struct apple_spi_priv), | |
283 | .ops = &apple_spi_ops, | |
284 | }; |