1 /*
2  * Copyright (c) 2016 Piotr Mienkowski
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 /** @file
7  * @brief Atmel SAM MCU family General Purpose Input Output (GPIO)
8  * module HAL driver.
9  */
10 
11 #include <zephyr/sys/__assert.h>
12 #include "soc_gpio.h"
13 
14 /*
15  * There exist minor differences between SAM MCU family members in naming
16  * of some of the registers. Check that our expectations are met.
17  */
18 #if (!defined(PIO_IFSCER_P0) && !defined(PIO_DIFSR_P0)) \
19 	|| (!defined(PIO_IFSCDR_P0) && !defined(PIO_SCIFSR_P0)) \
20 	|| (!defined(PIO_ABCDSR_P0) && !defined(PIO_ABSR_P0))
21 #error "Unsupported Atmel SAM MCU series"
22 #endif
23 
configure_common_attr(Pio * pio,uint32_t mask,uint32_t flags)24 static void configure_common_attr(Pio *pio, uint32_t mask, uint32_t flags)
25 {
26 	/* Disable interrupts on the pin(s) */
27 	pio->PIO_IDR = mask;
28 
29 	/* Configure pull-up(s) */
30 	if (flags & SOC_GPIO_PULLUP) {
31 		pio->PIO_PUER = mask;
32 	} else {
33 		pio->PIO_PUDR = mask;
34 	}
35 
36 /* Configure pull-down only for MCU series that support it */
37 #if defined PIO_PPDER_P0
38 	/* Configure pull-down(s) */
39 	if (flags & SOC_GPIO_PULLDOWN) {
40 		pio->PIO_PPDER = mask;
41 	} else {
42 		pio->PIO_PPDDR = mask;
43 	}
44 #endif
45 
46 	/* Configure open drain (multi-drive) */
47 	if (flags & SOC_GPIO_OPENDRAIN) {
48 		pio->PIO_MDER = mask;
49 	} else {
50 		pio->PIO_MDDR = mask;
51 	}
52 }
53 
configure_input_attr(Pio * pio,uint32_t mask,uint32_t flags)54 static void configure_input_attr(Pio *pio, uint32_t mask, uint32_t flags)
55 {
56 	/* Configure input filter */
57 	if ((flags & SOC_GPIO_IN_FILTER_MASK) != 0U) {
58 		if ((flags & SOC_GPIO_IN_FILTER_MASK) == SOC_GPIO_IN_FILTER_DEBOUNCE) {
59 			/* Enable de-bounce, disable de-glitch */
60 #if defined PIO_IFSCER_P0
61 			pio->PIO_IFSCER = mask;
62 #elif defined PIO_DIFSR_P0
63 			pio->PIO_DIFSR = mask;
64 #endif
65 		} else {
66 			/* Disable de-bounce, enable de-glitch */
67 #if defined PIO_IFSCDR_P0
68 			pio->PIO_IFSCDR = mask;
69 #elif defined PIO_SCIFSR_P0
70 			pio->PIO_SCIFSR = mask;
71 #endif
72 		}
73 		pio->PIO_IFER = mask;
74 	} else {
75 		pio->PIO_IFDR = mask;
76 	}
77 
78 	/* Configure interrupt */
79 	if (flags & SOC_GPIO_INT_ENABLE) {
80 		if ((flags & SOC_GPIO_INT_TRIG_MASK) == SOC_GPIO_INT_TRIG_DOUBLE_EDGE) {
81 			/* Disable additional interrupt modes, enable the default */
82 			pio->PIO_AIMDR = mask;
83 		} else {
84 			/* Configure additional interrupt mode */
85 			if ((flags & SOC_GPIO_INT_TRIG_MASK) == SOC_GPIO_INT_TRIG_EDGE) {
86 				/* Select edge detection event */
87 				pio->PIO_ESR = mask;
88 			} else {
89 				/* Select level detection event */
90 				pio->PIO_LSR = mask;
91 			}
92 
93 			if (flags & SOC_GPIO_INT_ACTIVE_HIGH) {
94 				pio->PIO_REHLSR = mask;
95 			} else {
96 				pio->PIO_FELLSR = mask;
97 			}
98 			/* Enable additional interrupt mode */
99 			pio->PIO_AIMER = mask;
100 		}
101 		/* Enable interrupts on the pin(s) */
102 		pio->PIO_IER = mask;
103 	} else {
104 		/* Nothing to do. All interrupts were disabled in the
105 		 * beginning.
106 		 */
107 	}
108 }
109 
configure_output_attr(Pio * pio,uint32_t mask,uint32_t flags)110 static void configure_output_attr(Pio *pio, uint32_t mask, uint32_t flags)
111 {
112 	/* Enable control of the I/O line by the PIO_ODSR register */
113 	pio->PIO_OWER = mask;
114 }
115 
soc_gpio_configure(const struct soc_gpio_pin * pin)116 void soc_gpio_configure(const struct soc_gpio_pin *pin)
117 {
118 	uint32_t mask = pin->mask;
119 	Pio *pio = pin->regs;
120 	uint8_t periph_id = pin->periph_id;
121 	uint32_t flags = pin->flags;
122 	uint32_t type = pin->flags & SOC_GPIO_FUNC_MASK;
123 
124 	/* Configure pin attributes common to all functions */
125 	configure_common_attr(pio, mask, flags);
126 
127 	switch (type) {
128 	case SOC_GPIO_FUNC_A:
129 #if defined PIO_ABCDSR_P0
130 		pio->PIO_ABCDSR[0] &= ~mask;
131 		pio->PIO_ABCDSR[1] &= ~mask;
132 #elif defined PIO_ABSR_P0
133 		pio->PIO_ABSR &= ~mask;
134 #endif
135 		/* Connect pin to the peripheral (disconnect PIO block) */
136 		pio->PIO_PDR = mask;
137 		break;
138 
139 	case SOC_GPIO_FUNC_B:
140 #if defined PIO_ABCDSR_P0
141 		pio->PIO_ABCDSR[0] |= mask;
142 		pio->PIO_ABCDSR[1] &= ~mask;
143 #elif defined PIO_ABSR_P0
144 		pio->PIO_ABSR |= mask;
145 #endif
146 		/* Connect pin to the peripheral (disconnect PIO block) */
147 		pio->PIO_PDR = mask;
148 		break;
149 
150 #if defined PIO_ABCDSR_P0
151 	case SOC_GPIO_FUNC_C:
152 		pio->PIO_ABCDSR[0] &= ~mask;
153 		pio->PIO_ABCDSR[1] |= mask;
154 		/* Connect pin to the peripheral (disconnect PIO block) */
155 		pio->PIO_PDR = mask;
156 		break;
157 
158 	case SOC_GPIO_FUNC_D:
159 		pio->PIO_ABCDSR[0] |= mask;
160 		pio->PIO_ABCDSR[1] |= mask;
161 		/* Connect pin to the peripheral (disconnect PIO block) */
162 		pio->PIO_PDR = mask;
163 		break;
164 #endif
165 
166 	case SOC_GPIO_FUNC_IN:
167 		/* Enable module's clock */
168 		soc_pmc_peripheral_enable(periph_id);
169 		/* Configure pin attributes related to input function */
170 		configure_input_attr(pio, mask, flags);
171 		/* Configure pin as input */
172 		pio->PIO_ODR = mask;
173 		pio->PIO_PER = mask;
174 		break;
175 
176 	case SOC_GPIO_FUNC_OUT_1:
177 	case SOC_GPIO_FUNC_OUT_0:
178 		/* Set initial pin value */
179 		if (type == SOC_GPIO_FUNC_OUT_1) {
180 			pio->PIO_SODR = mask;
181 		} else {
182 			pio->PIO_CODR = mask;
183 		}
184 
185 		/* Configure pin attributes related to output function */
186 		configure_output_attr(pio, mask, flags);
187 		/* Configure pin(s) as output(s) */
188 		pio->PIO_OER = mask;
189 		pio->PIO_PER = mask;
190 		break;
191 
192 	default:
193 		__ASSERT(0, "Unsupported pin function, check pin.flags value");
194 		return;
195 	}
196 }
197 
soc_gpio_list_configure(const struct soc_gpio_pin pins[],unsigned int size)198 void soc_gpio_list_configure(const struct soc_gpio_pin pins[],
199 			     unsigned int size)
200 {
201 	for (int i = 0; i < size; i++) {
202 		soc_gpio_configure(&pins[i]);
203 	}
204 }
205