1 /*
2  * Copyright (c) 2020 Linaro Ltd.
3  * Copyright (c) 2020-2021 ATL Electronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /** @file
9  * @brief Cypress PSoC-6 MCU family devicetree helper macros
10  */
11 
12 #ifndef _CYPRESS_PSOC6_DT_H_
13 #define _CYPRESS_PSOC6_DT_H_
14 
15 #include <zephyr/devicetree.h>
16 #include <zephyr/irq.h>
17 
18 /*
19  * Devicetree macros related to interrupt
20  *
21  * The main "API" macro is CY_PSOC6_IRQ_CONFIG. It is an internal definition
22  * used to configure the PSoC-6 interrupts in a generic way. This is necessary
23  * because Cortex-M0+ can handle a limited number of interrupts and have
24  * multiplexers in front of any NVIC interrupt line.
25  *
26  * The CY_PSOC6_IRQ_CONFIG expands from CY_PSOC6_DT_INST_NVIC_INSTALL, the
27  * public API used by drivers. See below code fragment:
28  *
29  * static void <driver>_psoc6_isr(const struct device *dev)
30  * {
31  *     ...
32  * }
33  *
34  * #define <DRIVER>_PSOC6_INIT(n)					\
35  *    ...								\
36  * static void <driver>_psoc6_irq_config(const struct device *port)	\
37  * {									\
38  *      CY_PSOC6_DT_INST_NVIC_INSTALL(n,				\
39  *                                    <driver>_psoc6_isr);		\
40  *      };								\
41  * };
42  *
43  * where:
44  *   n   - driver instance number
45  *   isr - isr function to be called
46  *
47  * Cortex-M4 simple pass the parameter and constructs an usual NVIC
48  * configuration code.
49  *
50  * The Cortex-M0+ must get from interrupt parent the interrupt line and
51  * configure the interrupt channel to connect PSoC-6 peripheral interrupt to
52  * Cortex-M0+ NVIC. The multiplexer is configured by CY_PSOC6_DT_NVIC_MUX_MAP
53  * using the interrupt value from the interrupt parent.
54  *
55  * see cypress,psoc6-int-mux.yaml for devicetree documentation.
56  */
57 #ifdef CONFIG_CPU_CORTEX_M0PLUS
58 /* Cortex-M0+
59  * - install config only when exists an interrupt_parent property
60  * - get peripheral irq using PROP_BY_INDEX, to avoid translation from
61  *   interrupt-parent node property.
62  * - configure interrupt channel using the channel number register value from
63  *   interrupt-parent node.
64  */
65 #define CY_PSOC6_DT_INST_NVIC_INSTALL(n, isr)	              \
66 	IF_ENABLED(DT_INST_NODE_HAS_PROP(n, interrupt_parent),\
67 		(CY_PSOC6_IRQ_CONFIG(n, isr)))
68 #define CY_PSOC6_NVIC_MUX_IRQN(n) DT_IRQN(DT_INST_PHANDLE_BY_IDX(n,\
69 					  interrupt_parent, 0))
70 
71 #define CY_PSOC6_NVIC_MUX_IRQ_PRIO(n) DT_IRQ(DT_INST_PHANDLE_BY_IDX(n,\
72 					     interrupt_parent, 0), priority)
73 /*
74  * DT_INST_PROP_BY_IDX should be used get interrupt and configure, instead
75  * DT_INST_IRQN. The DT_INST_IRQN return IRQ number with level translation,
76  * since it uses interrupt-parent, and the value at Cortex-M0 NVIC multiplexers
77  * will be wrong.
78  *
79  * See multi-level-interrupt-handling.
80  */
81 #define CY_PSOC6_NVIC_MUX_MAP(n) Cy_SysInt_SetInterruptSource( \
82 					DT_IRQN(DT_INST_PHANDLE_BY_IDX(n,\
83 						interrupt_parent, 0)), \
84 					DT_INST_PROP_BY_IDX(n, interrupts, 0))
85 #else
86 /* Cortex-M4
87  * - bypass config
88  * - uses irq directly from peripheral devicetree definition
89  * - no map/translations
90  */
91 #define CY_PSOC6_DT_INST_NVIC_INSTALL(n, isr) CY_PSOC6_IRQ_CONFIG(n, isr)
92 #define CY_PSOC6_NVIC_MUX_IRQN(n) DT_INST_IRQN(n)
93 #define CY_PSOC6_NVIC_MUX_IRQ_PRIO(n) DT_INST_IRQ(n, priority)
94 #define CY_PSOC6_NVIC_MUX_MAP(n)
95 #endif
96 
97 #define CY_PSOC6_IRQ_CONFIG(n, isr)			\
98 	do {						\
99 		IRQ_CONNECT(CY_PSOC6_NVIC_MUX_IRQN(n),	\
100 			    CY_PSOC6_NVIC_MUX_IRQ_PRIO(n),\
101 			    isr, DEVICE_DT_INST_GET(n), 0);\
102 		CY_PSOC6_NVIC_MUX_MAP(n);		\
103 		irq_enable(CY_PSOC6_NVIC_MUX_IRQN(n));	\
104 	} while (false)
105 
106 /*
107  * Devicetree related macros to construct pin control config data
108  */
109 
110 /* Get GPIO register address associated with pinctrl-0 pin at index 'i' */
111 #define CY_PSOC6_PIN_TO_GPIO_REG_ADDR(inst, i) \
112 	DT_REG_ADDR(DT_PHANDLE(DT_INST_PINCTRL_0(inst, i), cypress_pins))
113 
114 /* Get PIN associated with pinctrl-0 pin at index 'i' */
115 #define CY_PSOC6_PIN(inst, i) \
116 	DT_PHA(DT_INST_PINCTRL_0(inst, i), cypress_pins, pin)
117 
118 /* Get HSIOM value associated with pinctrl-0 pin at index 'i' */
119 #define CY_PSOC6_PIN_HSIOM(inst, i) \
120 	DT_PHA(DT_INST_PINCTRL_0(inst, i), cypress_pins, hsiom)
121 
122 /* Helper function for CY_PSOC6_PIN_FLAGS */
123 #define CY_PSOC6_PIN_FLAG(inst, i, flag) \
124 	DT_PROP(DT_INST_PINCTRL_0(inst, i), flag)
125 
126 /* Convert DT flags to SoC flags */
127 #define CY_PSOC6_PIN_FLAGS(inst, i) \
128 	(CY_PSOC6_PIN_FLAG(inst, i, bias_pull_up) << \
129 		SOC_GPIO_PULLUP_POS | \
130 	 CY_PSOC6_PIN_FLAG(inst, i, bias_pull_down) << \
131 		SOC_GPIO_PULLUP_POS | \
132 	 CY_PSOC6_PIN_FLAG(inst, i, drive_open_drain) << \
133 		SOC_GPIO_OPENDRAIN_POS | \
134 	 CY_PSOC6_PIN_FLAG(inst, i, drive_open_source) << \
135 		SOC_GPIO_OPENSOURCE_POS | \
136 	 CY_PSOC6_PIN_FLAG(inst, i, drive_push_pull) << \
137 		SOC_GPIO_PUSHPULL_POS | \
138 	 CY_PSOC6_PIN_FLAG(inst, i, input_enable) << \
139 		SOC_GPIO_INPUTENABLE_POS)
140 
141 /* Construct a soc_pio_pin element for pin cfg */
142 #define CY_PSOC6_DT_INST_PIN(inst, idx)					\
143 	{								\
144 		(GPIO_PRT_Type *)CY_PSOC6_PIN_TO_GPIO_REG_ADDR(inst, idx), \
145 		CY_PSOC6_PIN(inst, idx),				\
146 		CY_PSOC6_PIN_HSIOM(inst, idx) << SOC_GPIO_FUNC_POS |	\
147 		CY_PSOC6_PIN_FLAGS(inst, idx)				\
148 	}
149 
150 /* Get the number of pins for pinctrl-0 */
151 #define CY_PSOC6_DT_INST_NUM_PINS(inst) DT_INST_NUM_PINCTRLS_BY_IDX(inst, 0)
152 
153 /* internal macro to structure things for use with UTIL_LISTIFY */
154 #define CY_PSOC6_PIN_ELEM(idx, inst) CY_PSOC6_DT_INST_PIN(inst, idx)
155 
156 /* Construct an array initializer for soc_gpio_pin for a device instance */
157 #define CY_PSOC6_DT_INST_PINS(inst)			\
158 	{ LISTIFY(CY_PSOC6_DT_INST_NUM_PINS(inst),	\
159 		  CY_PSOC6_PIN_ELEM, (,), inst)	\
160 	}
161 
162 #endif /* _CYPRESS_PSOC6_SOC_DT_H_ */
163