xref: /optee_os/core/drivers/pl022_spi.c (revision 918bb3a5f3e473ec252ff2dfb71d666108dd22f4)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2aca1545dSVictor Chong /*
3aca1545dSVictor Chong  * Copyright (c) 2016, Linaro Limited
4aca1545dSVictor Chong  *
5aca1545dSVictor Chong  */
6aca1545dSVictor Chong 
7aca1545dSVictor Chong #include <assert.h>
8aca1545dSVictor Chong #include <drivers/pl022_spi.h>
9aca1545dSVictor Chong #include <initcall.h>
10aca1545dSVictor Chong #include <io.h>
11aca1545dSVictor Chong #include <kernel/panic.h>
12aca1545dSVictor Chong #include <kernel/tee_time.h>
13aca1545dSVictor Chong #include <platform_config.h>
14aca1545dSVictor Chong #include <trace.h>
15aca1545dSVictor Chong #include <util.h>
16aca1545dSVictor Chong 
17aca1545dSVictor Chong /* SPI register offsets */
18aca1545dSVictor Chong #define SSPCR0		0x000
19aca1545dSVictor Chong #define SSPCR1		0x004
20aca1545dSVictor Chong #define SSPDR		0x008
21aca1545dSVictor Chong #define SSPSR		0x00C
22aca1545dSVictor Chong #define SSPCPSR		0x010
23aca1545dSVictor Chong #define SSPIMSC		0x014
24aca1545dSVictor Chong #define SSPRIS		0x018
25aca1545dSVictor Chong #define SSPMIS		0x01C
26aca1545dSVictor Chong #define SSPICR		0x020
27aca1545dSVictor Chong #define SSPDMACR	0x024
28aca1545dSVictor Chong 
29aca1545dSVictor Chong #ifdef PLATFORM_hikey
30aca1545dSVictor Chong /* HiKey extensions */
31aca1545dSVictor Chong #define SSPTXFIFOCR	0x028
32aca1545dSVictor Chong #define SSPRXFIFOCR	0x02C
33aca1545dSVictor Chong #define SSPB2BTRANS	0x030
34aca1545dSVictor Chong #endif
35aca1545dSVictor Chong 
36aca1545dSVictor Chong /* test registers */
37aca1545dSVictor Chong #define SSPTCR		0x080
38aca1545dSVictor Chong #define SSPITIP		0x084
39aca1545dSVictor Chong #define SSPITOP		0x088
40aca1545dSVictor Chong #define SSPTDR		0x08C
41aca1545dSVictor Chong 
42aca1545dSVictor Chong #define SSPPeriphID0	0xFE0
43aca1545dSVictor Chong #define SSPPeriphID1	0xFE4
44aca1545dSVictor Chong #define SSPPeriphID2	0xFE8
45aca1545dSVictor Chong #define SSPPeriphID3	0xFEC
46aca1545dSVictor Chong 
47aca1545dSVictor Chong #define SSPPCellID0	0xFF0
48aca1545dSVictor Chong #define SSPPCellID1	0xFF4
49aca1545dSVictor Chong #define SSPPCellID2	0xFF8
50aca1545dSVictor Chong #define SSPPCellID3	0xFFC
51aca1545dSVictor Chong 
52aca1545dSVictor Chong /* SPI register masks */
53aca1545dSVictor Chong #define SSPCR0_SCR		SHIFT_U32(0xFF, 8)
54aca1545dSVictor Chong #define SSPCR0_SPH		SHIFT_U32(1, 7)
55aca1545dSVictor Chong #define SSPCR0_SPH1		SHIFT_U32(1, 7)
56aca1545dSVictor Chong #define SSPCR0_SPH0		SHIFT_U32(0, 7)
57aca1545dSVictor Chong #define SSPCR0_SPO		SHIFT_U32(1, 6)
58aca1545dSVictor Chong #define SSPCR0_SPO1		SHIFT_U32(1, 6)
59aca1545dSVictor Chong #define SSPCR0_SPO0		SHIFT_U32(0, 6)
60aca1545dSVictor Chong #define SSPCR0_FRF		SHIFT_U32(3, 4)
61aca1545dSVictor Chong #define SSPCR0_FRF_SPI		SHIFT_U32(0, 4)
62aca1545dSVictor Chong #define SSPCR0_DSS		SHIFT_U32(0xFF, 0)
63aca1545dSVictor Chong #define SSPCR0_DSS_16BIT	SHIFT_U32(0xF, 0)
64aca1545dSVictor Chong #define SSPCR0_DSS_8BIT		SHIFT_U32(7, 0)
65aca1545dSVictor Chong 
66aca1545dSVictor Chong #define SSPCR1_SOD		SHIFT_U32(1, 3)
67aca1545dSVictor Chong #define SSPCR1_SOD_ENABLE	SHIFT_U32(1, 3)
68aca1545dSVictor Chong #define SSPCR1_SOD_DISABLE	SHIFT_U32(0, 3)
69aca1545dSVictor Chong #define SSPCR1_MS		SHIFT_U32(1, 2)
70aca1545dSVictor Chong #define SSPCR1_MS_SLAVE		SHIFT_U32(1, 2)
71aca1545dSVictor Chong #define SSPCR1_MS_MASTER	SHIFT_U32(0, 2)
72aca1545dSVictor Chong #define SSPCR1_SSE		SHIFT_U32(1, 1)
73aca1545dSVictor Chong #define SSPCR1_SSE_ENABLE	SHIFT_U32(1, 1)
74aca1545dSVictor Chong #define SSPCR1_SSE_DISABLE	SHIFT_U32(0, 1)
75aca1545dSVictor Chong #define SSPCR1_LBM		SHIFT_U32(1, 0)
76aca1545dSVictor Chong #define SSPCR1_LBM_YES		SHIFT_U32(1, 0)
77aca1545dSVictor Chong #define SSPCR1_LBM_NO		SHIFT_U32(0, 0)
78aca1545dSVictor Chong 
79aca1545dSVictor Chong #define SSPDR_DATA	SHIFT_U32(0xFFFF, 0)
80aca1545dSVictor Chong 
81aca1545dSVictor Chong #define SSPSR_BSY	SHIFT_U32(1, 4)
82aca1545dSVictor Chong #define SSPSR_RNF	SHIFT_U32(1, 3)
83aca1545dSVictor Chong #define SSPSR_RNE	SHIFT_U32(1, 2)
84aca1545dSVictor Chong #define SSPSR_TNF	SHIFT_U32(1, 1)
85aca1545dSVictor Chong #define SSPSR_TFE	SHIFT_U32(1, 0)
86aca1545dSVictor Chong 
87aca1545dSVictor Chong #define SSPCPSR_CPSDVR	SHIFT_U32(0xFF, 0)
88aca1545dSVictor Chong 
89aca1545dSVictor Chong #define SSPIMSC_TXIM	SHIFT_U32(1, 3)
90aca1545dSVictor Chong #define SSPIMSC_RXIM	SHIFT_U32(1, 2)
91aca1545dSVictor Chong #define SSPIMSC_RTIM	SHIFT_U32(1, 1)
92aca1545dSVictor Chong #define SSPIMSC_RORIM	SHIFT_U32(1, 0)
93aca1545dSVictor Chong 
94aca1545dSVictor Chong #define SSPRIS_TXRIS	SHIFT_U32(1, 3)
95aca1545dSVictor Chong #define SSPRIS_RXRIS	SHIFT_U32(1, 2)
96aca1545dSVictor Chong #define SSPRIS_RTRIS	SHIFT_U32(1, 1)
97aca1545dSVictor Chong #define SSPRIS_RORRIS	SHIFT_U32(1, 0)
98aca1545dSVictor Chong 
99aca1545dSVictor Chong #define SSPMIS_TXMIS	SHIFT_U32(1, 3)
100aca1545dSVictor Chong #define SSPMIS_RXMIS	SHIFT_U32(1, 2)
101aca1545dSVictor Chong #define SSPMIS_RTMIS	SHIFT_U32(1, 1)
102aca1545dSVictor Chong #define SSPMIS_RORMIS	SHIFT_U32(1, 0)
103aca1545dSVictor Chong 
104aca1545dSVictor Chong #define SSPICR_RTIC		SHIFT_U32(1, 1)
105aca1545dSVictor Chong #define SSPICR_RORIC		SHIFT_U32(1, 0)
106aca1545dSVictor Chong 
107aca1545dSVictor Chong #define SSPDMACR_TXDMAE	SHIFT_U32(1, 1)
108aca1545dSVictor Chong #define SSPDMACR_RXDMAE	SHIFT_U32(1, 0)
109aca1545dSVictor Chong 
110aca1545dSVictor Chong #define SSPPeriphID0_PartNumber0	SHIFT_U32(0xFF, 0) /* 0x22 */
111aca1545dSVictor Chong #define SSPPeriphID1_Designer0		SHIFT_U32(0xF, 4) /* 0x1 */
112aca1545dSVictor Chong #define SSPPeriphID1_PartNumber1	SHIFT_U32(0xF, 0) /* 0x0 */
113aca1545dSVictor Chong #define SSPPeriphID2_Revision		SHIFT_U32(0xF, 4)
114aca1545dSVictor Chong #define SSPPeriphID2_Designer1		SHIFT_U32(0xF, 0) /* 0x4 */
115aca1545dSVictor Chong #define SSPPeriphID3_Configuration	SHIFT_U32(0xFF, 0) /* 0x00 */
116aca1545dSVictor Chong 
117aca1545dSVictor Chong #define SSPPCellID_0	SHIFT_U32(0xFF, 0) /* 0x0D */
118aca1545dSVictor Chong #define SSPPCellID_1	SHIFT_U32(0xFF, 0) /* 0xF0 */
119aca1545dSVictor Chong #define SSPPPCellID_2	SHIFT_U32(0xFF, 0) /* 0x05 */
120aca1545dSVictor Chong #define SSPPPCellID_3	SHIFT_U32(0xFF, 0) /* 0xB1 */
121aca1545dSVictor Chong 
122aca1545dSVictor Chong #define MASK_32 0xFFFFFFFF
123aca1545dSVictor Chong #define MASK_28 0xFFFFFFF
124aca1545dSVictor Chong #define MASK_24 0xFFFFFF
125aca1545dSVictor Chong #define MASK_20 0xFFFFF
126aca1545dSVictor Chong #define MASK_16 0xFFFF
127aca1545dSVictor Chong #define MASK_12 0xFFF
128aca1545dSVictor Chong #define MASK_8 0xFF
129aca1545dSVictor Chong #define MASK_4 0xF
130aca1545dSVictor Chong /* SPI register masks */
131aca1545dSVictor Chong 
132aca1545dSVictor Chong #define SSP_CPSDVR_MAX		254
133aca1545dSVictor Chong #define SSP_CPSDVR_MIN		2
134aca1545dSVictor Chong #define SSP_SCR_MAX		255
135aca1545dSVictor Chong #define SSP_SCR_MIN		0
136aca1545dSVictor Chong #define SSP_DATASIZE_MAX	16
137aca1545dSVictor Chong 
1389a2efe04SVictor Chong static enum spi_result pl022_txrx8(struct spi_chip *chip, uint8_t *wdat,
1399a2efe04SVictor Chong 	uint8_t *rdat, size_t num_pkts)
140aca1545dSVictor Chong {
141aca1545dSVictor Chong 	size_t i = 0;
142aca1545dSVictor Chong 	size_t j = 0;
143aca1545dSVictor Chong 	struct pl022_data *pd = container_of(chip, struct pl022_data, chip);
144aca1545dSVictor Chong 
1459a2efe04SVictor Chong 
1469a2efe04SVictor Chong 	if (pd->data_size_bits != 8) {
1479a2efe04SVictor Chong 		EMSG("data_size_bits should be 8, not %u",
1489a2efe04SVictor Chong 			pd->data_size_bits);
1499a2efe04SVictor Chong 		return SPI_ERR_CFG;
1509a2efe04SVictor Chong 	}
1519a2efe04SVictor Chong 
1522ff86f60SVictor Chong 	if (wdat)
15372057c7cSVictor Chong 		while (i < num_pkts) {
154*918bb3a5SEtienne Carriere 			if (io_read8(pd->base + SSPSR) & SSPSR_TNF) {
155aca1545dSVictor Chong 				/* tx 1 packet */
156*918bb3a5SEtienne Carriere 				io_write8(pd->base + SSPDR, wdat[i++]);
157aca1545dSVictor Chong 			}
158aca1545dSVictor Chong 
15972057c7cSVictor Chong 			if (rdat)
160*918bb3a5SEtienne Carriere 				if (io_read8(pd->base + SSPSR) & SSPSR_RNE) {
16172057c7cSVictor Chong 					/* rx 1 packet */
162*918bb3a5SEtienne Carriere 					rdat[j++] = io_read8(pd->base + SSPDR);
16372057c7cSVictor Chong 				}
16472057c7cSVictor Chong 		}
16572057c7cSVictor Chong 
16672057c7cSVictor Chong 	/* Capture remaining rdat not read above */
1679a2efe04SVictor Chong 	if (rdat) {
1689a2efe04SVictor Chong 		while ((j < num_pkts) &&
169*918bb3a5SEtienne Carriere 			(io_read8(pd->base + SSPSR) & SSPSR_BSY))
170*918bb3a5SEtienne Carriere 			if (io_read8(pd->base + SSPSR) & SSPSR_RNE) {
171aca1545dSVictor Chong 				/* rx 1 packet */
172*918bb3a5SEtienne Carriere 				rdat[j++] = io_read8(pd->base + SSPDR);
1732ff86f60SVictor Chong 			}
174aca1545dSVictor Chong 
1759a2efe04SVictor Chong 		if (j < num_pkts) {
1769a2efe04SVictor Chong 			EMSG("Packets requested %zu, received %zu",
1779a2efe04SVictor Chong 				num_pkts, j);
1789a2efe04SVictor Chong 			return SPI_ERR_PKTCNT;
1799a2efe04SVictor Chong 		}
180aca1545dSVictor Chong 	}
181aca1545dSVictor Chong 
1829a2efe04SVictor Chong 	return SPI_OK;
1839a2efe04SVictor Chong }
1849a2efe04SVictor Chong 
1859a2efe04SVictor Chong static enum spi_result pl022_txrx16(struct spi_chip *chip, uint16_t *wdat,
1869a2efe04SVictor Chong 	uint16_t *rdat, size_t num_pkts)
187aca1545dSVictor Chong {
188aca1545dSVictor Chong 	size_t i = 0;
189aca1545dSVictor Chong 	size_t j = 0;
190aca1545dSVictor Chong 	struct pl022_data *pd = container_of(chip, struct pl022_data, chip);
191aca1545dSVictor Chong 
1929a2efe04SVictor Chong 	if (pd->data_size_bits != 16) {
1939a2efe04SVictor Chong 		EMSG("data_size_bits should be 16, not %u",
1949a2efe04SVictor Chong 			pd->data_size_bits);
1959a2efe04SVictor Chong 		return SPI_ERR_CFG;
1969a2efe04SVictor Chong 	}
1979a2efe04SVictor Chong 
1982ff86f60SVictor Chong 	if (wdat)
19972057c7cSVictor Chong 		while (i < num_pkts) {
200*918bb3a5SEtienne Carriere 			if (io_read8(pd->base + SSPSR) & SSPSR_TNF) {
201aca1545dSVictor Chong 				/* tx 1 packet */
202*918bb3a5SEtienne Carriere 				io_write16(pd->base + SSPDR, wdat[i++]);
203aca1545dSVictor Chong 			}
204aca1545dSVictor Chong 
20572057c7cSVictor Chong 			if (rdat)
206*918bb3a5SEtienne Carriere 				if (io_read8(pd->base + SSPSR) & SSPSR_RNE) {
20772057c7cSVictor Chong 					/* rx 1 packet */
208*918bb3a5SEtienne Carriere 					rdat[j++] = io_read8(pd->base + SSPDR);
20972057c7cSVictor Chong 				}
21072057c7cSVictor Chong 		}
21172057c7cSVictor Chong 
21272057c7cSVictor Chong 	/* Capture remaining rdat not read above */
2139a2efe04SVictor Chong 	if (rdat) {
2149a2efe04SVictor Chong 		while ((j < num_pkts) &&
215*918bb3a5SEtienne Carriere 			(io_read8(pd->base + SSPSR) & SSPSR_BSY))
216*918bb3a5SEtienne Carriere 			if (io_read8(pd->base + SSPSR) & SSPSR_RNE) {
217aca1545dSVictor Chong 				/* rx 1 packet */
218*918bb3a5SEtienne Carriere 				rdat[j++] = io_read8(pd->base + SSPDR);
219aca1545dSVictor Chong 			}
220aca1545dSVictor Chong 
2219a2efe04SVictor Chong 		if (j < num_pkts) {
2229a2efe04SVictor Chong 			EMSG("Packets requested %zu, received %zu",
2239a2efe04SVictor Chong 				num_pkts, j);
2249a2efe04SVictor Chong 			return SPI_ERR_PKTCNT;
2259a2efe04SVictor Chong 		}
2269a2efe04SVictor Chong 	}
2279a2efe04SVictor Chong 
2289a2efe04SVictor Chong 	return SPI_OK;
229aca1545dSVictor Chong }
230aca1545dSVictor Chong 
231aca1545dSVictor Chong static void pl022_print_peri_id(struct pl022_data *pd __maybe_unused)
232aca1545dSVictor Chong {
233aca1545dSVictor Chong 	DMSG("Expected: 0x 22 10 ?4 00");
234aca1545dSVictor Chong 	DMSG("Read: 0x %02x %02x %02x %02x",
235*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPeriphID0),
236*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPeriphID1),
237*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPeriphID2),
238*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPeriphID3));
239aca1545dSVictor Chong }
240aca1545dSVictor Chong 
241aca1545dSVictor Chong static void pl022_print_cell_id(struct pl022_data *pd __maybe_unused)
242aca1545dSVictor Chong {
243aca1545dSVictor Chong 	DMSG("Expected: 0x 0d f0 05 b1");
244aca1545dSVictor Chong 	DMSG("Read: 0x %02x %02x %02x %02x",
245*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPCellID0),
246*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPCellID1),
247*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPCellID2),
248*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPPCellID3));
249aca1545dSVictor Chong }
250aca1545dSVictor Chong 
251aca1545dSVictor Chong static void pl022_sanity_check(struct pl022_data *pd)
252aca1545dSVictor Chong {
253aca1545dSVictor Chong 	assert(pd);
254aca1545dSVictor Chong 	assert(pd->chip.ops);
25526128b8fSVictor Chong 	assert(pd->cs_control <= PL022_CS_CTRL_MANUAL);
25626128b8fSVictor Chong 	switch (pd->cs_control) {
25726128b8fSVictor Chong 	case PL022_CS_CTRL_AUTO_GPIO:
25826128b8fSVictor Chong 		assert(pd->cs_data.gpio_data.chip);
25926128b8fSVictor Chong 		assert(pd->cs_data.gpio_data.chip->ops);
26026128b8fSVictor Chong 		break;
26126128b8fSVictor Chong 	case PL022_CS_CTRL_CB:
26226128b8fSVictor Chong 		assert(pd->cs_data.cs_cb);
26326128b8fSVictor Chong 		break;
26426128b8fSVictor Chong 	default:
26526128b8fSVictor Chong 		break;
26626128b8fSVictor Chong 	}
267aca1545dSVictor Chong 	assert(pd->clk_hz);
268aca1545dSVictor Chong 	assert(pd->speed_hz && pd->speed_hz <= pd->clk_hz/2);
269aca1545dSVictor Chong 	assert(pd->mode <= SPI_MODE3);
270aca1545dSVictor Chong 	assert(pd->data_size_bits == 8 || pd->data_size_bits == 16);
271aca1545dSVictor Chong 
272aca1545dSVictor Chong 	#ifdef PLATFORM_hikey
273aca1545dSVictor Chong 	DMSG("SSPB2BTRANS: Expected: 0x2. Read: 0x%x",
274*918bb3a5SEtienne Carriere 		io_read8(pd->base + SSPB2BTRANS));
275aca1545dSVictor Chong 	#endif
276aca1545dSVictor Chong 	pl022_print_peri_id(pd);
277aca1545dSVictor Chong 	pl022_print_cell_id(pd);
278aca1545dSVictor Chong }
279aca1545dSVictor Chong 
280aca1545dSVictor Chong static inline uint32_t pl022_calc_freq(struct pl022_data *pd,
281aca1545dSVictor Chong 	uint8_t cpsdvr, uint8_t scr)
282aca1545dSVictor Chong {
283aca1545dSVictor Chong 	return pd->clk_hz / (cpsdvr * (1 + scr));
284aca1545dSVictor Chong }
285aca1545dSVictor Chong 
28626128b8fSVictor Chong static void pl022_control_cs(struct spi_chip *chip, enum gpio_level value)
28726128b8fSVictor Chong {
28826128b8fSVictor Chong 	struct pl022_data *pd = container_of(chip, struct pl022_data, chip);
28926128b8fSVictor Chong 
29026128b8fSVictor Chong 	switch (pd->cs_control) {
29126128b8fSVictor Chong 	case PL022_CS_CTRL_AUTO_GPIO:
292*918bb3a5SEtienne Carriere 		if (io_read8(pd->base + SSPSR) & SSPSR_BSY)
29326128b8fSVictor Chong 			DMSG("pl022 busy - do NOT set CS!");
294*918bb3a5SEtienne Carriere 		while (io_read8(pd->base + SSPSR) & SSPSR_BSY)
29526128b8fSVictor Chong 			;
29626128b8fSVictor Chong 		DMSG("pl022 done - set CS!");
29726128b8fSVictor Chong 
29826128b8fSVictor Chong 		pd->cs_data.gpio_data.chip->ops->set_value(
29926128b8fSVictor Chong 			pd->cs_data.gpio_data.pin_num, value);
30026128b8fSVictor Chong 		break;
30126128b8fSVictor Chong 	case PL022_CS_CTRL_CB:
30226128b8fSVictor Chong 		pd->cs_data.cs_cb(value);
30326128b8fSVictor Chong 		break;
30426128b8fSVictor Chong 	default:
30526128b8fSVictor Chong 		break;
30626128b8fSVictor Chong 	}
30726128b8fSVictor Chong }
30826128b8fSVictor Chong 
309aca1545dSVictor Chong static void pl022_calc_clk_divisors(struct pl022_data *pd,
310aca1545dSVictor Chong 	uint8_t *cpsdvr, uint8_t *scr)
311aca1545dSVictor Chong {
312aca1545dSVictor Chong 	unsigned int freq1 = 0;
313aca1545dSVictor Chong 	unsigned int freq2 = 0;
314aca1545dSVictor Chong 	uint8_t tmp_cpsdvr1;
315aca1545dSVictor Chong 	uint8_t tmp_scr1;
316aca1545dSVictor Chong 	uint8_t tmp_cpsdvr2 = 0;
317aca1545dSVictor Chong 	uint8_t tmp_scr2 = 0;
318aca1545dSVictor Chong 
319aca1545dSVictor Chong 	for (tmp_scr1 = SSP_SCR_MIN; tmp_scr1 < SSP_SCR_MAX; tmp_scr1++) {
320aca1545dSVictor Chong 		for (tmp_cpsdvr1 = SSP_CPSDVR_MIN; tmp_cpsdvr1 < SSP_CPSDVR_MAX;
321aca1545dSVictor Chong 			tmp_cpsdvr1++) {
322aca1545dSVictor Chong 			freq1 = pl022_calc_freq(pd, tmp_cpsdvr1, tmp_scr1);
323aca1545dSVictor Chong 			if (freq1 == pd->speed_hz)
324aca1545dSVictor Chong 				goto done;
325aca1545dSVictor Chong 			else if (freq1 < pd->speed_hz)
326aca1545dSVictor Chong 				goto stage2;
327aca1545dSVictor Chong 		}
328aca1545dSVictor Chong 	}
329aca1545dSVictor Chong 
330aca1545dSVictor Chong stage2:
331aca1545dSVictor Chong 	for (tmp_cpsdvr2 = SSP_CPSDVR_MIN; tmp_cpsdvr2 < SSP_CPSDVR_MAX;
332aca1545dSVictor Chong 		tmp_cpsdvr2++) {
333aca1545dSVictor Chong 		for (tmp_scr2 = SSP_SCR_MIN; tmp_scr2 < SSP_SCR_MAX;
334aca1545dSVictor Chong 			tmp_scr2++) {
335aca1545dSVictor Chong 			freq2 = pl022_calc_freq(pd, tmp_cpsdvr2, tmp_scr2);
336aca1545dSVictor Chong 			if (freq2 <= pd->speed_hz)
337aca1545dSVictor Chong 				goto done;
338aca1545dSVictor Chong 		}
339aca1545dSVictor Chong 	}
340aca1545dSVictor Chong 
341aca1545dSVictor Chong done:
342aca1545dSVictor Chong 	if (freq1 >= freq2) {
343aca1545dSVictor Chong 		*cpsdvr = tmp_cpsdvr1;
344aca1545dSVictor Chong 		*scr = tmp_scr1;
345aca1545dSVictor Chong 		DMSG("speed: requested: %u, closest1: %u",
346aca1545dSVictor Chong 			pd->speed_hz, freq1);
347aca1545dSVictor Chong 	} else {
348aca1545dSVictor Chong 		*cpsdvr = tmp_cpsdvr2;
349aca1545dSVictor Chong 		*scr = tmp_scr2;
350aca1545dSVictor Chong 		DMSG("speed: requested: %u, closest2: %u",
351aca1545dSVictor Chong 			pd->speed_hz, freq2);
352aca1545dSVictor Chong 	}
353aca1545dSVictor Chong 	DMSG("CPSDVR: %u (0x%x), SCR: %u (0x%x)",
354aca1545dSVictor Chong 		*cpsdvr, *cpsdvr, *scr, *scr);
355aca1545dSVictor Chong }
356aca1545dSVictor Chong 
357aca1545dSVictor Chong static void pl022_flush_fifo(struct pl022_data *pd)
358aca1545dSVictor Chong {
359aca1545dSVictor Chong 	uint32_t __maybe_unused rdat;
360aca1545dSVictor Chong 
361aca1545dSVictor Chong 	do {
362*918bb3a5SEtienne Carriere 		while (io_read32(pd->base + SSPSR) & SSPSR_RNE) {
363*918bb3a5SEtienne Carriere 			rdat = io_read32(pd->base + SSPDR);
364aca1545dSVictor Chong 			DMSG("rdat: 0x%x", rdat);
365aca1545dSVictor Chong 		}
366*918bb3a5SEtienne Carriere 	} while (io_read32(pd->base + SSPSR) & SSPSR_BSY);
367aca1545dSVictor Chong }
368aca1545dSVictor Chong 
3696356eeb2SVictor Chong static void pl022_configure(struct spi_chip *chip)
370aca1545dSVictor Chong {
371aca1545dSVictor Chong 	uint16_t mode;
372aca1545dSVictor Chong 	uint16_t data_size;
373aca1545dSVictor Chong 	uint8_t cpsdvr;
374aca1545dSVictor Chong 	uint8_t scr;
375aca1545dSVictor Chong 	uint8_t lbm;
3766356eeb2SVictor Chong 	struct pl022_data *pd = container_of(chip, struct pl022_data, chip);
377aca1545dSVictor Chong 
378aca1545dSVictor Chong 	pl022_sanity_check(pd);
37926128b8fSVictor Chong 
38026128b8fSVictor Chong 	switch (pd->cs_control) {
38126128b8fSVictor Chong 	case PL022_CS_CTRL_AUTO_GPIO:
38226128b8fSVictor Chong 		DMSG("Use auto GPIO CS control");
38326128b8fSVictor Chong 		DMSG("Mask/disable interrupt for CS GPIO");
38426128b8fSVictor Chong 		pd->cs_data.gpio_data.chip->ops->set_interrupt(
38526128b8fSVictor Chong 			pd->cs_data.gpio_data.pin_num,
38626128b8fSVictor Chong 			GPIO_INTERRUPT_DISABLE);
38726128b8fSVictor Chong 		DMSG("Set CS GPIO dir to out");
38826128b8fSVictor Chong 		pd->cs_data.gpio_data.chip->ops->set_direction(
38926128b8fSVictor Chong 			pd->cs_data.gpio_data.pin_num,
39026128b8fSVictor Chong 			GPIO_DIR_OUT);
39126128b8fSVictor Chong 		break;
39226128b8fSVictor Chong 	case PL022_CS_CTRL_CB:
39326128b8fSVictor Chong 		DMSG("Use registered CS callback");
39426128b8fSVictor Chong 		break;
39526128b8fSVictor Chong 	case PL022_CS_CTRL_MANUAL:
39626128b8fSVictor Chong 		DMSG("Use manual CS control");
39726128b8fSVictor Chong 		break;
39826128b8fSVictor Chong 	default:
39926128b8fSVictor Chong 		EMSG("Invalid CS control type: %d", pd->cs_control);
40026128b8fSVictor Chong 		panic();
40126128b8fSVictor Chong 	}
40226128b8fSVictor Chong 
40326128b8fSVictor Chong 	DMSG("Pull CS high");
40426128b8fSVictor Chong 	pl022_control_cs(chip, GPIO_LEVEL_HIGH);
40526128b8fSVictor Chong 
406aca1545dSVictor Chong 	pl022_calc_clk_divisors(pd, &cpsdvr, &scr);
407aca1545dSVictor Chong 
408aca1545dSVictor Chong 	/* configure ssp based on platform settings */
409aca1545dSVictor Chong 	switch (pd->mode) {
410aca1545dSVictor Chong 	case SPI_MODE0:
4112ff86f60SVictor Chong 		DMSG("SPI mode 0");
4122ff86f60SVictor Chong 		mode = SSPCR0_SPO0 | SSPCR0_SPH0;
413aca1545dSVictor Chong 		break;
414aca1545dSVictor Chong 	case SPI_MODE1:
4152ff86f60SVictor Chong 		DMSG("SPI mode 1");
4162ff86f60SVictor Chong 		mode = SSPCR0_SPO0 | SSPCR0_SPH1;
417aca1545dSVictor Chong 		break;
418aca1545dSVictor Chong 	case SPI_MODE2:
4192ff86f60SVictor Chong 		DMSG("SPI mode 2");
4202ff86f60SVictor Chong 		mode = SSPCR0_SPO1 | SSPCR0_SPH0;
421aca1545dSVictor Chong 		break;
422aca1545dSVictor Chong 	case SPI_MODE3:
4232ff86f60SVictor Chong 		DMSG("SPI mode 3");
4242ff86f60SVictor Chong 		mode = SSPCR0_SPO1 | SSPCR0_SPH1;
425aca1545dSVictor Chong 		break;
426aca1545dSVictor Chong 	default:
427aca1545dSVictor Chong 		EMSG("Invalid SPI mode: %u", pd->mode);
428aca1545dSVictor Chong 		panic();
429aca1545dSVictor Chong 	}
430aca1545dSVictor Chong 
431aca1545dSVictor Chong 	switch (pd->data_size_bits) {
432aca1545dSVictor Chong 	case 8:
433aca1545dSVictor Chong 		DMSG("Data size: 8");
4342ff86f60SVictor Chong 		data_size = SSPCR0_DSS_8BIT;
435aca1545dSVictor Chong 		break;
436aca1545dSVictor Chong 	case 16:
437aca1545dSVictor Chong 		DMSG("Data size: 16");
4382ff86f60SVictor Chong 		data_size = SSPCR0_DSS_16BIT;
439aca1545dSVictor Chong 		break;
440aca1545dSVictor Chong 	default:
441aca1545dSVictor Chong 		EMSG("Unsupported data size: %u bits", pd->data_size_bits);
442aca1545dSVictor Chong 		panic();
443aca1545dSVictor Chong 	}
444aca1545dSVictor Chong 
445aca1545dSVictor Chong 	if (pd->loopback) {
446aca1545dSVictor Chong 		DMSG("Starting in loopback mode!");
447aca1545dSVictor Chong 		lbm = SSPCR1_LBM_YES;
448aca1545dSVictor Chong 	} else {
449aca1545dSVictor Chong 		DMSG("Starting in regular (non-loopback) mode!");
450aca1545dSVictor Chong 		lbm = SSPCR1_LBM_NO;
451aca1545dSVictor Chong 	}
452aca1545dSVictor Chong 
45326128b8fSVictor Chong 	DMSG("Set Serial Clock Rate (SCR), SPI mode (phase and clock)");
45426128b8fSVictor Chong 	DMSG("Set frame format (SPI) and data size (8- or 16-bit)");
455aca1545dSVictor Chong 	io_mask16(pd->base + SSPCR0, SHIFT_U32(scr, 8) | mode | SSPCR0_FRF_SPI |
456aca1545dSVictor Chong 		data_size, MASK_16);
457aca1545dSVictor Chong 
45826128b8fSVictor Chong 	DMSG("Set master mode, disable SSP, set loopback mode");
459aca1545dSVictor Chong 	io_mask8(pd->base + SSPCR1, SSPCR1_SOD_DISABLE | SSPCR1_MS_MASTER |
460aca1545dSVictor Chong 		SSPCR1_SSE_DISABLE | lbm, MASK_4);
461aca1545dSVictor Chong 
46226128b8fSVictor Chong 	DMSG("Set clock prescale");
463aca1545dSVictor Chong 	io_mask8(pd->base + SSPCPSR, cpsdvr, SSPCPSR_CPSDVR);
464aca1545dSVictor Chong 
46526128b8fSVictor Chong 	DMSG("Disable interrupts");
466aca1545dSVictor Chong 	io_mask8(pd->base + SSPIMSC, 0, MASK_4);
467aca1545dSVictor Chong 
46826128b8fSVictor Chong 	DMSG("Clear interrupts");
4699a2efe04SVictor Chong 	io_mask8(pd->base + SSPICR, SSPICR_RORIC | SSPICR_RTIC,
4709a2efe04SVictor Chong 		SSPICR_RORIC | SSPICR_RTIC);
4719a2efe04SVictor Chong 
47226128b8fSVictor Chong 	DMSG("Empty FIFO before starting");
4736356eeb2SVictor Chong 	pl022_flush_fifo(pd);
474aca1545dSVictor Chong }
475aca1545dSVictor Chong 
4766356eeb2SVictor Chong static void pl022_start(struct spi_chip *chip)
477aca1545dSVictor Chong {
4786356eeb2SVictor Chong 	struct pl022_data *pd = container_of(chip, struct pl022_data, chip);
479aca1545dSVictor Chong 
48026128b8fSVictor Chong 	DMSG("Enable SSP");
481aca1545dSVictor Chong 	io_mask8(pd->base + SSPCR1, SSPCR1_SSE_ENABLE, SSPCR1_SSE);
48226128b8fSVictor Chong 
48326128b8fSVictor Chong 	pl022_control_cs(chip, GPIO_LEVEL_LOW);
484aca1545dSVictor Chong }
485aca1545dSVictor Chong 
4866356eeb2SVictor Chong static void pl022_end(struct spi_chip *chip)
487aca1545dSVictor Chong {
4886356eeb2SVictor Chong 	struct pl022_data *pd = container_of(chip, struct pl022_data, chip);
4896356eeb2SVictor Chong 
49026128b8fSVictor Chong 	pl022_control_cs(chip, GPIO_LEVEL_HIGH);
49126128b8fSVictor Chong 
49226128b8fSVictor Chong 	DMSG("Disable SSP");
493aca1545dSVictor Chong 	io_mask8(pd->base + SSPCR1, SSPCR1_SSE_DISABLE, SSPCR1_SSE);
494aca1545dSVictor Chong }
495aca1545dSVictor Chong 
4966356eeb2SVictor Chong static const struct spi_ops pl022_ops = {
4976356eeb2SVictor Chong 	.configure = pl022_configure,
4986356eeb2SVictor Chong 	.start = pl022_start,
4996356eeb2SVictor Chong 	.txrx8 = pl022_txrx8,
5006356eeb2SVictor Chong 	.txrx16 = pl022_txrx16,
5016356eeb2SVictor Chong 	.end = pl022_end,
5026356eeb2SVictor Chong };
5036356eeb2SVictor Chong 
5046356eeb2SVictor Chong void pl022_init(struct pl022_data *pd)
5056356eeb2SVictor Chong {
5066356eeb2SVictor Chong 	assert(pd);
5076356eeb2SVictor Chong 	pd->chip.ops = &pl022_ops;
5086356eeb2SVictor Chong }
509