1 /*
2 * Copyright 2023 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9
10 #define LOG_MODULE_NAME nxp_s32_emios
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_NXP_S32_EMIOS_LOG_LEVEL);
13
14 #include <Emios_Mcl_Ip.h>
15 #include <Emios_Mcl_Ip_Irq.h>
16
17 #define DT_DRV_COMPAT nxp_s32_emios
18
19 struct nxp_s32_emios_config {
20 uint8_t instance;
21 Emios_Mcl_Ip_ConfigType *mcl_info;
22 void (*irq_config)(void);
23 };
24
nxp_s32_emios_init(const struct device * dev)25 static int nxp_s32_emios_init(const struct device *dev)
26 {
27 const struct nxp_s32_emios_config *config = dev->config;
28
29 if (Emios_Mcl_Ip_Init(config->instance, config->mcl_info)) {
30 LOG_ERR("Could not initialize eMIOS");
31 return -EINVAL;
32 }
33
34 config->irq_config();
35
36 return 0;
37 }
38
39 #define MAX_MASTER_BUS_PERIOD 65535U
40 #define MIN_MASTER_BUS_PERIOD 2U
41 #define MAX_GLOB_PRESCALER 256U
42 #define MIN_GLOB_PRESCALER 1U
43
44 #define NXP_S32_EMIOS_MASTER_BUS_MODE(mode) DT_CAT(EMIOS_IP_, mode)
45
46 #define NXP_S32_EMIOS_INSTANCE_CHECK(idx, n) \
47 ((DT_INST_REG_ADDR(n) == IP_EMIOS_##idx##_BASE) ? idx : 0)
48
49 #define NXP_S32_EMIOS_GET_INSTANCE(n) \
50 LISTIFY(__DEBRACKET eMIOS_INSTANCE_COUNT, NXP_S32_EMIOS_INSTANCE_CHECK, (|), n)
51
52 #define NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n) \
53 BUILD_ASSERT(IN_RANGE(DT_INST_PROP(n, clock_divider), \
54 MIN_GLOB_PRESCALER, MAX_GLOB_PRESCALER), \
55 "Divider for eMIOS global prescaler is out of range"); \
56 const Emios_Ip_GlobalConfigType nxp_s32_emios_##n##_global_config = { \
57 .allowDebugMode = true, \
58 .clkDivVal = DT_INST_PROP(n, clock_divider) - 1U, \
59 .enableGlobalTimeBase = true \
60 };
61
62 #define NXP_S32_EMIOS_MASTER_BUS_VERIFY(node_id) \
63 BUILD_ASSERT(IN_RANGE(DT_PROP(node_id, period), \
64 MIN_MASTER_BUS_PERIOD, MAX_MASTER_BUS_PERIOD), \
65 "Node "DT_NODE_PATH(node_id)": period is out of range");
66
67 #define NXP_S32_EMIOS_MASTER_BUS_CONFIG(node_id) \
68 { \
69 .hwChannel = DT_PROP(node_id, channel), \
70 .defaultPeriod = DT_PROP(node_id, period), \
71 .masterBusPrescaler = DT_PROP(node_id, prescaler) - 1, \
72 .allowDebugMode = DT_PROP(node_id, freeze), \
73 .masterMode = NXP_S32_EMIOS_MASTER_BUS_MODE(DT_STRING_TOKEN(node_id, mode)), \
74 .masterBusAltPrescaler = 0, \
75 },
76
77 #define NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n) \
78 DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus), \
79 NXP_S32_EMIOS_MASTER_BUS_VERIFY) \
80 const Emios_Ip_MasterBusConfigType nxp_s32_emios_##n##_master_bus_config[] = { \
81 DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus), \
82 NXP_S32_EMIOS_MASTER_BUS_CONFIG) \
83 };
84
85 #define NXP_S32_EMIOS_GENERATE_CONFIG(n) \
86 NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n) \
87 NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n) \
88 const Emios_Mcl_Ip_ConfigType nxp_s32_emios_##n##_mcl_config = { \
89 .channelsNumber = ARRAY_SIZE(nxp_s32_emios_##n##_master_bus_config), \
90 .emiosGlobalConfig = &nxp_s32_emios_##n##_global_config, \
91 .masterBusConfig = &nxp_s32_emios_##n##_master_bus_config \
92 };
93
94 #define EMIOS_INTERRUPT_NAME(name) DT_CAT3(EMIOS, name, _IRQ)
95
96 /*
97 * The real interrupt handlers only defined in some circumstances, just add
98 * weak implementations to avoid populating so many preprocessor directives
99 */
100 #define EMIOS_INTERRUPT_DEFINE(node_id, prop, idx) \
101 __weak void EMIOS_INTERRUPT_NAME(DT_STRING_TOKEN_BY_IDX(node_id, prop, idx))(void) {}
102
103 #define NXP_S32_EMIOS_INTERRUPT_DEFINE(n) \
104 DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, EMIOS_INTERRUPT_DEFINE)
105
106 #define EMIOS_INTERRUPT_CONFIG(node_id, prop, idx) \
107 do { \
108 IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \
109 DT_IRQ_BY_IDX(node_id, idx, priority), \
110 EMIOS_INTERRUPT_NAME(DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)),\
111 DEVICE_DT_GET(node_id), \
112 0); \
113 irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \
114 } while (false);
115
116 #define NXP_S32_EMIOS_INTERRUPT_CONFIG(n) \
117 static void nxp_s32_emios_##n##_interrupt_config(void) \
118 { \
119 DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, EMIOS_INTERRUPT_CONFIG) \
120 }
121
122 #define NXP_S32_EMIOS_INIT_DEVICE(n) \
123 NXP_S32_EMIOS_GENERATE_CONFIG(n) \
124 NXP_S32_EMIOS_INTERRUPT_DEFINE(n) \
125 NXP_S32_EMIOS_INTERRUPT_CONFIG(n) \
126 const struct nxp_s32_emios_config nxp_s32_emios_##n##_config = { \
127 .instance = NXP_S32_EMIOS_GET_INSTANCE(n), \
128 .mcl_info = (Emios_Mcl_Ip_ConfigType *)&nxp_s32_emios_##n##_mcl_config, \
129 .irq_config = nxp_s32_emios_##n##_interrupt_config, \
130 }; \
131 DEVICE_DT_INST_DEFINE(n, \
132 &nxp_s32_emios_init, \
133 NULL, \
134 NULL, \
135 &nxp_s32_emios_##n##_config, \
136 POST_KERNEL, \
137 CONFIG_NXP_S32_EMIOS_INIT_PRIORITY, \
138 NULL);
139
140 DT_INST_FOREACH_STATUS_OKAY(NXP_S32_EMIOS_INIT_DEVICE)
141