1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2016, Linaro Limited
4 */
5
6 #include <drivers/pl022_spi.h>
7 #include <drivers/pl061_gpio.h>
8 #include <hikey_peripherals.h>
9 #include <io.h>
10 #include <kernel/tee_time.h>
11 #include <mm/core_memprot.h>
12 #include <stdint.h>
13 #include <trace.h>
14 #include <util.h>
15
16 #define PL022_STAT 0x00C
17 #define PL022_STAT_BSY SHIFT_U32(1, 4)
18
spi_cs_callback(enum gpio_level value)19 static void spi_cs_callback(enum gpio_level value)
20 {
21 static bool inited;
22 static struct pl061_data pd;
23 vaddr_t gpio6_base = core_mmu_get_va(GPIO6_BASE, MEM_AREA_IO_NSEC,
24 PL061_REG_SIZE);
25 vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
26 PL022_REG_SIZE);
27
28 if (!inited) {
29 pl061_init(&pd);
30 pl061_register(gpio6_base, 6);
31 pl061_set_mode_control(GPIO6_2, PL061_MC_SW);
32 pd.chip.ops->set_interrupt(NULL, GPIO6_2,
33 GPIO_INTERRUPT_DISABLE);
34 pd.chip.ops->set_direction(NULL, GPIO6_2, GPIO_DIR_OUT);
35 inited = true;
36 }
37
38 if (io_read8(spi_base + PL022_STAT) & PL022_STAT_BSY)
39 DMSG("pl022 busy - do NOT set CS!");
40 while (io_read8(spi_base + PL022_STAT) & PL022_STAT_BSY)
41 ;
42 DMSG("pl022 done - set CS!");
43
44 pd.chip.ops->set_value(NULL, GPIO6_2, value);
45 }
46
spi_set_cs_mux(uint32_t val)47 static void spi_set_cs_mux(uint32_t val)
48 {
49 uint32_t data;
50 vaddr_t pmx0_base = core_mmu_get_va(PMX0_BASE, MEM_AREA_IO_NSEC,
51 PMX0_REG_SIZE);
52
53 if (val == PINMUX_SPI) {
54 DMSG("Configure gpio6 pin2 as SPI");
55 io_write32(pmx0_base + PMX0_IOMG106, PINMUX_SPI);
56 } else {
57 DMSG("Configure gpio6 pin2 as GPIO");
58 io_write32(pmx0_base + PMX0_IOMG106, PINMUX_GPIO);
59 }
60
61 data = io_read32(pmx0_base + PMX0_IOMG106);
62 if (data)
63 DMSG("gpio6 pin2 is SPI");
64 else
65 DMSG("gpio6 pin2 is GPIO");
66 }
67
spi_test_with_manual_cs_control(void)68 static void spi_test_with_manual_cs_control(void)
69 {
70 struct pl022_data pd;
71 vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
72 PL022_REG_SIZE);
73 uint8_t tx[3] = {0x01, 0x80, 0x00};
74 uint8_t rx[3] = {0};
75 size_t i, j, len = 3;
76 enum spi_result res;
77
78 spi_set_cs_mux(PINMUX_GPIO);
79
80 DMSG("Set CS callback");
81 pd.cs_control = PL022_CS_CTRL_MANUAL;
82
83 DMSG("spi_base: 0x%" PRIxVA "\n", spi_base);
84 DMSG("Configure SPI");
85 pd.base = spi_base;
86 pd.clk_hz = SPI_CLK_HZ;
87 pd.speed_hz = SPI_10_KHZ;
88 pd.mode = SPI_MODE0;
89 pd.data_size_bits = 8;
90 pd.loopback = true;
91
92 pl022_init(&pd);
93 pd.chip.ops->configure(&pd.chip);
94 pd.chip.ops->start(&pd.chip);
95
96 /*
97 * Pulse CS only once for the whole transmission.
98 * This is the scheme used by the pl022 driver.
99 */
100 spi_cs_callback(GPIO_LEVEL_HIGH);
101 tee_time_busy_wait(2);
102 spi_cs_callback(GPIO_LEVEL_LOW);
103 for (j = 0; j < 10; j++) {
104 DMSG("SPI test loop: %zu", j);
105 res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
106 if (res) {
107 EMSG("SPI transceive error %d", res);
108 break;
109 }
110
111 for (i = 0; i < len; i++)
112 DMSG("rx[%zu] = 0x%x", i, rx[i]);
113
114 tee_time_busy_wait(20);
115 }
116 spi_cs_callback(GPIO_LEVEL_HIGH);
117
118 /* Pulse CS once per transfer */
119 spi_cs_callback(GPIO_LEVEL_HIGH);
120 tee_time_busy_wait(2);
121 for (j = 10; j < 20; j++) {
122 DMSG("SPI test loop: %zu", j);
123 spi_cs_callback(GPIO_LEVEL_LOW);
124 res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
125 if (res) {
126 EMSG("SPI transceive error %d", res);
127 break;
128 }
129
130 for (i = 0; i < len; i++)
131 DMSG("rx[%zu] = 0x%x", i, rx[i]);
132
133 tee_time_busy_wait(20);
134 spi_cs_callback(GPIO_LEVEL_HIGH);
135 }
136
137 /* Pulse CS once per word/byte */
138 spi_set_cs_mux(PINMUX_SPI);
139 tee_time_busy_wait(2);
140 for (j = 20; j < 30; j++) {
141 DMSG("SPI test loop: %zu", j);
142 res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
143 if (res) {
144 EMSG("SPI transceive error %d", res);
145 break;
146 }
147
148 for (i = 0; i < len; i++)
149 DMSG("rx[%zu] = 0x%x", i, rx[i]);
150
151 tee_time_busy_wait(20);
152 }
153
154 pd.chip.ops->end(&pd.chip);
155 }
156
spi_test_with_registered_cs_cb(void)157 static void spi_test_with_registered_cs_cb(void)
158 {
159 struct pl022_data pd;
160 vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
161 PL022_REG_SIZE);
162 uint8_t tx[3] = {0x01, 0x80, 0x00};
163 uint8_t rx[3] = {0};
164 size_t i, j, len = 3;
165 enum spi_result res;
166
167 spi_set_cs_mux(PINMUX_GPIO);
168
169 DMSG("Set CS callback");
170 pd.cs_data.cs_cb = spi_cs_callback;
171 pd.cs_control = PL022_CS_CTRL_CB;
172
173 DMSG("spi_base: 0x%" PRIxVA "\n", spi_base);
174 DMSG("Configure SPI");
175 pd.base = spi_base;
176 pd.clk_hz = SPI_CLK_HZ;
177 pd.speed_hz = SPI_10_KHZ;
178 pd.mode = SPI_MODE0;
179 pd.data_size_bits = 8;
180 pd.loopback = true;
181
182 pl022_init(&pd);
183 pd.chip.ops->configure(&pd.chip);
184 pd.chip.ops->start(&pd.chip);
185
186 for (j = 0; j < 20; j++) {
187 DMSG("SPI test loop: %zu", j);
188 res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
189 if (res) {
190 EMSG("SPI transceive error %d", res);
191 break;
192 }
193
194 for (i = 0; i < len; i++)
195 DMSG("rx[%zu] = 0x%x", i, rx[i]);
196
197 tee_time_busy_wait(20);
198 }
199
200 pd.chip.ops->end(&pd.chip);
201 }
202
spi_test_with_builtin_cs_control(void)203 static void spi_test_with_builtin_cs_control(void)
204 {
205 struct pl061_data pd061;
206 struct pl022_data pd022;
207 vaddr_t gpio6_base = core_mmu_get_va(GPIO6_BASE, MEM_AREA_IO_NSEC,
208 PL061_REG_SIZE);
209 vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
210 PL022_REG_SIZE);
211 uint8_t tx[3] = {0x01, 0x80, 0x00};
212 uint8_t rx[3] = {0};
213 size_t i, j, len = 3;
214 enum spi_result res;
215
216 spi_set_cs_mux(PINMUX_GPIO);
217
218 DMSG("gpio6_base: 0x%" PRIxVA "\n", gpio6_base);
219 DMSG("Configure GPIO");
220 pl061_init(&pd061);
221 pl061_register(gpio6_base, 6);
222 DMSG("Enable software mode control for chip select");
223 pl061_set_mode_control(GPIO6_2, PL061_MC_SW);
224
225 pd022.cs_data.gpio_data.chip = &pd061.chip;
226 pd022.cs_data.gpio_data.pin_num = GPIO6_2;
227 pd022.cs_control = PL022_CS_CTRL_AUTO_GPIO;
228
229 DMSG("spi_base: 0x%" PRIxVA "\n", spi_base);
230 DMSG("Configure SPI");
231 pd022.base = spi_base;
232 pd022.clk_hz = SPI_CLK_HZ;
233 pd022.speed_hz = SPI_10_KHZ;
234 pd022.mode = SPI_MODE0;
235 pd022.data_size_bits = 8;
236 pd022.loopback = true;
237
238 pl022_init(&pd022);
239 pd022.chip.ops->configure(&pd022.chip);
240 pd022.chip.ops->start(&pd022.chip);
241
242 for (j = 0; j < 20; j++) {
243 DMSG("SPI test loop: %zu", j);
244 res = pd022.chip.ops->txrx8(&pd022.chip, tx, rx, len);
245 if (res) {
246 EMSG("SPI transceive error %d", res);
247 break;
248 }
249
250 for (i = 0; i < len; i++)
251 DMSG("rx[%zu] = 0x%x", i, rx[i]);
252
253 tee_time_busy_wait(20);
254 }
255
256 pd022.chip.ops->end(&pd022.chip);
257 }
258
259 /*
260 * spi_init() MUST be run before calling this function!
261 *
262 * spi_test runs some loopback tests, so the SPI module will just receive
263 * what is transmitted, i.e. 0x01, 0x80, 0x00.
264 *
265 * In non-loopback mode, the transmitted value will elicit a readback of
266 * the measured value from the ADC chip on the Linksprite 96Boards
267 * Mezzanine card [1], which can be connected to either a sliding
268 * rheostat [2] or photoresistor [3].
269 *
270 * [1] http://linksprite.com/wiki/index.php5?title=Linker_Mezzanine_card_for_96board
271 * [2] http://learn.linksprite.com/96-board/sliding-rheostat
272 * [3] http://learn.linksprite.com/96-board/photoresistor
273 */
spi_test(void)274 void spi_test(void)
275 {
276 spi_test_with_builtin_cs_control();
277 spi_test_with_registered_cs_cb();
278 spi_test_with_manual_cs_control();
279 }
280