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