1 /*
2 * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include "hardware/gpio.h"
8 #include "hardware/irq.h"
9 #include "pico/unique_id.h"
10 #include "cyw43.h"
11 #include "pico/cyw43_driver.h"
12 #include "pico/async_context.h"
13
14 #ifndef CYW43_GPIO_IRQ_HANDLER_PRIORITY
15 #define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
16 #endif
17
18 #ifndef CYW43_SLEEP_CHECK_MS
19 #define CYW43_SLEEP_CHECK_MS 50
20 #endif
21
22 static async_context_t *cyw43_async_context;
23
24 static void cyw43_sleep_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
25 static void cyw43_do_poll(async_context_t *context, async_when_pending_worker_t *worker);
26
27 static async_at_time_worker_t sleep_timeout_worker = {
28 .do_work = cyw43_sleep_timeout_reached
29 };
30
31 static async_when_pending_worker_t cyw43_poll_worker = {
32 .do_work = cyw43_do_poll
33 };
34
cyw43_set_irq_enabled(bool enabled)35 static void cyw43_set_irq_enabled(bool enabled) {
36 gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, enabled);
37 }
38
39 // GPIO interrupt handler to tell us there's cyw43 has work to do
cyw43_gpio_irq_handler(void)40 static void cyw43_gpio_irq_handler(void)
41 {
42 uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
43 if (events & GPIO_IRQ_LEVEL_HIGH) {
44 // As we use a high level interrupt, it will go off forever until it's serviced
45 // So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
46 // which is called at the end of cyw43_poll_func
47 cyw43_set_irq_enabled(false);
48 async_context_set_work_pending(cyw43_async_context, &cyw43_poll_worker);
49 }
50 }
51
cyw43_irq_init(__unused void * param)52 uint32_t cyw43_irq_init(__unused void *param) {
53 #ifndef NDEBUG
54 assert(get_core_num() == async_context_core_num(cyw43_async_context));
55 #endif
56 gpio_add_raw_irq_handler_with_order_priority(CYW43_PIN_WL_HOST_WAKE, cyw43_gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
57 cyw43_set_irq_enabled(true);
58 irq_set_enabled(IO_IRQ_BANK0, true);
59 return 0;
60 }
61
cyw43_irq_deinit(__unused void * param)62 uint32_t cyw43_irq_deinit(__unused void *param) {
63 #ifndef NDEBUG
64 assert(get_core_num() == async_context_core_num(cyw43_async_context));
65 #endif
66 gpio_remove_raw_irq_handler(CYW43_PIN_WL_HOST_WAKE, cyw43_gpio_irq_handler);
67 cyw43_set_irq_enabled(false);
68 return 0;
69 }
70
cyw43_post_poll_hook(void)71 void cyw43_post_poll_hook(void) {
72 #ifndef NDEBUG
73 assert(get_core_num() == async_context_core_num(cyw43_async_context));
74 #endif
75 cyw43_set_irq_enabled(true);
76 }
77
cyw43_schedule_internal_poll_dispatch(__unused void (* func)(void))78 void cyw43_schedule_internal_poll_dispatch(__unused void (*func)(void)) {
79 assert(func == cyw43_poll);
80 async_context_set_work_pending(cyw43_async_context, &cyw43_poll_worker);
81 }
82
cyw43_do_poll(async_context_t * context,__unused async_when_pending_worker_t * worker)83 static void cyw43_do_poll(async_context_t *context, __unused async_when_pending_worker_t *worker) {
84 #ifndef NDEBUG
85 assert(get_core_num() == async_context_core_num(cyw43_async_context));
86 #endif
87 if (cyw43_poll) {
88 if (cyw43_sleep > 0) {
89 cyw43_sleep--;
90 }
91 cyw43_poll();
92 if (cyw43_sleep) {
93 async_context_add_at_time_worker_in_ms(context, &sleep_timeout_worker, CYW43_SLEEP_CHECK_MS);
94 } else {
95 async_context_remove_at_time_worker(context, &sleep_timeout_worker);
96 }
97 }
98 }
99
cyw43_sleep_timeout_reached(async_context_t * context,__unused async_at_time_worker_t * worker)100 static void cyw43_sleep_timeout_reached(async_context_t *context, __unused async_at_time_worker_t *worker) {
101 assert(context == cyw43_async_context);
102 assert(worker == &sleep_timeout_worker);
103 async_context_set_work_pending(context, &cyw43_poll_worker);
104 }
105
cyw43_driver_init(async_context_t * context)106 bool cyw43_driver_init(async_context_t *context) {
107 cyw43_init(&cyw43_state);
108 cyw43_async_context = context;
109 // we need the IRQ to be on the same core as the context, because we need to be able to enable/disable the IRQ
110 // from there later
111 async_context_execute_sync(context, cyw43_irq_init, NULL);
112 async_context_add_when_pending_worker(context, &cyw43_poll_worker);
113 return true;
114 }
115
cyw43_driver_deinit(async_context_t * context)116 void cyw43_driver_deinit(async_context_t *context) {
117 assert(context == cyw43_async_context);
118 async_context_remove_at_time_worker(context, &sleep_timeout_worker);
119 async_context_remove_when_pending_worker(context, &cyw43_poll_worker);
120 // the IRQ IS on the same core as the context, so must be de-initialized there
121 async_context_execute_sync(context, cyw43_irq_deinit, NULL);
122 cyw43_deinit(&cyw43_state);
123 cyw43_async_context = NULL;
124 }
125
126 // todo maybe add an #ifdef in cyw43_driver
storage_read_blocks(__unused uint8_t * dest,__unused uint32_t block_num,__unused uint32_t num_blocks)127 uint32_t storage_read_blocks(__unused uint8_t *dest, __unused uint32_t block_num, __unused uint32_t num_blocks) {
128 // shouldn't be used
129 panic_unsupported();
130 }
131
132 // Generate a mac address if one is not set in otp
cyw43_hal_generate_laa_mac(__unused int idx,uint8_t buf[6])133 void __attribute__((weak)) cyw43_hal_generate_laa_mac(__unused int idx, uint8_t buf[6]) {
134 CYW43_DEBUG("Warning. No mac in otp. Generating mac from board id\n");
135 pico_unique_board_id_t board_id;
136 pico_get_unique_board_id(&board_id);
137 memcpy(buf, &board_id.id[2], 6);
138 buf[0] &= (uint8_t)~0x1; // unicast
139 buf[0] |= 0x2; // locally administered
140 }
141
142 // Return mac address
cyw43_hal_get_mac(__unused int idx,uint8_t buf[6])143 void cyw43_hal_get_mac(__unused int idx, uint8_t buf[6]) {
144 // The mac should come from cyw43 otp.
145 // This is loaded into the state after the driver is initialised
146 // cyw43_hal_generate_laa_mac is called by the driver to generate a mac if otp is not set
147 memcpy(buf, cyw43_state.mac, 6);
148 }
149
150 // Prevent background processing in pensv and access by the other core
151 // These methods are called in pensv context and on either core
152 // They can be called recursively
cyw43_thread_enter(void)153 void cyw43_thread_enter(void) {
154 async_context_acquire_lock_blocking(cyw43_async_context);
155 }
156
cyw43_thread_exit(void)157 void cyw43_thread_exit(void) {
158 async_context_release_lock(cyw43_async_context);
159 }
160
161 #ifndef NDEBUG
cyw43_thread_lock_check(void)162 void cyw43_thread_lock_check(void) {
163 async_context_lock_check(cyw43_async_context);
164 }
165 #endif
166
cyw43_await_background_or_timeout_us(uint32_t timeout_us)167 void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
168 if (__get_current_exception() > 0) {
169 async_context_wait_until(cyw43_async_context, make_timeout_time_us(timeout_us));
170 return;
171 }
172 async_context_wait_for_work_until(cyw43_async_context, make_timeout_time_us(timeout_us));
173 }
174
cyw43_delay_ms(uint32_t ms)175 void cyw43_delay_ms(uint32_t ms) {
176 async_context_wait_until(cyw43_async_context, make_timeout_time_ms(ms));
177 }
178
cyw43_delay_us(uint32_t us)179 void cyw43_delay_us(uint32_t us) {
180 async_context_wait_until(cyw43_async_context, make_timeout_time_us(us));
181 }
182
183 #if !CYW43_LWIP
no_lwip_fail()184 static void no_lwip_fail() {
185 panic("cyw43 has no ethernet interface");
186 }
cyw43_cb_tcpip_init(cyw43_t * self,int itf)187 void __attribute__((weak)) cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
188 }
cyw43_cb_tcpip_deinit(cyw43_t * self,int itf)189 void __attribute__((weak)) cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
190 }
cyw43_cb_tcpip_set_link_up(cyw43_t * self,int itf)191 void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
192 no_lwip_fail();
193 }
cyw43_cb_tcpip_set_link_down(cyw43_t * self,int itf)194 void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
195 no_lwip_fail();
196 }
cyw43_cb_process_ethernet(void * cb_data,int itf,size_t len,const uint8_t * buf)197 void __attribute__((weak)) cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
198 no_lwip_fail();
199 }
200 #endif
201