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