1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @brief File containing common functions for RPU hardware interaction
9 * using QSPI and SPI that can be invoked by shell or the driver.
10 */
11
12 #include <string.h>
13 #include <sys/time.h>
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/sys/printk.h>
17 #include <zephyr/devicetree.h>
18 #include <zephyr/dt-bindings/gpio/nordic-nrf-gpio.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/drivers/wifi/nrf_wifi/bus/rpu_hw_if.h>
21 #include <zephyr/drivers/wifi/nrf_wifi/bus/qspi_if.h>
22
23 #include "spi_if.h"
24
25 LOG_MODULE_REGISTER(wifi_nrf_bus, CONFIG_WIFI_NRF70_BUSLIB_LOG_LEVEL);
26
27 #define NRF7002_NODE DT_NODELABEL(nrf70)
28
29 static const struct gpio_dt_spec host_irq_spec =
30 GPIO_DT_SPEC_GET(NRF7002_NODE, host_irq_gpios);
31
32 static const struct gpio_dt_spec iovdd_ctrl_spec =
33 GPIO_DT_SPEC_GET(NRF7002_NODE, iovdd_ctrl_gpios);
34
35 static const struct gpio_dt_spec bucken_spec =
36 GPIO_DT_SPEC_GET(NRF7002_NODE, bucken_gpios);
37
38 char blk_name[][15] = { "SysBus", "ExtSysBus", "PBus", "PKTRAM",
39 "GRAM", "LMAC_ROM", "LMAC_RET_RAM", "LMAC_SRC_RAM",
40 "UMAC_ROM", "UMAC_RET_RAM", "UMAC_SRC_RAM" };
41
42 uint32_t rpu_7002_memmap[][3] = {
43 { 0x000000, 0x008FFF, 1 },
44 { 0x009000, 0x03FFFF, 2 },
45 { 0x040000, 0x07FFFF, 1 },
46 { 0x0C0000, 0x0F0FFF, 0 },
47 { 0x080000, 0x092000, 2 },
48 { 0x100000, 0x134000, 1 },
49 { 0x140000, 0x14C000, 1 },
50 { 0x180000, 0x190000, 1 },
51 { 0x200000, 0x261800, 1 },
52 { 0x280000, 0x2A4000, 1 },
53 { 0x300000, 0x338000, 1 }
54 };
55
56 static const struct qspi_dev *qdev;
57 static struct qspi_config *cfg;
58
validate_addr_blk(uint32_t start_addr,uint32_t end_addr,uint32_t block_no,bool * hl_flag,int * selected_blk)59 static int validate_addr_blk(uint32_t start_addr,
60 uint32_t end_addr,
61 uint32_t block_no,
62 bool *hl_flag,
63 int *selected_blk)
64 {
65 uint32_t *block_map = rpu_7002_memmap[block_no];
66
67 if (((start_addr >= block_map[0]) && (start_addr <= block_map[1])) &&
68 ((end_addr >= block_map[0]) && (end_addr <= block_map[1]))) {
69 if (block_no == PKTRAM) {
70 *hl_flag = 0;
71 }
72 *selected_blk = block_no;
73 return 0;
74 }
75
76 return -1;
77 }
78
rpu_validate_addr(uint32_t start_addr,uint32_t len,bool * hl_flag)79 static int rpu_validate_addr(uint32_t start_addr, uint32_t len, bool *hl_flag)
80 {
81 int ret = 0, i;
82 uint32_t end_addr;
83 int selected_blk;
84
85 end_addr = start_addr + len - 1;
86
87 *hl_flag = 1;
88
89 for (i = 0; i < NUM_MEM_BLOCKS; i++) {
90 ret = validate_addr_blk(start_addr, end_addr, i, hl_flag, &selected_blk);
91 if (!ret) {
92 break;
93 }
94 }
95
96 if (ret) {
97 LOG_ERR("Address validation failed - pls check memmory map and re-try");
98 return -1;
99 }
100
101 if ((selected_blk == LMAC_ROM) || (selected_blk == UMAC_ROM)) {
102 LOG_ERR("Error: Cannot write to ROM blocks");
103 return -1;
104 }
105
106 cfg->qspi_slave_latency = (*hl_flag) ? rpu_7002_memmap[selected_blk][2] : 0;
107
108 return 0;
109 }
110
rpu_irq_config(struct gpio_callback * irq_callback_data,void (* irq_handler)())111 int rpu_irq_config(struct gpio_callback *irq_callback_data, void (*irq_handler)())
112 {
113 int ret;
114
115 if (!device_is_ready(host_irq_spec.port)) {
116 LOG_ERR("Host IRQ GPIO %s is not ready", host_irq_spec.port->name);
117 return -ENODEV;
118 }
119
120 ret = gpio_pin_configure_dt(&host_irq_spec, GPIO_INPUT);
121 if (ret) {
122 LOG_ERR("Failed to configure host_irq pin %d", host_irq_spec.pin);
123 goto out;
124 }
125
126 ret = gpio_pin_interrupt_configure_dt(&host_irq_spec,
127 GPIO_INT_EDGE_TO_ACTIVE);
128 if (ret) {
129 LOG_ERR("Failed to configure interrupt on host_irq pin %d",
130 host_irq_spec.pin);
131 goto out;
132 }
133
134 gpio_init_callback(irq_callback_data,
135 irq_handler,
136 BIT(host_irq_spec.pin));
137
138 ret = gpio_add_callback(host_irq_spec.port, irq_callback_data);
139 if (ret) {
140 LOG_ERR("Failed to add callback on host_irq pin %d",
141 host_irq_spec.pin);
142 goto out;
143 }
144
145 LOG_DBG("Finished Interrupt config\n");
146
147 out:
148 return ret;
149 }
150
rpu_irq_remove(struct gpio_callback * irq_callback_data)151 int rpu_irq_remove(struct gpio_callback *irq_callback_data)
152 {
153 int ret;
154
155 ret = gpio_pin_configure_dt(&host_irq_spec, GPIO_DISCONNECTED);
156 if (ret) {
157 LOG_ERR("Failed to remove host_irq pin %d", host_irq_spec.pin);
158 goto out;
159 }
160
161 ret = gpio_remove_callback(host_irq_spec.port, irq_callback_data);
162 if (ret) {
163 LOG_ERR("Failed to remove callback on host_irq pin %d",
164 host_irq_spec.pin);
165 goto out;
166 }
167
168 out:
169 return ret;
170 }
171
rpu_gpio_config(void)172 static int rpu_gpio_config(void)
173 {
174 int ret;
175
176 if (!device_is_ready(iovdd_ctrl_spec.port)) {
177 LOG_ERR("IOVDD GPIO %s is not ready", iovdd_ctrl_spec.port->name);
178 return -ENODEV;
179 }
180
181 if (!device_is_ready(bucken_spec.port)) {
182 LOG_ERR("BUCKEN GPIO %s is not ready", bucken_spec.port->name);
183 return -ENODEV;
184 }
185
186 ret = gpio_pin_configure_dt(&bucken_spec, (GPIO_OUTPUT | NRF_GPIO_DRIVE_H0H1));
187 if (ret) {
188 LOG_ERR("BUCKEN GPIO configuration failed...");
189 return ret;
190 }
191
192 ret = gpio_pin_configure_dt(&iovdd_ctrl_spec, GPIO_OUTPUT);
193 if (ret) {
194 LOG_ERR("IOVDD GPIO configuration failed...");
195 gpio_pin_configure_dt(&bucken_spec, GPIO_DISCONNECTED);
196 return ret;
197 }
198
199 LOG_DBG("GPIO configuration done...\n");
200
201 return 0;
202 }
203
rpu_gpio_remove(void)204 static int rpu_gpio_remove(void)
205 {
206 int ret;
207
208 ret = gpio_pin_configure_dt(&bucken_spec, GPIO_DISCONNECTED);
209 if (ret) {
210 LOG_ERR("BUCKEN GPIO remove failed...");
211 return ret;
212 }
213
214 ret = gpio_pin_configure_dt(&iovdd_ctrl_spec, GPIO_DISCONNECTED);
215 if (ret) {
216 LOG_ERR("IOVDD GPIO remove failed...");
217 return ret;
218 }
219
220 LOG_DBG("GPIO remove done...\n");
221 return ret;
222 }
223
rpu_pwron(void)224 static int rpu_pwron(void)
225 {
226 int ret;
227
228 ret = gpio_pin_set_dt(&bucken_spec, 1);
229 if (ret) {
230 LOG_ERR("BUCKEN GPIO set failed...");
231 return ret;
232 }
233 /* Settling time is 50us (H0) or 100us (L0) */
234 k_msleep(1);
235
236 ret = gpio_pin_set_dt(&iovdd_ctrl_spec, 1);
237 if (ret) {
238 LOG_ERR("IOVDD GPIO set failed...");
239 gpio_pin_set_dt(&bucken_spec, 0);
240 return ret;
241 }
242 /* Settling time for iovdd nRF7002 DK/EK - switch (TCK106AG): ~600us */
243 k_msleep(1);
244
245 if ((bucken_spec.port == iovdd_ctrl_spec.port) &&
246 (bucken_spec.pin == iovdd_ctrl_spec.pin)) {
247 /* When a single GPIO is used, we need a total wait time after bucken assertion
248 * to be 6ms (1ms + 1ms + 4ms).
249 */
250 k_msleep(4);
251 }
252
253 LOG_DBG("Bucken = %d, IOVDD = %d", gpio_pin_get_dt(&bucken_spec),
254 gpio_pin_get_dt(&iovdd_ctrl_spec));
255
256 return ret;
257 }
258
rpu_pwroff(void)259 static int rpu_pwroff(void)
260 {
261 int ret;
262
263 ret = gpio_pin_set_dt(&bucken_spec, 0); /* BUCKEN = 0 */
264 if (ret) {
265 LOG_ERR("BUCKEN GPIO set failed...");
266 return ret;
267 }
268
269 ret = gpio_pin_set_dt(&iovdd_ctrl_spec, 0); /* IOVDD CNTRL = 0 */
270 if (ret) {
271 LOG_ERR("IOVDD GPIO set failed...");
272 return ret;
273 }
274
275 return ret;
276 }
277
rpu_read(unsigned int addr,void * data,int len)278 int rpu_read(unsigned int addr, void *data, int len)
279 {
280 bool hl_flag;
281
282 if (rpu_validate_addr(addr, len, &hl_flag)) {
283 return -1;
284 }
285
286 if (hl_flag) {
287 return qdev->hl_read(addr, data, len);
288 } else {
289 return qdev->read(addr, data, len);
290 }
291 }
292
rpu_write(unsigned int addr,const void * data,int len)293 int rpu_write(unsigned int addr, const void *data, int len)
294 {
295 bool hl_flag;
296
297 if (rpu_validate_addr(addr, len, &hl_flag)) {
298 return -1;
299 }
300
301 return qdev->write(addr, data, len);
302 }
303
rpu_sleep(void)304 int rpu_sleep(void)
305 {
306 #if CONFIG_NRF70_ON_QSPI
307 return qspi_cmd_sleep_rpu(&qspi_perip);
308 #else
309 return spim_cmd_sleep_rpu_fn();
310 #endif
311 }
312
rpu_wakeup(void)313 int rpu_wakeup(void)
314 {
315 int ret;
316
317 ret = rpu_wrsr2(1);
318 if (ret) {
319 LOG_ERR("Error: WRSR2 failed");
320 return ret;
321 }
322
323 ret = rpu_rdsr2();
324 if (ret < 0) {
325 LOG_ERR("Error: RDSR2 failed");
326 return ret;
327 }
328
329 ret = rpu_rdsr1();
330 if (ret < 0) {
331 LOG_ERR("Error: RDSR1 failed");
332 return ret;
333 }
334
335 return 0;
336 }
337
rpu_sleep_status(void)338 int rpu_sleep_status(void)
339 {
340 return rpu_rdsr1();
341 }
342
rpu_get_sleep_stats(uint32_t addr,uint32_t * buff,uint32_t wrd_len)343 void rpu_get_sleep_stats(uint32_t addr, uint32_t *buff, uint32_t wrd_len)
344 {
345 int ret;
346
347 ret = rpu_wakeup();
348 if (ret) {
349 LOG_ERR("Error: RPU wakeup failed");
350 return;
351 }
352
353 ret = rpu_read(addr, buff, wrd_len * 4);
354 if (ret) {
355 LOG_ERR("Error: RPU read failed");
356 return;
357 }
358
359 ret = rpu_sleep();
360 if (ret) {
361 LOG_ERR("Error: RPU sleep failed");
362 return;
363 }
364 }
365
rpu_wrsr2(uint8_t data)366 int rpu_wrsr2(uint8_t data)
367 {
368 int ret;
369
370 #if CONFIG_NRF70_ON_QSPI
371 ret = qspi_cmd_wakeup_rpu(&qspi_perip, data);
372 #else
373 ret = spim_cmd_rpu_wakeup_fn(data);
374 #endif
375
376 LOG_DBG("Written 0x%x to WRSR2", data);
377 return ret;
378 }
379
rpu_rdsr2(void)380 int rpu_rdsr2(void)
381 {
382 #if CONFIG_NRF70_ON_QSPI
383 return qspi_validate_rpu_wake_writecmd(&qspi_perip);
384 #else
385 return spi_validate_rpu_wake_writecmd();
386 #endif
387 }
388
rpu_rdsr1(void)389 int rpu_rdsr1(void)
390 {
391 #if CONFIG_NRF70_ON_QSPI
392 return qspi_wait_while_rpu_awake(&qspi_perip);
393 #else
394 return spim_wait_while_rpu_awake();
395 #endif
396 }
397
398
rpu_clks_on(void)399 int rpu_clks_on(void)
400 {
401 uint32_t rpu_clks = 0x100;
402 /* Enable RPU Clocks */
403 qdev->write(0x048C20, &rpu_clks, 4);
404 LOG_DBG("RPU Clocks ON...");
405 return 0;
406 }
407
408 #define RPU_EXP_SIG 0x42000020
409 /* Read a known value from RPU memory to validate RPU communication */
rpu_validate_comms(void)410 int rpu_validate_comms(void)
411 {
412 uint32_t rpu_test;
413 int ret;
414
415 /* UCCP_SOC_FAB_MST_READ_IDLE - HW reset value */
416 ret = rpu_read(0x0005C, &rpu_test, 4);
417 if (ret) {
418 LOG_ERR("Error: RPU comms test: read failed\n");
419 return ret;
420 }
421
422 if (rpu_test != RPU_EXP_SIG) {
423 LOG_ERR("Error: RPU comms test: sig failed: expected 0x%x, got 0x%x\n",
424 RPU_EXP_SIG, rpu_test);
425 return -1;
426 }
427
428 LOG_DBG("RPU comms test passed\n");
429
430 return 0;
431 }
432
rpu_init(void)433 int rpu_init(void)
434 {
435 int ret;
436
437 qdev = qspi_dev();
438 cfg = qspi_get_config();
439
440 ret = rpu_gpio_config();
441 if (ret) {
442 goto out;
443 }
444
445 #ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH
446 ret = sr_gpio_config();
447 if (ret) {
448 goto remove_rpu_gpio;
449 }
450 #endif
451 ret = rpu_pwron();
452 if (ret) {
453 #ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH
454 goto remove_sr_gpio;
455 #else
456 goto remove_rpu_gpio;
457 #endif
458 }
459
460 return 0;
461
462 #ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH
463 remove_sr_gpio:
464 sr_gpio_remove();
465 #endif
466 remove_rpu_gpio:
467 rpu_gpio_remove();
468 out:
469 return ret;
470 }
471
rpu_enable(void)472 int rpu_enable(void)
473 {
474 int ret;
475
476 ret = rpu_wakeup();
477 if (ret) {
478 goto rpu_pwroff;
479 }
480
481 ret = rpu_clks_on();
482 if (ret) {
483 goto rpu_pwroff;
484 }
485
486 /* TODO: rpu_validate_comms() needs firmware download to be done
487 * successfully before it can be called. So, disable this for
488 * nrf70_buslib only usage.
489 */
490 #ifdef CONFIG_WIFI_NRF70
491 ret = rpu_validate_comms();
492 if (ret) {
493 goto rpu_pwroff;
494 }
495 #endif
496 return 0;
497 rpu_pwroff:
498 rpu_pwroff();
499 return ret;
500 }
501
rpu_disable(void)502 int rpu_disable(void)
503 {
504 int ret;
505
506 ret = rpu_pwroff();
507 if (ret) {
508 goto out;
509 }
510 ret = rpu_gpio_remove();
511 if (ret) {
512 goto out;
513 }
514
515 #ifdef CONFIG_NRF70_SR_COEX_RF_SWITCH
516 ret = sr_gpio_remove();
517 if (ret) {
518 goto out;
519 }
520 #endif
521 qdev = NULL;
522 cfg = NULL;
523
524 out:
525 return ret;
526 }
527