xref: /aosp_15_r20/external/coreboot/src/soc/cavium/cn81xx/spi.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
4  * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
5  */
6 
7 #include <device/mmio.h>
8 #include <assert.h>
9 #include <console/console.h>
10 #include <endian.h>
11 #include <soc/addressmap.h>
12 #include <soc/spi.h>
13 #include <soc/clock.h>
14 #include <spi-generic.h>
15 #include <spi_flash.h>
16 #include <timer.h>
17 
18 union cavium_spi_cfg {
19 	u64 u;
20 	struct {
21 		u64 enable	: 1;
22 		u64 idlelow	: 1;
23 		u64 clk_cont	: 1;
24 		u64 wireor	: 1;
25 		u64 lsbfirst	: 1;
26 		u64		: 2;
27 		u64 cshi	: 1;
28 		u64 idleclks	: 2;
29 		u64 tristate	: 1;
30 		u64 cslate	: 1;
31 		u64 csena	: 4; /* Must be one */
32 		u64 clkdiv	: 13;
33 		u64		: 35;
34 	} s;
35 };
36 
37 union cavium_spi_sts {
38 	u64 u;
39 	struct {
40 		u64 busy	: 1;
41 		u64 mpi_intr	: 1;
42 		u64		: 6;
43 		u64 rxnum	: 5;
44 		u64		: 51;
45 	} s;
46 };
47 
48 union cavium_spi_tx {
49 	u64 u;
50 	struct {
51 		u64 totnum	: 5;
52 		u64		: 3;
53 		u64 txnum	: 5;
54 		u64		: 3;
55 		u64 leavecs	: 1;
56 		u64		: 3;
57 		u64 csid	: 2;
58 		u64		: 42;
59 	} s;
60 };
61 
62 struct cavium_spi {
63 	union cavium_spi_cfg cfg;
64 	union cavium_spi_sts sts;
65 	union cavium_spi_tx tx;
66 	u64	rsvd1;
67 	u64	sts_w1s;
68 	u64	rsvd2;
69 	u64	int_ena_w1c;
70 	u64	int_ena_w1s;
71 	u64	wide_dat;
72 	u8	rsvd4[0x38];
73 	u64	dat[8];
74 };
75 
76 check_member(cavium_spi, cfg, 0);
77 check_member(cavium_spi, sts, 0x8);
78 check_member(cavium_spi, tx, 0x10);
79 check_member(cavium_spi, dat[7], 0xb8);
80 
81 struct cavium_spi_slave {
82 	struct cavium_spi *regs;
83 	int cs;
84 };
85 
86 #define SPI_TIMEOUT_US	5000
87 
88 static struct cavium_spi_slave cavium_spi_slaves[] = {
89 	{
90 	 .regs = (struct cavium_spi *)MPI_PF_BAR0,
91 	 .cs = 0,
92 	},
93 };
94 
to_cavium_spi(const struct spi_slave * slave)95 static struct cavium_spi_slave *to_cavium_spi(const struct spi_slave *slave)
96 {
97 	assert(slave->bus < ARRAY_SIZE(cavium_spi_slaves));
98 	return &cavium_spi_slaves[slave->bus];
99 }
100 
101 /**
102  * Enable the SPI controller. Pins are driven.
103  *
104  * @param bus                 The SPI bus to operate on
105  */
spi_enable(const size_t bus)106 void spi_enable(const size_t bus)
107 {
108 	union cavium_spi_cfg cfg;
109 
110 	assert(bus < ARRAY_SIZE(cavium_spi_slaves));
111 	if (bus >= ARRAY_SIZE(cavium_spi_slaves))
112 		return;
113 
114 	struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
115 
116 	cfg.u = read64(&regs->cfg);
117 	cfg.s.csena = 0xf;
118 	cfg.s.enable = 1;
119 	write64(&regs->cfg, cfg.u);
120 }
121 
122 /**
123  * Disable the SPI controller. Pins are tristated.
124  *
125  * @param bus                 The SPI bus to operate on
126  */
spi_disable(const size_t bus)127 void spi_disable(const size_t bus)
128 {
129 	union cavium_spi_cfg cfg;
130 
131 	assert(bus < ARRAY_SIZE(cavium_spi_slaves));
132 	if (bus >= ARRAY_SIZE(cavium_spi_slaves))
133 		return;
134 
135 	struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
136 
137 	cfg.u = read64(&regs->cfg);
138 	cfg.s.csena = 0xf;
139 	cfg.s.enable = 0;
140 	write64(&regs->cfg, cfg.u);
141 }
142 
143 /**
144  * Set SPI Chip select line and level if asserted.
145  *
146  * @param bus                 The SPI bus to operate on
147  * @param chip_select         The chip select pin to use (0 - 3)
148  * @param assert_is_low       CS pin state is low when asserted
149  */
spi_set_cs(const size_t bus,const size_t chip_select,const size_t assert_is_low)150 void spi_set_cs(const size_t bus,
151 		const size_t chip_select,
152 		const size_t assert_is_low)
153 {
154 	union cavium_spi_cfg cfg;
155 
156 	assert(bus < ARRAY_SIZE(cavium_spi_slaves));
157 	if (bus >= ARRAY_SIZE(cavium_spi_slaves))
158 		return;
159 
160 	cavium_spi_slaves[bus].cs = chip_select & 0x3;
161 	struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
162 
163 	cfg.u = read64(&regs->cfg);
164 	cfg.s.csena = 0xf;
165 	cfg.s.cshi = !assert_is_low;
166 	write64(&regs->cfg, cfg.u);
167 
168 	//FIXME: CS2/3: Change pin mux here
169 }
170 
171 /**
172  * Set SPI clock frequency.
173  *
174  * @param bus                 The SPI bus to operate on
175  * @param speed_hz            The SPI frequency in Hz
176  * @param idle_low            The SPI clock idles low
177  * @param idle_cycles         Number of CLK cycles between two commands (0 - 3)
178 
179  */
spi_set_clock(const size_t bus,const size_t speed_hz,const size_t idle_low,const size_t idle_cycles)180 void spi_set_clock(const size_t bus,
181 		   const size_t speed_hz,
182 		   const size_t idle_low,
183 		   const size_t idle_cycles)
184 {
185 	union cavium_spi_cfg cfg;
186 
187 	assert(bus < ARRAY_SIZE(cavium_spi_slaves));
188 	if (bus >= ARRAY_SIZE(cavium_spi_slaves))
189 		return;
190 
191 	struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
192 	const uint64_t sclk = thunderx_get_io_clock();
193 
194 	cfg.u = read64(&regs->cfg);
195 	cfg.s.csena = 0xf;
196 	cfg.s.clk_cont = 0;
197 	cfg.s.idlelow = !!idle_low;
198 	cfg.s.idleclks = idle_cycles & 0x3;
199 	cfg.s.clkdiv = MIN(sclk / (2ULL * speed_hz), 0x1fff);
200 	write64(&regs->cfg, cfg.u);
201 
202 	printk(BIOS_DEBUG, "SPI: set clock to %lld kHz\n",
203 	       (sclk / (2ULL * cfg.s.clkdiv)) >> 10);
204 }
205 
206 /**
207  * Get current SPI clock frequency in Hz.
208  *
209  * @param bus                 The SPI bus to operate on
210  */
spi_get_clock(const size_t bus)211 uint64_t spi_get_clock(const size_t bus)
212 {
213 	union cavium_spi_cfg cfg;
214 
215 	assert(bus < ARRAY_SIZE(cavium_spi_slaves));
216 	if (bus >= ARRAY_SIZE(cavium_spi_slaves))
217 		return 0;
218 
219 	struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
220 	const uint64_t sclk = thunderx_get_io_clock();
221 
222 	cfg.u = read64(&regs->cfg);
223 
224 	return (sclk / (2ULL * cfg.s.clkdiv));
225 }
226 
227 /**
228  * Set SPI LSB/MSB first.
229  *
230  * @param bus                 The SPI bus to operate on
231  * @param lsb_first           The SPI operates LSB first
232  *
233  */
spi_set_lsbmsb(const size_t bus,const size_t lsb_first)234 void spi_set_lsbmsb(const size_t bus, const size_t lsb_first)
235 {
236 	union cavium_spi_cfg cfg;
237 
238 	assert(bus < ARRAY_SIZE(cavium_spi_slaves));
239 	if (bus >= ARRAY_SIZE(cavium_spi_slaves))
240 		return;
241 
242 	struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
243 
244 	cfg.u = read64(&regs->cfg);
245 	cfg.s.csena = 0xf;
246 	cfg.s.lsbfirst = !!lsb_first;
247 	write64(&regs->cfg, cfg.u);
248 }
249 
250 /**
251  * Init SPI with custom parameters and enable SPI controller.
252  *
253  * @param bus                 The SPI bus to operate on
254  * @param speed_hz            The SPI frequency in Hz
255  * @param idle_low            The SPI clock idles low
256  * @param idle_cycles         Number of CLK cycles between two commands (0 - 3)
257  * @param lsb_first           The SPI operates LSB first
258  * @param chip_select         The chip select pin to use (0 - 3)
259  * @param assert_is_low       CS pin state is low when asserted
260  */
spi_init_custom(const size_t bus,const size_t speed_hz,const size_t idle_low,const size_t idle_cycles,const size_t lsb_first,const size_t chip_select,const size_t assert_is_low)261 void spi_init_custom(const size_t bus,
262 		     const size_t speed_hz,
263 		     const size_t idle_low,
264 		     const size_t idle_cycles,
265 		     const size_t lsb_first,
266 		     const size_t chip_select,
267 		     const size_t assert_is_low)
268 {
269 	spi_disable(bus);
270 	spi_set_clock(bus, speed_hz, idle_low, idle_cycles);
271 	spi_set_lsbmsb(bus, lsb_first);
272 	spi_set_cs(bus, chip_select, assert_is_low);
273 	spi_enable(bus);
274 }
275 
276 /**
277  * Init all SPI controllers with default values and enable all SPI controller.
278  *
279  */
spi_init(void)280 void spi_init(void)
281 {
282 	for (size_t i = 0; i < ARRAY_SIZE(cavium_spi_slaves); i++) {
283 		spi_disable(i);
284 		spi_set_clock(i, 12500000, 0, 0);
285 		spi_set_lsbmsb(i, 0);
286 		spi_set_cs(i, 0, 1);
287 		spi_enable(i);
288 	}
289 }
290 
cavium_spi_wait(struct cavium_spi * regs)291 static int cavium_spi_wait(struct cavium_spi *regs)
292 {
293 	struct stopwatch sw;
294 	union cavium_spi_sts sts;
295 
296 	stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_US);
297 	do {
298 		sts.u = read64(&regs->sts);
299 		if (!sts.s.busy)
300 			return 0;
301 	} while (!stopwatch_expired(&sw));
302 	printk(BIOS_DEBUG, "SPI: Timed out after %uus\n", SPI_TIMEOUT_US);
303 	return -1;
304 }
305 
do_xfer(const struct spi_slave * slave,struct spi_op * vector,int leavecs)306 static int do_xfer(const struct spi_slave *slave, struct spi_op *vector,
307 		int leavecs)
308 {
309 	struct cavium_spi *regs = to_cavium_spi(slave)->regs;
310 	uint8_t *out_buf = (uint8_t *)vector->dout;
311 	size_t bytesout = vector->bytesout;
312 	uint8_t *in_buf = (uint8_t *)vector->din;
313 	size_t bytesin = vector->bytesin;
314 	union cavium_spi_sts sts;
315 	union cavium_spi_tx tx;
316 
317 	/**
318 	 * The CN81xx SPI controller is half-duplex and has 8 data registers.
319 	 * If >8 bytes remain in the transfer then we must set LEAVECS = 1 so
320 	 * that the /CS remains asserted. Once <=8 bytes remain we must set
321 	 * LEAVECS = 0 so that /CS is de-asserted, thus completing the transfer.
322 	 */
323 	while (bytesout) {
324 		size_t out_now = MIN(bytesout, 8);
325 		unsigned int i;
326 
327 		for (i = 0; i < out_now; i++)
328 			write64(&regs->dat[i], out_buf[i] & 0xff);
329 
330 		tx.u = 0;
331 		tx.s.csid = to_cavium_spi(slave)->cs;
332 		if (leavecs || ((bytesout > 8) || bytesin))
333 			tx.s.leavecs = 1;
334 		/* number of bytes to transmit goes in both TXNUM and TOTNUM */
335 		tx.s.totnum = out_now;
336 		tx.s.txnum = out_now;
337 		write64(&regs->tx, tx.u);
338 
339 		/* check status */
340 		if (cavium_spi_wait(regs) < 0)
341 			return -1;
342 
343 		bytesout -= out_now;
344 		out_buf += out_now;
345 	}
346 
347 	while (bytesin) {
348 		size_t in_now = MIN(bytesin, 8);
349 		unsigned int i;
350 
351 		tx.u = 0;
352 		tx.s.csid = to_cavium_spi(slave)->cs;
353 		if (leavecs || (bytesin > 8))
354 			tx.s.leavecs = 1;
355 		tx.s.totnum = in_now;
356 		write64(&regs->tx, tx.u);
357 
358 		/* check status */
359 		if (cavium_spi_wait(regs) < 0)
360 			return -1;
361 
362 		sts.u = read64(&regs->sts);
363 		if (sts.s.rxnum != in_now) {
364 			printk(BIOS_ERR,
365 			       "SPI: Incorrect number of bytes received: %u.\n",
366 			       sts.s.rxnum);
367 			return -1;
368 		}
369 
370 		for (i = 0; i < in_now; i++) {
371 			*in_buf = (uint8_t)((read64(&regs->dat[i]) & 0xff));
372 			in_buf++;
373 		}
374 		bytesin -= in_now;
375 	}
376 
377 	return 0;
378 }
379 
spi_ctrlr_xfer_vector(const struct spi_slave * slave,struct spi_op vectors[],size_t count)380 static int spi_ctrlr_xfer_vector(const struct spi_slave *slave,
381 		struct spi_op vectors[], size_t count)
382 {
383 	int i;
384 
385 	for (i = 0; i < count; i++) {
386 		if (do_xfer(slave, &vectors[i], count - 1 == i ? 0 : 1)) {
387 			printk(BIOS_ERR,
388 			       "SPI: Failed to transfer %zu vectors.\n", count);
389 			return -1;
390 		}
391 	}
392 
393 	return 0;
394 }
395 static const struct spi_ctrlr spi_ctrlr = {
396 	.xfer_vector = spi_ctrlr_xfer_vector,
397 	.max_xfer_size = SPI_CTRLR_DEFAULT_MAX_XFER_SIZE,
398 };
399 
400 const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
401 	{
402 		.ctrlr = &spi_ctrlr,
403 		.bus_start = 0,
404 		.bus_end = ARRAY_SIZE(cavium_spi_slaves) - 1,
405 	},
406 };
407 const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);
408