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