diff options
-rw-r--r-- | libhw/CMakeLists.txt | 3 | ||||
-rw-r--r-- | libhw/alarmclock.c | 21 | ||||
-rw-r--r-- | libhw/common_include/libhw/generic/alarmclock.h | 98 | ||||
-rw-r--r-- | libhw/common_include/libhw/generic/bootclock.h | 22 | ||||
-rw-r--r-- | libhw/common_include/libhw/generic/net.h | 2 | ||||
-rw-r--r-- | libhw/host_alarmclock.c | 87 | ||||
-rw-r--r-- | libhw/host_bootclock.c | 20 | ||||
-rw-r--r-- | libhw/host_net.c | 32 | ||||
-rw-r--r-- | libhw/host_sigrt.c | 21 | ||||
-rw-r--r-- | libhw/host_sigrt.h | 47 | ||||
-rw-r--r-- | libhw/rp2040_hwspi.c | 3 | ||||
-rw-r--r-- | libhw/rp2040_hwtimer.c | 195 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/rp2040_hwalarm.h | 55 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/rp2040_hwspi.h | 3 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/rp2040_hwtimer.h | 26 |
15 files changed, 436 insertions, 199 deletions
diff --git a/libhw/CMakeLists.txt b/libhw/CMakeLists.txt index 0dee422..1baeee8 100644 --- a/libhw/CMakeLists.txt +++ b/libhw/CMakeLists.txt @@ -27,7 +27,8 @@ endif() if (PICO_PLATFORM STREQUAL "host") target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include) target_sources(libhw INTERFACE - host_bootclock.c + host_sigrt.c + host_alarmclock.c host_net.c ) endif() diff --git a/libhw/alarmclock.c b/libhw/alarmclock.c new file mode 100644 index 0000000..7b311c0 --- /dev/null +++ b/libhw/alarmclock.c @@ -0,0 +1,21 @@ +/* libhw/alarmclock.c - Device-independent <libhw/generic/alarmclock.h> utilities + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libcr/coroutine.h> +#include <libmisc/vcall.h> + +#include <libhw/generic/alarmclock.h> + +static void alarmclock_sleep_intrhandler(void *_arg) { + cid_t *cid = _arg; + cr_unpause_from_intrhandler(*cid); +} + +void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns) { + cid_t cid = cr_getcid(); + struct alarmclock_trigger trigger; + VCALL(clock, add_trigger, &trigger, abstime_ns, alarmclock_sleep_intrhandler, &cid); +} diff --git a/libhw/common_include/libhw/generic/alarmclock.h b/libhw/common_include/libhw/generic/alarmclock.h new file mode 100644 index 0000000..bf0d42f --- /dev/null +++ b/libhw/common_include/libhw/generic/alarmclock.h @@ -0,0 +1,98 @@ +/* libhw/generic/alarmclock.h - Device-independent alarmclock definitions + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBHW_GENERIC_ALARMCLOCK_H_ +#define _LIBHW_GENERIC_ALARMCLOCK_H_ + +#include <stdbool.h> /* for bool */ +#include <stdint.h> /* for uint{n}_t and UINT{n}_C */ + +#include <libmisc/private.h> +#include <libmisc/vcall.h> + +/* Useful constants ***********************************************************/ + +#define NS_PER_S UINT64_C(1000000000) /* needs at least log₂(10⁹) ≈ 29.9 bits */ +#define US_PER_S UINT64_C(1000000) /* needs at least log₂(10⁶) ≈ 19.9 bits */ +#define MS_PER_S UINT64_C(1000) /* needs at least log₂(10³) ≈ 9.9 bits */ + +/* Structs ********************************************************************/ + +struct alarmclock_trigger; +struct alarmclock_trigger { + BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H) + void *alarmclock; + struct alarmclock_trigger *prev, *next; + + uint64_t fire_at_ns; + void (*cb)(void *); + void *cb_arg; + END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H) +}; + +/* Interface ******************************************************************/ + +struct alarmclock_vtable; + +typedef struct { + struct alarmclock_vtable *vtable; +} implements_alarmclock; + +struct alarmclock_vtable { + /** + * (2⁶⁴-1 nanoseconds is more than 500 years; there is little + * risk of this overflowing) + */ + uint64_t (*get_time_ns)(implements_alarmclock *self); + + /** + * Returns true on error. + * + * Implementations may return an error if fire_at_ns is more + * than UINT32_MAX µs (72 minutes) in the future. + * + * If fire_at_ns is in the past, then it will fire + * immediately. + */ + bool (*add_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger, + uint64_t fire_at_ns, + void (*cb)(void *), + void *cb_arg); + + void (*del_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger); +}; + +/* Utilities ******************************************************************/ + +void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns); + +static inline void alarmclock_sleep_for_ns(implements_alarmclock *clock, uint64_t delta_ns) { + alarmclock_sleep_until_ns(clock, VCALL(clock, get_time_ns) + delta_ns); +} + +static inline void alarmclock_sleep_for_us(implements_alarmclock *clock, uint64_t delta_us) { + alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S)); +} + +static inline void alarmclock_sleep_for_ms(implements_alarmclock *clock, uint64_t delta_ms) { + alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S)); +} + +static inline void alarmclock_sleep_for_s(implements_alarmclock *clock, uint64_t delta_s) { + alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S); +} + +/* Globals ********************************************************************/ + +extern const implements_alarmclock *bootclock; + +#define sleep_until_ns(t) alarmclock_sleep_until_ns(bootclock, t) +#define sleep_for_ns(t) alarmclock_sleep_for_ns(bootclock, t) +#define sleep_for_us(t) alarmclock_sleep_for_us(bootclock, t) +#define sleep_for_ms(t) alarmclock_sleep_for_ms(bootclock, t) +#define sleep_for_s(t) alarmclock_sleep_for_s(bootclock, t) + +#endif /* _LIBHW_GENERIC_ALARMCLOCK_H_ */ diff --git a/libhw/common_include/libhw/generic/bootclock.h b/libhw/common_include/libhw/generic/bootclock.h deleted file mode 100644 index bea5338..0000000 --- a/libhw/common_include/libhw/generic/bootclock.h +++ /dev/null @@ -1,22 +0,0 @@ -/* libhw/generic/bootclock.h - Device-independent clock definitions - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBHW_GENERIC_BOOTCLOCK_H_ -#define _LIBHW_GENERIC_BOOTCLOCK_H_ - -#include <stdint.h> /* for uint{n}_t and UINT{n}_C */ - -#define NS_PER_S UINT64_C(1000000000) /* needs at least log₂(10⁹) ≈ 29.9 bits */ -#define US_PER_S UINT64_C(1000000) /* needs at least log₂(10⁶) ≈ 19.9 bits */ -#define MS_PER_S UINT64_C(1000) /* needs at least log₂(10³) ≈ 9.9 bits */ - -/** - * (2⁶⁴-1 nanoseconds is more than 500 years; there is little - * risk of this overflowing) - */ -uint64_t bootclock_get_ns(void); - -#endif /* _LIBHW_GENERIC_BOOTCLOCK_H_ */ diff --git a/libhw/common_include/libhw/generic/net.h b/libhw/common_include/libhw/generic/net.h index af8844f..08b411f 100644 --- a/libhw/common_include/libhw/generic/net.h +++ b/libhw/common_include/libhw/generic/net.h @@ -34,7 +34,7 @@ struct net_eth_addr { /* Streams (e.g. TCP) *********************************************************/ struct net_stream_listener_vtable; -struct net_stream_conn_btable; +struct net_stream_conn_vtable; typedef struct { struct net_stream_listener_vtable *vtable; diff --git a/libhw/host_alarmclock.c b/libhw/host_alarmclock.c new file mode 100644 index 0000000..a89294c --- /dev/null +++ b/libhw/host_alarmclock.c @@ -0,0 +1,87 @@ +/* libhw/host_alarmclock.c - <libhw/generic/alarmclock.h> implementation for POSIX hosts + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <assert.h> +#include <time.h> /* for clock_gettime(), CLOCK_MONOTONIC, clockid_t, struct timespec */ + +#include <libmisc/vcall.h> + +#define IMPLEMENTATION_FOR_LIBHW_GENERIC_ALARMCLOCK_H YES +#include <libhw/generic/alarmclock.h> + +#include "host_sigrt.h" + +struct hostclock { + implements_alarmclock; + bool initialized; + clockid_t clock_id; + timer_t timer_id; + struct alarmclock_trigger *queue; +}; + +static uint64_t hostclock_get_ns(implements_alarmclock *_alarmclock) { + struct hostclock *alarmclock = + VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock); + assert(alarmclock); + + struct timespec ts; + int r; + + r = clock_gettime(alarmclock->clock_id, &ts); + assert(r == 0); + + return (((uint64_t)ts.tv_sec) * NS_PER_S) + (uint64_t)ts.tv_nsec; +} + +static void hostclock_handle_sig_alarm(int UNUSED(sig), siginfo_t *info, void *UNUSED(ucontext)) { + struct hostclock *alarmclock = info->si_value.sival_ptr; + assert(alarmclock) + + while (alarmclock->queue && + alarmclock->queue->fire_at_ns <= hostclock_get_ns(alarmclock)) { + struct alarmclock_trigger *trigger = alarmclock->queue; + trigger->cb(trigger->cb_arg); + alarmclock->queue = trigger->next; + trigger->alarmclock = NULL; + trigger->next = NULL; + trigger->prev = NULL; + } + + if (alarmclock->queue) { + timer_settime(alarmclock + } + timer_hw->alarm[alarm_num] = (uint32_t)NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns); +} + +static bool hostclock_add_trigger(implements_alarmclock *self, + struct alarmclock_trigger *trigger, + uint64_t fire_at_ns, + void (*cb)(void *), + void *cb_arg) { + struct hostclock *clock = + VCALL_SELF(struct hostclock, implements_alarmclock, _clock); + + +} + +static void hostclock_del_trigger(implements_alarmclock *self, + struct alarmclock_trigger *trigger) { +} + +/* Globals ********************************************************************/ + +static struct alarmclock_vtable hostclock_vtable = { + .get_time_ns = hostclock_get_time_ns, + .add_trigger = hostclock_add_trigger, + .del_trigger = hostclock_del_trigger, +}; + +static struct hostclock clock_monotonic = { + .vtable = hostclock_vtable, + .clockid = CLOCK_MONOTONIC, +}; + +const implements_alarmclock *bootclock = &clock_monotonic; diff --git a/libhw/host_bootclock.c b/libhw/host_bootclock.c deleted file mode 100644 index ca9565c..0000000 --- a/libhw/host_bootclock.c +++ /dev/null @@ -1,20 +0,0 @@ -/* libhw/host_bootclock.c - <libhw/generic/bootclock.h> implementation for POSIX hosts - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include <assert.h> -#include <time.h> /* for clock_gettime(), CLOCK_MONOTONIC, struct timespec */ - -#include <libhw/generic/bootclock.h> - -uint64_t bootclock_get_ns(void) { - struct timespec ts; - int r; - - r = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(r == 0); - - return (((uint64_t)ts.tv_sec) * NS_PER_S) + (uint64_t)ts.tv_nsec; -} diff --git a/libhw/host_net.c b/libhw/host_net.c index 904019a..97e7726 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -17,16 +17,18 @@ #include <sys/socket.h> /* for struct sockaddr{,_storage}, SOCK_*, SOL_*, SO_*, socket(), setsockopt(), bind(), listen(), accept() */ /* async */ #include <pthread.h> /* for pthread_* */ -#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SIGRTMIN, SIGRTMAX, SA_SIGINFO */ +#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SA_SIGINFO */ #include <libcr/coroutine.h> #include <libmisc/vcall.h> -#include <libhw/generic/bootclock.h> +#include <libhw/generic/alarmclock.h> #define IMPLEMENTATION_FOR_LIBHW_HOST_NET_H YES #include <libhw/host_net.h> +#include "host_sigrt.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */ + /* common *********************************************************************/ #define UNUSED(name) /* name __attribute__ ((unused)) */ @@ -43,9 +45,7 @@ static void hostnet_init(void) { if (hostnet_sig_io) return; - hostnet_sig_io = SIGRTMIN; - if (hostnet_sig_io > SIGRTMAX) - error(1, 0, "SIGRTMAX exceeded"); + hostnet_sig_io = host_sigrt_alloc(); action.sa_flags = SA_SIGINFO; action.sa_sigaction = hostnet_handle_sig_io; @@ -230,17 +230,12 @@ static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, si .ret = &ret, }; if (conn->read_deadline_ns) { - uint64_t now_ns = bootclock_get_ns(); + uint64_t now_ns = VCALL(bootclock, get_time_ns); if (conn->read_deadline_ns < now_ns) return -NET_ETIMEDOUT; - uint64_t timeout_ns = conn->read_deadline_ns-now_ns; - args.timeout.tv_sec = timeout_ns - /NS_PER_S; - args.timeout.tv_usec = (timeout_ns - ((uint64_t)args.timeout.tv_sec)*NS_PER_S) - /(NS_PER_S/US_PER_S); + args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); } else { - args.timeout.tv_sec = 0; - args.timeout.tv_usec = 0; + args.timeout = ns_to_host_us_time(0); } if (RUN_PTHREAD(hostnet_pthread_read, &args)) @@ -500,17 +495,12 @@ static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf .ret_port = ret_port, }; if (conn->read_deadline_ns) { - uint64_t now_ns = bootclock_get_ns(); + uint64_t now_ns = VCALL(bootclock, get_time_ns); if (conn->read_deadline_ns < now_ns) return -NET_ETIMEDOUT; - uint64_t timeout_ns = conn->read_deadline_ns-now_ns; - args.timeout.tv_sec = timeout_ns - /NS_PER_S; - args.timeout.tv_usec = (timeout_ns - ((uint64_t)args.timeout.tv_sec)*NS_PER_S) - /(NS_PER_S/US_PER_S); + args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); } else { - args.timeout.tv_sec = 0; - args.timeout.tv_usec = 0; + args.timeout = ns_to_host_us_time(0); } if (RUN_PTHREAD(hostnet_pthread_recvfrom, &args)) diff --git a/libhw/host_sigrt.c b/libhw/host_sigrt.c new file mode 100644 index 0000000..66041fc --- /dev/null +++ b/libhw/host_sigrt.c @@ -0,0 +1,21 @@ +/* libhw/host_sigrt.c - Manage glibc realtime signals + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <error.h> /* for error(3gnu) */ +#include <signal.h> /* for SIGRTMIN, SIGRTMAX */ + +#include "host_sigrt.h" + +int host_alloc_sigrt(void) { + static int next = 0; + + if (!next) + next = SIGRTMIN; + int ret = next++; + if (ret > SIGRTMAX) + error(1, 0, "SIGRTMAX exceeded"); + return ret; +} diff --git a/libhw/host_sigrt.h b/libhw/host_sigrt.h new file mode 100644 index 0000000..fa5675b --- /dev/null +++ b/libhw/host_sigrt.h @@ -0,0 +1,47 @@ +/* libhw/host_sigrt.h - Manage glibc realtime signals + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBHW_HOST_SIGRT_H_ +#define _LIBHW_HOST_SIGRT_H_ + +#include <time.h> /* for struct timespec */ +#include <sys/time.h> /* for struct timeval */ + +#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */ + +int host_sigrt_alloc(void); + +typedef struct timeval host_us_time_t; +typedef struct timespec host_ns_time_t; + +static inline host_us_time_t ns_to_host_us_time(uint64_t time_ns) { + host_us_time_t ret; + ret.tv_sec = time_ns + /NS_PER_S; + ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S) + /(NS_PER_S/US_PER_S); + return ret; +} + +static inline host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) { + host_ns_time_t ret; + ret.tv_sec = time_ns + /NS_PER_S; + ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S; + return ret; +} + +static inline uint64_t ns_from_host_us_time(host_us_time_t host_time) { + return (((uint64_t)ts.tv_sec) * NS_PER_S) + + ((uint64_t)ts.tv_nsec * (NS_PER_S/US_PER_S)); +} + +static inline uint64_t ns_from_host_ns_time(host_ns_time_t host_time) { + return (((uint64_t)ts.tv_sec) * NS_PER_S) + + ((uint64_t)ts.tv_nsec); +} + +#endif /* _LIBHW_HOST_SIGRT_H_ */ diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c index b4adeb7..4edcdf7 100644 --- a/libhw/rp2040_hwspi.c +++ b/libhw/rp2040_hwspi.c @@ -1,5 +1,4 @@ -/* libhw/rp2040_hwspi.c - `implements_spi` implementation for the RP2040's - * ARM Primecell SSP (PL022) (implementation file) +/* libhw/rp2040_hwspi.c - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022) * * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/libhw/rp2040_hwtimer.c b/libhw/rp2040_hwtimer.c index b8255b8..6daa69d 100644 --- a/libhw/rp2040_hwtimer.c +++ b/libhw/rp2040_hwtimer.c @@ -1,120 +1,165 @@ -/* libhw/rp2040_hwtimer.c - Manage the RP2040's hardware timer +/* libhw/rp2040_hwtimer.c - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer * * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ +#include <assert.h> + #include <hardware/irq.h> /* pico-sdk:hardware_irq */ #include <hardware/timer.h> /* pico-sdk:hardware_timer */ #include <libcr/coroutine.h> +#include <libmisc/vcall.h> + +#define IMPLEMENTATION_FOR_LIBHW_GENERIC_ALARMCLOCK_H YES +#include <libhw/generic/alarmclock.h> +#include <libhw/rp2040_hwtimer.h> + +/* Init ***********************************************************************/ + +struct rp2040_hwtimer { + implements_alarmclock; + enum rp2040_hwalarm_instance alarm_num; + bool initialized; + struct alarmclock_trigger *queue; +}; + +static uint64_t rp2040_hwtimer_get_time_ns(implements_alarmclock *self); +static bool rp2040_hwtimer_add_trigger(implements_alarmclock *self, + struct alarmclock_trigger *trigger, + uint64_t fire_at_ns, + void (*cb)(void *), + void *cb_arg); +static void rp2040_hwtimer_del_trigger(implements_alarmclock *self, + struct alarmclock_trigger *trigger); + +static struct alarmclock_vtable rp2040_hwtimer_vtable = { + .get_time_ns = rp2040_hwtimer_get_time_ns, + .add_trigger = rp2040_hwtimer_add_trigger, + .del_trigger = rp2040_hwtimer_del_trigger, +}; + +static struct rp2040_hwtimer hwtimers[] = { + { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 0 }, + { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 1 }, + { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 2 }, + { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 3 }, +}; +static_assert(sizeof(hwtimers)/sizeof(hwtimers[0]) == _RP2040_HWALARM_NUM); + +implements_alarmclock *rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) { + assert(alarm_num < _RP2040_HWALARM_NUM); + return &hwtimers[alarm_num]; +} -#define IMPLEMENTATION_FOR_LIBHW_RP2040_HWALARM_H YES -#include <libhw/rp2040_hwalarm.h> -#include <libhw/generic/bootclock.h> +/* Main implementation ********************************************************/ -uint64_t bootclock_get_ns(void) { - return timer_time_us_64(timer_hw)*(NS_PER_S/US_PER_S); +static uint64_t rp2040_hwtimer_get_time_ns(implements_alarmclock *) { + return timer_time_us_64(timer_hw) * (NS_PER_S/US_PER_S); } -static struct { - bool initialized; - struct rp2040_hwalarm_trigger *queue; -} alarms[_RP2040_HWALARM_NUM] = {0}; - -static void rp2040_hwalarm_intrhandler(void) { - uint irq_num = __get_current_exception() - VTABLE_FIRST_IRQ; - uint alarm_num = TIMER_ALARM_NUM_FROM_IRQ(irq_num); - assert(alarm_num < _RP2040_HWALARM_NUM); - - typeof(alarms[0]) *alarm = &alarms[alarm_num]; - - uint64_t now_us = timer_time_us_64(timer_hw); - while (alarm->queue && alarm->queue->fire_at_us <= now_us) { - struct rp2040_hwalarm_trigger *trigger = alarm->queue; - trigger->cb(trigger->cb_arg); - alarm->queue = trigger->next; - trigger->alarm = NULL; - trigger->next = NULL; - trigger->prev = NULL; - now_us = timer_time_us_64(timer_hw); - } - - hw_clear_bits(&timer_hw->intf, 1 << alarm_num); - if (alarm->queue) - timer_hw->alarm[alarm_num] = (uint32_t)alarm->queue->fire_at_us; -} +#define NS_TO_US_ROUNDUP(x) ( ( (x) + (NS_PER_S/US_PER_S)-1) / (NS_PER_S/US_PER_S) ) -bool rp2040_hwalarm_trigger_init(struct rp2040_hwalarm_trigger *self, - enum rp2040_hwalarm_instance alarm_num, - uint64_t fire_at_ns, - void (*cb)(void *), - void *cb_arg) { - assert(self); +static void rp2040_hwtimer_intrhandler(void) { + uint irq_num = __get_current_exception() - VTABLE_FIRST_IRQ; + enum rp2040_hwalarm_instance alarm_num = TIMER_ALARM_NUM_FROM_IRQ(irq_num); assert(alarm_num < _RP2040_HWALARM_NUM); + + struct rp2040_hwtimer *alarmclock = &hwtimers[alarm_num]; + + while (alarmclock->queue && + NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns) <= timer_time_us_64(timer_hw)) { + struct alarmclock_trigger *trigger = alarmclock->queue; + trigger->cb(trigger->cb_arg); + alarmclock->queue = trigger->next; + trigger->alarmclock = NULL; + trigger->next = NULL; + trigger->prev = NULL; + } + + hw_clear_bits(&timer_hw->intf, 1 << alarm_num); + if (alarmclock->queue) + timer_hw->alarm[alarm_num] = (uint32_t)NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns); +} + +static bool rp2040_hwtimer_add_trigger(implements_alarmclock *_alarmclock, + struct alarmclock_trigger *trigger, + uint64_t fire_at_ns, + void (*cb)(void *), + void *cb_arg) { + struct rp2040_hwtimer *alarmclock = + VCALL_SELF(struct rp2040_hwtimer, implements_alarmclock, _alarmclock); + assert(alarmclock); + assert(trigger); assert(fire_at_ns); assert(cb); - uint64_t fire_at_us = (fire_at_ns + (NS_PER_S/US_PER_S) - 1)/(NS_PER_S/US_PER_S); - uint64_t now_us = timer_time_us_64(timer_hw); - if (fire_at_us > now_us && (fire_at_us - now_us) > UINT32_MAX) + if (NS_TO_US_ROUNDUP(fire_at_ns) > now_us && + (NS_TO_US_ROUNDUP(fire_at_ns) - now_us) > UINT32_MAX) /* Too far in the future. */ return true; - typeof(alarms[0]) *alarm = &alarms[alarm_num]; - - self->alarm = alarm; - self->fire_at_us = fire_at_us; - self->cb = cb; - self->cb_arg = cb_arg; + trigger->alarmclock = alarmclock; + trigger->fire_at_ns = fire_at_ns; + trigger->cb = cb; + trigger->cb_arg = cb_arg; cr_disable_interrupts(); - struct rp2040_hwalarm_trigger **dst = &alarm->queue; - while (*dst && fire_at_us >= (*dst)->fire_at_us) + struct alarmclock_trigger **dst = &alarmclock->queue; + while (*dst && fire_at_ns >= (*dst)->fire_at_ns) dst = &(*dst)->next; - self->next = *dst; - self->prev = *dst ? (*dst)->prev : NULL; + trigger->next = *dst; + trigger->prev = *dst ? (*dst)->prev : NULL; if (*dst) - (*dst)->prev = self; - *dst = self; - if (!alarm->initialized) { - hw_set_bits(&timer_hw->inte, 1 << alarm_num); - irq_set_exclusive_handler(TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num), - rp2040_hwalarm_intrhandler); - irq_set_enabled(TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num), true); - alarm->initialized = true; + (*dst)->prev = trigger; + *dst = trigger; + if (!alarmclock->initialized) { + hw_set_bits(&timer_hw->inte, 1 << alarmclock->alarm_num); + irq_set_exclusive_handler(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num), + rp2040_hwtimer_intrhandler); + irq_set_enabled(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num), true); + alarmclock->initialized = true; } - if (alarm->queue == self) { + if (alarmclock->queue == trigger) { /* Force the interrupt handler to trigger as soon as * we enable interrupts. This handles the case of - * when fire_at_us is before when we called + * when fire_at_ns is before when we called * cr_disable_interrupts(). We could check * timer_time_us_64() again after calling * cr_disable_interrupts() and do this conditionally, * but I don't think that would be any more efficient * than just letting the interrupt fire. */ - hw_set_bits(&timer_hw->intf, 1 << alarm_num); + hw_set_bits(&timer_hw->intf, 1 << alarmclock->alarm_num); } cr_enable_interrupts(); return false; } -void rp2040_hwalarm_trigger_cancel(struct rp2040_hwalarm_trigger *self) { - assert(self); +static void rp2040_hwtimer_del_trigger(implements_alarmclock *_alarmclock, + struct alarmclock_trigger *trigger) { + struct rp2040_hwtimer *alarmclock = + VCALL_SELF(struct rp2040_hwtimer, implements_alarmclock, _alarmclock); + assert(alarmclock); + assert(trigger); + cr_disable_interrupts(); - if (self->alarm) { - typeof(alarms[0]) *alarm = self->alarm; - if (!self->prev) - alarm->queue = self->next; + if (trigger->alarmclock == alarmclock) { + if (!trigger->prev) + alarmclock->queue = trigger->next; else - self->prev->next = self->next; - if (self->next) - self->next->prev = self->prev; - self->alarm = NULL; - self->prev = NULL; - self->next = NULL; + trigger->prev->next = trigger->next; + if (trigger->next) + trigger->next->prev = trigger->prev; + trigger->alarmclock = NULL; + trigger->prev = NULL; + trigger->next = NULL; } cr_enable_interrupts(); } + +/* Globals ********************************************************************/ + +const implements_alarmclock *bootclock = &hwtimers[0]; diff --git a/libhw/rp2040_include/libhw/rp2040_hwalarm.h b/libhw/rp2040_include/libhw/rp2040_hwalarm.h deleted file mode 100644 index 3a7ae10..0000000 --- a/libhw/rp2040_include/libhw/rp2040_hwalarm.h +++ /dev/null @@ -1,55 +0,0 @@ -/* libhw/rp2040_hwalarm.h - Alarms for the RP2040 - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBHW_RP2040_HWALARM_H_ -#define _LIBHW_RP2040_HWALARM_H_ - -#include <stdbool.h> /* for bool */ -#include <stdint.h> /* for uint{n}_t */ - -#include <libmisc/private.h> /* for BEGIN_PRIVATE, END_PRIVATE */ - -/** - * The RP2040 has one system "timer" (which we also use for - * ./rp2040_bootclock.c) with 4 alarm interrupts. - */ -enum rp2040_hwalarm_instance { - RP2040_HWALARM_0 = 0, - RP2040_HWALARM_1 = 1, - RP2040_HWALARM_2 = 2, - RP2040_HWALARM_3 = 3, - _RP2040_HWALARM_NUM, -}; - -struct rp2040_hwalarm_trigger; -struct rp2040_hwalarm_trigger { - BEGIN_PRIVATE(LIBHW_RP2040_HWALARM_H) - void *alarm; - struct rp2040_hwalarm_trigger *prev, *next; - - uint64_t fire_at_us; - void (*cb)(void *); - void *cb_arg; - END_PRIVATE(LIBHW_RP2040_HWALARM_H) -}; - -/** - * Returns true on error. - * - * fire_at_ns must be at most UINT32_MAX µs (72 minutes) in the future - * (or an error will be returned). - * - * If fire_at_ns is in the past, then it will fire immediately. - */ -bool rp2040_hwalarm_trigger_init(struct rp2040_hwalarm_trigger *self, - enum rp2040_hwalarm_instance alarm_num, - uint64_t fire_at_ns, - void (*cb)(void *), - void *cb_arg); - -void rp2040_hwalarm_trigger_cancel(struct rp2040_hwalarm_trigger *self); - -#endif /* _LIBHW_RP2040_HWALARM_H_ */ diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h index 8d90dbb..19fb4ba 100644 --- a/libhw/rp2040_include/libhw/rp2040_hwspi.h +++ b/libhw/rp2040_include/libhw/rp2040_hwspi.h @@ -1,5 +1,4 @@ -/* libhw/rp2040_hwspi.h - `implements_spi` implementation for the RP2040's - * ARM Primecell SSP (PL022) (header file) +/* libhw/rp2040_hwspi.h - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022) * * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/libhw/rp2040_include/libhw/rp2040_hwtimer.h b/libhw/rp2040_include/libhw/rp2040_hwtimer.h new file mode 100644 index 0000000..0885edf --- /dev/null +++ b/libhw/rp2040_include/libhw/rp2040_hwtimer.h @@ -0,0 +1,26 @@ +/* libhw/rp2040_hwtimer.h - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBHW_RP2040_HWALARM_H_ +#define _LIBHW_RP2040_HWALARM_H_ + +#include <libhw/generic/alarmclock.h> + +/** + * The RP2040 has one system "timer" (which we also use for + * ./rp2040_bootclock.c) with 4 alarm interrupts. + */ +enum rp2040_hwalarm_instance { + RP2040_HWALARM_0 = 0, + RP2040_HWALARM_1 = 1, + RP2040_HWALARM_2 = 2, + RP2040_HWALARM_3 = 3, + _RP2040_HWALARM_NUM, +}; + +implements_alarmclock *rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num); + +#endif /* _LIBHW_RP2040_HWALARM_H_ */ |