|  | /* | 
|  | * Copyright (c) 2024 Nordic Semiconductor ASA | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT nordic_nrf_bellboard_rx | 
|  |  | 
|  | #include <zephyr/devicetree.h> | 
|  | #include <zephyr/drivers/mbox.h> | 
|  | #include <zephyr/sys/util.h> | 
|  | #include <zephyr/sys/__assert.h> | 
|  |  | 
|  | #include <hal/nrf_bellboard.h> | 
|  |  | 
|  | #define BELLBOARD_NUM_IRQS 4U | 
|  |  | 
|  | BUILD_ASSERT(DT_NUM_IRQS(DT_DRV_INST(0)) <= BELLBOARD_NUM_IRQS, "# interrupt exceeds maximum"); | 
|  |  | 
|  | BUILD_ASSERT((DT_INST_PROP_LEN(0, nordic_interrupt_mapping) % 2) == 0, | 
|  | "# interrupt mappings not specified in pairs"); | 
|  |  | 
|  | /* BELLBOARD event mappings */ | 
|  | #define EVT_MAPPING_ITEM(idx) DT_INST_PROP_BY_IDX(0, nordic_interrupt_mapping, idx) | 
|  | #define BELLBOARD_GET_EVT_MAPPING(idx, _)                                                          \ | 
|  | COND_CODE_1(                                                                               \ | 
|  | DT_INST_PROP_HAS_IDX(0, nordic_interrupt_mapping, UTIL_INC(UTIL_X2(idx))),         \ | 
|  | ([EVT_MAPPING_ITEM(UTIL_INC(UTIL_X2(idx)))] = EVT_MAPPING_ITEM(UTIL_X2(idx)),),    \ | 
|  | ()) | 
|  |  | 
|  | static const uint32_t evt_mappings[BELLBOARD_NUM_IRQS] = { | 
|  | LISTIFY(DT_NUM_IRQS(DT_DRV_INST(0)), BELLBOARD_GET_EVT_MAPPING, ())}; | 
|  |  | 
|  | /* BELLBOARD instance */ | 
|  | static NRF_BELLBOARD_Type *bellboard = (NRF_BELLBOARD_Type *)DT_INST_REG_ADDR(0); | 
|  |  | 
|  | /* BELLBOARD runtime resources */ | 
|  | static mbox_callback_t cbs[NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT]; | 
|  | static void *cbs_ctx[NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT]; | 
|  | static uint32_t evt_enabled_masks[BELLBOARD_NUM_IRQS]; | 
|  |  | 
|  | static void bellboard_rx_isr(const void *parameter) | 
|  | { | 
|  | uint8_t irq_idx = (uint8_t)(uintptr_t)parameter; | 
|  | uint32_t int_pend; | 
|  |  | 
|  | int_pend = nrf_bellboard_int_pending_get(bellboard, irq_idx); | 
|  |  | 
|  | for (uint8_t i = 0U; i < NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT; i++) { | 
|  | nrf_bellboard_event_t event = nrf_bellboard_triggered_event_get(i); | 
|  |  | 
|  | if ((int_pend & BIT(i)) != 0U) { | 
|  | /* Only clear those events that have their corresponding bit set | 
|  | * in INTPEND at the time we read it. Otherwise, if two (or more) | 
|  | * events are generated in quick succession, INTPEND may be set for | 
|  | * only one of events, but we clear the EVENTS_TRIGGERED bit for | 
|  | * all of them, thus losing them. | 
|  | * | 
|  | * Assume nrf_bellboard_event_check() is true for the event | 
|  | * that raised this interrupt. | 
|  | */ | 
|  | __ASSERT_NO_MSG(nrf_bellboard_event_check(bellboard, event)); | 
|  |  | 
|  | nrf_bellboard_event_clear(bellboard, event); | 
|  |  | 
|  | if (cbs[i] != NULL) { | 
|  | cbs[i](DEVICE_DT_INST_GET(0), i, cbs_ctx[i], NULL); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint32_t bellboard_rx_max_channels_get(const struct device *dev) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | return NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT; | 
|  | } | 
|  |  | 
|  | static int bellboard_rx_register_callback(const struct device *dev, uint32_t id, mbox_callback_t cb, | 
|  | void *user_data) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | if (id >= NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | cbs[id] = cb; | 
|  | cbs_ctx[id] = user_data; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int bellboard_rx_set_enabled(const struct device *dev, uint32_t id, bool enable) | 
|  | { | 
|  | bool valid_found = false; | 
|  |  | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | if (id >= NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | for (uint8_t i = 0U; i < BELLBOARD_NUM_IRQS; i++) { | 
|  | uint32_t *evt_enabled_mask; | 
|  |  | 
|  | if ((evt_mappings[i] == 0U) || ((evt_mappings[i] & BIT(id)) == 0U)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | valid_found = true; | 
|  | evt_enabled_mask = &evt_enabled_masks[i]; | 
|  |  | 
|  | if (enable) { | 
|  | if ((*evt_enabled_mask & BIT(id)) != 0U) { | 
|  | return -EALREADY; | 
|  | } | 
|  |  | 
|  | *evt_enabled_mask |= BIT(id); | 
|  | nrf_bellboard_int_enable(bellboard, i, BIT(id)); | 
|  | } else { | 
|  | if ((*evt_enabled_mask & BIT(id)) == 0U) { | 
|  | return -EALREADY; | 
|  | } | 
|  |  | 
|  | *evt_enabled_mask &= ~BIT(id); | 
|  | nrf_bellboard_int_disable(bellboard, i, BIT(id)); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!valid_found) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static DEVICE_API(mbox, bellboard_rx_driver_api) = { | 
|  | .max_channels_get = bellboard_rx_max_channels_get, | 
|  | .register_callback = bellboard_rx_register_callback, | 
|  | .set_enabled = bellboard_rx_set_enabled, | 
|  | }; | 
|  |  | 
|  | #define BELLBOARD_IRQ_CONFIGURE(name, idx)                                                         \ | 
|  | COND_CODE_1(DT_INST_IRQ_HAS_NAME(0, name),                                                 \ | 
|  | (IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, name, irq),                                \ | 
|  | DT_INST_IRQ_BY_NAME(0, name, priority), bellboard_rx_isr,         \ | 
|  | (const void *)idx, 0);                                            \ | 
|  | irq_enable(DT_INST_IRQ_BY_NAME(0, name, irq));),                              \ | 
|  | ()) | 
|  |  | 
|  | static int bellboard_rx_init(const struct device *dev) | 
|  | { | 
|  | uint32_t evt_all_mappings = | 
|  | evt_mappings[0] | evt_mappings[1] | evt_mappings[2] | evt_mappings[3]; | 
|  |  | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | nrf_bellboard_int_disable(bellboard, 0, evt_mappings[0]); | 
|  | nrf_bellboard_int_disable(bellboard, 1, evt_mappings[1]); | 
|  | nrf_bellboard_int_disable(bellboard, 2, evt_mappings[2]); | 
|  | nrf_bellboard_int_disable(bellboard, 3, evt_mappings[3]); | 
|  |  | 
|  | for (uint8_t i = 0U; i < NRF_BELLBOARD_EVENTS_TRIGGERED_COUNT; i++) { | 
|  | if ((evt_all_mappings & BIT(i)) != 0U) { | 
|  | nrf_bellboard_event_clear(bellboard, nrf_bellboard_triggered_event_get(i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | BELLBOARD_IRQ_CONFIGURE(irq0, 0); | 
|  | BELLBOARD_IRQ_CONFIGURE(irq1, 1); | 
|  | BELLBOARD_IRQ_CONFIGURE(irq2, 2); | 
|  | BELLBOARD_IRQ_CONFIGURE(irq3, 3); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | DEVICE_DT_INST_DEFINE(0, bellboard_rx_init, NULL, NULL, NULL, POST_KERNEL, | 
|  | CONFIG_MBOX_INIT_PRIORITY, &bellboard_rx_driver_api); |