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_CONFIG(node_id)						\
63 	{											\
64 		.hwChannel = DT_PROP(node_id, channel),						\
65 		.defaultPeriod = MAX_MASTER_BUS_PERIOD,						\
66 		.masterBusPrescaler = DT_PROP(node_id, prescaler) - 1,				\
67 		.allowDebugMode = DT_PROP(node_id, freeze),					\
68 		.masterMode = NXP_S32_EMIOS_MASTER_BUS_MODE(DT_STRING_TOKEN(node_id, mode)),	\
69 		.masterBusAltPrescaler = 0,							\
70 	},
71 
72 #define NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n)						\
73 	const Emios_Ip_MasterBusConfigType nxp_s32_emios_##n##_master_bus_config[] = {		\
74 		DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_CHILD(n, master_bus),			\
75 					     NXP_S32_EMIOS_MASTER_BUS_CONFIG)			\
76 	};
77 
78 #define NXP_S32_EMIOS_GENERATE_CONFIG(n)							\
79 	NXP_S32_EMIOS_GENERATE_GLOBAL_CONFIG(n)							\
80 	NXP_S32_EMIOS_GENERATE_MASTER_BUS_CONFIG(n)						\
81 	const Emios_Mcl_Ip_ConfigType nxp_s32_emios_##n##_mcl_config = {			\
82 		.channelsNumber = ARRAY_SIZE(nxp_s32_emios_##n##_master_bus_config),		\
83 		.emiosGlobalConfig = &nxp_s32_emios_##n##_global_config,			\
84 		.masterBusConfig = &nxp_s32_emios_##n##_master_bus_config			\
85 	};
86 
87 #define EMIOS_INTERRUPT_NAME(name)	DT_CAT3(EMIOS, name, _IRQ)
88 
89 /*
90  * The real interrupt handlers only defined in some circumstances, just add
91  * weak implementations to avoid populating so many preprocessor directives
92  */
93 #define EMIOS_INTERRUPT_DEFINE(node_id, prop, idx)						\
94 	__weak void EMIOS_INTERRUPT_NAME(DT_STRING_TOKEN_BY_IDX(node_id, prop, idx))(void) {}
95 
96 #define NXP_S32_EMIOS_INTERRUPT_DEFINE(n)							\
97 	DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, EMIOS_INTERRUPT_DEFINE)
98 
99 #define EMIOS_INTERRUPT_CONFIG(node_id, prop, idx)						\
100 	do {											\
101 		IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq),					\
102 			    DT_IRQ_BY_IDX(node_id, idx, priority),				\
103 			    EMIOS_INTERRUPT_NAME(DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)),\
104 			    DEVICE_DT_GET(node_id),						\
105 			    0);									\
106 		irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq));					\
107 	} while (false);
108 
109 #define NXP_S32_EMIOS_INTERRUPT_CONFIG(n)							\
110 	static void nxp_s32_emios_##n##_interrupt_config(void)					\
111 	{											\
112 		DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, EMIOS_INTERRUPT_CONFIG)		\
113 	}
114 
115 #define NXP_S32_EMIOS_INIT_DEVICE(n)								\
116 	NXP_S32_EMIOS_GENERATE_CONFIG(n)							\
117 	NXP_S32_EMIOS_INTERRUPT_DEFINE(n)							\
118 	NXP_S32_EMIOS_INTERRUPT_CONFIG(n)							\
119 	const struct nxp_s32_emios_config nxp_s32_emios_##n##_config = {			\
120 		.instance = NXP_S32_EMIOS_GET_INSTANCE(n),					\
121 		.mcl_info = (Emios_Mcl_Ip_ConfigType *)&nxp_s32_emios_##n##_mcl_config,		\
122 		.irq_config = nxp_s32_emios_##n##_interrupt_config,				\
123 	};											\
124 	DEVICE_DT_INST_DEFINE(n,								\
125 			&nxp_s32_emios_init,							\
126 			NULL,									\
127 			NULL,									\
128 			&nxp_s32_emios_##n##_config,						\
129 			POST_KERNEL,								\
130 			CONFIG_NXP_S32_EMIOS_INIT_PRIORITY,					\
131 			NULL);
132 
133 DT_INST_FOREACH_STATUS_OKAY(NXP_S32_EMIOS_INIT_DEVICE)
134