| /* |
| * Copyright 2023 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| |
| #define LOG_MODULE_NAME nxp_s32_emios |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_NXP_S32_EMIOS_LOG_LEVEL); |
| |
| #include <Emios_Mcl_Ip.h> |
| #include <Emios_Mcl_Ip_Irq.h> |
| |
| #define DT_DRV_COMPAT nxp_s32_emios |
| |
| struct nxp_s32_emios_config { |
| uint8_t instance; |
| Emios_Mcl_Ip_ConfigType *mcl_info; |
| void (*irq_config)(void); |
| }; |
| |
| static int nxp_s32_emios_init(const struct device *dev) |
| { |
| const struct nxp_s32_emios_config *config = dev->config; |
| |
| if (Emios_Mcl_Ip_Init(config->instance, config->mcl_info)) { |
| LOG_ERR("Could not initialize eMIOS"); |
| return -EINVAL; |
| } |
| |
| config->irq_config(); |
| |
| return 0; |
| } |
| |
| #define MAX_MASTER_BUS_PERIOD 65535U |
| #define MIN_MASTER_BUS_PERIOD 2U |
| #define MAX_GLOB_PRESCALER 256U |
| #define MIN_GLOB_PRESCALER 1U |
| |
| #define NXP_S32_EMIOS_MASTER_BUS_MODE(mode) DT_CAT(EMIOS_IP_, mode) |
| |
| #define NXP_S32_EMIOS_INSTANCE_CHECK(idx, n) \ |
| ((DT_INST_REG_ADDR(n) == IP_EMIOS_##idx##_BASE) ? idx : 0) |
| |
| #define NXP_S32_EMIOS_GET_INSTANCE(n) \ |
| LISTIFY(__DEBRACKET eMIOS_INSTANCE_COUNT, NXP_S32_EMIOS_INSTANCE_CHECK, (|), n) |
| |
| #define NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n) \ |
| BUILD_ASSERT(IN_RANGE(DT_INST_PROP(n, clock_divider), \ |
| MIN_GLOB_PRESCALER, MAX_GLOB_PRESCALER), \ |
| "Divider for eMIOS global prescaler is out of range"); \ |
| const Emios_Ip_GlobalConfigType nxp_s32_emios_##n##_global_config = { \ |
| .allowDebugMode = true, \ |
| .clkDivVal = DT_INST_PROP(n, clock_divider) - 1U, \ |
| .enableGlobalTimeBase = true \ |
| }; |
| |
| #define NXP_S32_EMIOS_MASTER_BUS_VERIFY(node_id) \ |
| BUILD_ASSERT(IN_RANGE(DT_PROP(node_id, period), \ |
| MIN_MASTER_BUS_PERIOD, MAX_MASTER_BUS_PERIOD), \ |
| "Node "DT_NODE_PATH(node_id)": period is out of range"); |
| |
| #define NXP_S32_EMIOS_MASTER_BUS_CONFIG(node_id) \ |
| { \ |
| .hwChannel = DT_PROP(node_id, channel), \ |
| .defaultPeriod = DT_PROP(node_id, period), \ |
| .masterBusPrescaler = DT_PROP(node_id, prescaler) - 1, \ |
| .allowDebugMode = DT_PROP(node_id, freeze), \ |
| .masterMode = NXP_S32_EMIOS_MASTER_BUS_MODE(DT_STRING_TOKEN(node_id, mode)), \ |
| .masterBusAltPrescaler = 0, \ |
| }, |
| |
| #define NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n) \ |
| DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus), \ |
| NXP_S32_EMIOS_MASTER_BUS_VERIFY) \ |
| const Emios_Ip_MasterBusConfigType nxp_s32_emios_##n##_master_bus_config[] = { \ |
| DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus), \ |
| NXP_S32_EMIOS_MASTER_BUS_CONFIG) \ |
| }; |
| |
| #define NXP_S32_EMIOS_GENERATE_CONFIG(n) \ |
| NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n) \ |
| NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n) \ |
| const Emios_Mcl_Ip_ConfigType nxp_s32_emios_##n##_mcl_config = { \ |
| .channelsNumber = ARRAY_SIZE(nxp_s32_emios_##n##_master_bus_config), \ |
| .emiosGlobalConfig = &nxp_s32_emios_##n##_global_config, \ |
| .masterBusConfig = &nxp_s32_emios_##n##_master_bus_config \ |
| }; |
| |
| #define EMIOS_INTERRUPT_NAME(name) DT_CAT3(EMIOS, name, _IRQ) |
| |
| /* |
| * The real interrupt handlers only defined in some circumstances, just add |
| * weak implementations to avoid populating so many preprocessor directives |
| */ |
| #define EMIOS_INTERRUPT_DEFINE(node_id, prop, idx) \ |
| __weak void EMIOS_INTERRUPT_NAME(DT_STRING_TOKEN_BY_IDX(node_id, prop, idx))(void) {} |
| |
| #define NXP_S32_EMIOS_INTERRUPT_DEFINE(n) \ |
| DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, EMIOS_INTERRUPT_DEFINE) |
| |
| #define EMIOS_INTERRUPT_CONFIG(node_id, prop, idx) \ |
| do { \ |
| IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \ |
| DT_IRQ_BY_IDX(node_id, idx, priority), \ |
| EMIOS_INTERRUPT_NAME(DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)),\ |
| DEVICE_DT_GET(node_id), \ |
| 0); \ |
| irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \ |
| } while (false); |
| |
| #define NXP_S32_EMIOS_INTERRUPT_CONFIG(n) \ |
| static void nxp_s32_emios_##n##_interrupt_config(void) \ |
| { \ |
| DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, EMIOS_INTERRUPT_CONFIG) \ |
| } |
| |
| #define NXP_S32_EMIOS_INIT_DEVICE(n) \ |
| NXP_S32_EMIOS_GENERATE_CONFIG(n) \ |
| NXP_S32_EMIOS_INTERRUPT_DEFINE(n) \ |
| NXP_S32_EMIOS_INTERRUPT_CONFIG(n) \ |
| const struct nxp_s32_emios_config nxp_s32_emios_##n##_config = { \ |
| .instance = NXP_S32_EMIOS_GET_INSTANCE(n), \ |
| .mcl_info = (Emios_Mcl_Ip_ConfigType *)&nxp_s32_emios_##n##_mcl_config, \ |
| .irq_config = nxp_s32_emios_##n##_interrupt_config, \ |
| }; \ |
| DEVICE_DT_INST_DEFINE(n, \ |
| &nxp_s32_emios_init, \ |
| NULL, \ |
| NULL, \ |
| &nxp_s32_emios_##n##_config, \ |
| POST_KERNEL, \ |
| CONFIG_NXP_S32_EMIOS_INIT_PRIORITY, \ |
| NULL); |
| |
| DT_INST_FOREACH_STATUS_OKAY(NXP_S32_EMIOS_INIT_DEVICE) |