Skip to content

Commit 33c35da

Browse files
drivers: gpio: add gpio for sama7g5
Add driver for sama7g5 GPIO controller (PIO4) Signed-off-by: Xing Chen <xing.chen@microchip.com>
1 parent b15404f commit 33c35da

File tree

5 files changed

+432
-0
lines changed

5 files changed

+432
-0
lines changed

drivers/gpio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_RZT2M gpio_rzt2m.c)
9797
zephyr_library_sources_ifdef(CONFIG_GPIO_SAM gpio_sam.c)
9898
zephyr_library_sources_ifdef(CONFIG_GPIO_SAM0 gpio_sam0.c)
9999
zephyr_library_sources_ifdef(CONFIG_GPIO_SAM4L gpio_sam4l.c)
100+
zephyr_library_sources_ifdef(CONFIG_GPIO_SAM_PIO4 gpio_sam_pio4.c)
100101
zephyr_library_sources_ifdef(CONFIG_GPIO_SEDI gpio_sedi.c)
101102
zephyr_library_sources_ifdef(CONFIG_GPIO_SI32 gpio_si32.c)
102103
zephyr_library_sources_ifdef(CONFIG_GPIO_SIFIVE gpio_sifive.c)

drivers/gpio/Kconfig.sam

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@ config GPIO_SAM4L
1717
depends on DT_HAS_ATMEL_SAM4L_GPIO_ENABLED
1818
help
1919
Enable support for the Atmel SAM4L 'PORT' GPIO controllers.
20+
21+
config GPIO_SAM_PIO4
22+
bool "Microchip SAM PIO4 GPIO driver"
23+
default y
24+
depends on DT_HAS_MICROCHIP_SAM_PIO4_ENABLED
25+
help
26+
Microchip Parallel Input/Output Controller is used on SAMA5D2 and
27+
SAMA7G5 SoC series

drivers/gpio/gpio_sam_pio4.c

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
/*
2+
* Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
#define DT_DRV_COMPAT microchip_sam_pio4
9+
10+
#include <errno.h>
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/device.h>
13+
#include <zephyr/init.h>
14+
#include <soc.h>
15+
#include <zephyr/drivers/gpio.h>
16+
#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
17+
#include <zephyr/dt-bindings/gpio/microchip-sam-gpio.h>
18+
#include <zephyr/irq.h>
19+
20+
#include <zephyr/drivers/gpio/gpio_utils.h>
21+
22+
typedef void (*config_func_t)(const struct device *dev);
23+
24+
struct gpio_sam_config {
25+
/* gpio_driver_config needs to be first */
26+
struct gpio_driver_config common;
27+
pio_group_registers_t *regs;
28+
config_func_t config_func;
29+
30+
const struct atmel_sam_pmc_config clock_cfg;
31+
};
32+
33+
struct gpio_sam_runtime {
34+
/* gpio_driver_data needs to be first */
35+
struct gpio_driver_data common;
36+
struct k_spinlock lock;
37+
sys_slist_t cb;
38+
};
39+
40+
static int gpio_sam_config(const struct device *dev, gpio_pin_t pin,
41+
gpio_flags_t flags)
42+
{
43+
const struct gpio_sam_config * const cfg = dev->config;
44+
struct gpio_sam_runtime *context = dev->data;
45+
pio_group_registers_t * const pio = cfg->regs;
46+
k_spinlock_key_t key;
47+
const uint32_t mask = BIT(pin);
48+
uint32_t conf;
49+
50+
/* Check if pin number is out of range */
51+
if ((mask & cfg->common.port_pin_mask) == 0U) {
52+
return -ENOTSUP;
53+
}
54+
55+
/* Get PIO configuration */
56+
key = k_spin_lock(&context->lock);
57+
pio->PIO_MSKR = mask;
58+
conf = pio->PIO_CFGR;
59+
k_spin_unlock(&context->lock, key);
60+
61+
if ((flags & GPIO_SINGLE_ENDED) != 0U) {
62+
if ((flags & GPIO_LINE_OPEN_DRAIN) != 0U) {
63+
/* Enable open-drain drive mode */
64+
conf &= ~PIO_S_PIO_CFGR_OPD_Msk;
65+
conf |= PIO_S_PIO_CFGR_OPD(PIO_S_PIO_CFGR_OPD_ENABLED_Val);
66+
} else {
67+
/* Open-drain is the only supported single-ended mode */
68+
return -ENOTSUP;
69+
}
70+
} else {
71+
/* Disable open-drain drive mode */
72+
conf &= ~PIO_S_PIO_CFGR_OPD_Msk;
73+
conf |= PIO_S_PIO_CFGR_OPD(PIO_S_PIO_CFGR_OPD_DISABLED_Val);
74+
}
75+
76+
if ((flags & (GPIO_OUTPUT | GPIO_INPUT)) == 0U) {
77+
/* Neither input nor output mode is selected */
78+
79+
/* Disable the interrupt. */
80+
pio->PIO_IDR = mask;
81+
/* Disable pull-up and pull-down */
82+
conf &= ~(PIO_CFGR_PUEN_Msk | PIO_CFGR_PDEN_Msk);
83+
84+
/* Let the PIO control the pin (instead of a peripheral). */
85+
conf &= ~PIO_S_PIO_CFGR_FUNC_Msk;
86+
/* Disable output. */
87+
conf &= ~PIO_S_PIO_CFGR_DIR_Msk;
88+
89+
/* Update PIO configuration */
90+
key = k_spin_lock(&context->lock);
91+
pio->PIO_MSKR = mask;
92+
pio->PIO_CFGR = conf;
93+
k_spin_unlock(&context->lock, key);
94+
95+
return 0;
96+
}
97+
98+
if ((flags & GPIO_OUTPUT) != 0U) {
99+
if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
100+
pio->PIO_CODR = mask;
101+
}
102+
if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
103+
pio->PIO_SODR = mask;
104+
}
105+
106+
conf &= ~PIO_S_PIO_CFGR_DIR_Msk;
107+
conf |= PIO_S_PIO_CFGR_DIR(PIO_S_PIO_CFGR_DIR_OUTPUT_Val);
108+
} else { /* GPIO_INPUT */
109+
conf &= ~PIO_S_PIO_CFGR_DIR_Msk;
110+
conf |= PIO_S_PIO_CFGR_DIR(PIO_S_PIO_CFGR_DIR_INPUT_Val);
111+
}
112+
113+
/* Disable pull-up and pull-down by default */
114+
conf &= ~(PIO_CFGR_PUEN_Msk | PIO_CFGR_PDEN_Msk);
115+
if (((flags & GPIO_PULL_UP) != 0U) && ((flags & GPIO_PULL_DOWN) != 0U)) {
116+
return -ENOTSUP;
117+
}
118+
if ((flags & GPIO_PULL_UP) != 0U) {
119+
/* Enable pull-up. */
120+
conf |= PIO_CFGR_PUEN(PIO_CFGR_PUEN_ENABLED_Val);
121+
}
122+
if ((flags & GPIO_PULL_DOWN) != 0U) {
123+
/* Enable pull-down. */
124+
conf |= PIO_CFGR_PDEN(PIO_CFGR_PDEN_ENABLED_Val);
125+
}
126+
127+
/* Processing SAM PIO4 specific flags */
128+
if ((flags & SAM_GPIO_DIS_SLEWRATE) != 0U) {
129+
/* Disable slew rate control. */
130+
conf &= ~PIO_CFGR_SR_Msk;
131+
} else {
132+
/* Enable slew rate control by default. */
133+
conf |= PIO_CFGR_SR(PIO_CFGR_SR_ENABLED_Val);
134+
}
135+
if ((flags & SAM_GPIO_DEBOUNCE) != 0U) {
136+
conf |= PIO_CFGR_IFEN(PIO_CFGR_IFEN_ENABLED_Val);
137+
conf |= PIO_CFGR_IFSCEN(PIO_CFGR_IFSCEN_ENABLED_Val);
138+
} else {
139+
conf &= ~PIO_CFGR_IFEN_Msk;
140+
conf &= ~PIO_CFGR_IFSCEN_Msk;
141+
}
142+
if ((flags & SAM_GPIO_DIS_SCHMIT) != 0U) {
143+
/* Disable schmitt trigger */
144+
conf |= PIO_CFGR_SCHMITT(PIO_CFGR_SCHMITT_DISABLED_Val);
145+
}
146+
if ((flags & SAM_GPIO_DRVSTR_MASK) != 0U) {
147+
conf &= ~PIO_CFGR_DRVSTR_Msk;
148+
conf |= PIO_CFGR_DRVSTR((flags & SAM_GPIO_DRVSTR_MASK)
149+
>> SAM_GPIO_DRVSTR_POS);
150+
} else {
151+
/* Use default drive strength */
152+
conf &= ~PIO_CFGR_DRVSTR_Msk;
153+
}
154+
155+
/* Enable the PIO to control the pin (instead of a peripheral). */
156+
conf &= ~PIO_S_PIO_CFGR_FUNC_Msk;
157+
conf |= PIO_S_PIO_CFGR_FUNC(PIO_S_PIO_CFGR_FUNC_GPIO_Val);
158+
159+
/* Update PIO configuration */
160+
key = k_spin_lock(&context->lock);
161+
pio->PIO_MSKR = mask;
162+
pio->PIO_CFGR = conf;
163+
k_spin_unlock(&context->lock, key);
164+
165+
return 0;
166+
}
167+
168+
static int gpio_sam_port_get_raw(const struct device *dev, uint32_t *value)
169+
{
170+
const struct gpio_sam_config * const cfg = dev->config;
171+
pio_group_registers_t * const pio = cfg->regs;
172+
173+
*value = pio->PIO_PDSR;
174+
175+
return 0;
176+
}
177+
178+
static int gpio_sam_port_set_masked_raw(const struct device *dev,
179+
uint32_t mask,
180+
uint32_t value)
181+
{
182+
const struct gpio_sam_config * const cfg = dev->config;
183+
pio_group_registers_t * const pio = cfg->regs;
184+
185+
pio->PIO_ODSR = (pio->PIO_ODSR & ~mask) | (mask & value);
186+
187+
return 0;
188+
}
189+
190+
static int gpio_sam_port_set_bits_raw(const struct device *dev, uint32_t mask)
191+
{
192+
const struct gpio_sam_config * const cfg = dev->config;
193+
pio_group_registers_t * const pio = cfg->regs;
194+
195+
pio->PIO_SODR = mask;
196+
197+
return 0;
198+
}
199+
200+
static int gpio_sam_port_clear_bits_raw(const struct device *dev,
201+
uint32_t mask)
202+
{
203+
const struct gpio_sam_config * const cfg = dev->config;
204+
pio_group_registers_t * const pio = cfg->regs;
205+
206+
pio->PIO_CODR = mask;
207+
208+
return 0;
209+
}
210+
211+
static int gpio_sam_port_toggle_bits(const struct device *dev, uint32_t mask)
212+
{
213+
const struct gpio_sam_config * const cfg = dev->config;
214+
pio_group_registers_t * const pio = cfg->regs;
215+
216+
pio->PIO_ODSR ^= mask;
217+
218+
return 0;
219+
}
220+
221+
static int gpio_sam_pin_interrupt_configure(const struct device *dev,
222+
gpio_pin_t pin,
223+
enum gpio_int_mode mode,
224+
enum gpio_int_trig trig)
225+
{
226+
const struct gpio_sam_config * const cfg = dev->config;
227+
struct gpio_sam_runtime *context = dev->data;
228+
pio_group_registers_t * const pio = cfg->regs;
229+
k_spinlock_key_t key;
230+
const uint32_t mask = BIT(pin);
231+
uint32_t conf;
232+
233+
/* Check if pin number is out of range */
234+
if ((mask & cfg->common.port_pin_mask) == 0U) {
235+
return -ENOTSUP;
236+
}
237+
238+
/* Disable the interrupt. */
239+
pio->PIO_IDR = mask;
240+
241+
if (mode == GPIO_INT_MODE_DISABLED) {
242+
return 0;
243+
}
244+
245+
/* Get PIO configuration */
246+
key = k_spin_lock(&context->lock);
247+
pio->PIO_MSKR = mask;
248+
conf = pio->PIO_CFGR;
249+
k_spin_unlock(&context->lock, key);
250+
251+
conf &= ~PIO_S_PIO_CFGR_EVTSEL_Msk;
252+
253+
if (mode == GPIO_INT_MODE_LEVEL) {
254+
if (trig == GPIO_INT_TRIG_LOW) {
255+
conf |= PIO_S_PIO_CFGR_EVTSEL(PIO_S_PIO_CFGR_EVTSEL_LOW_Val);
256+
} else if (trig == GPIO_INT_TRIG_HIGH) {
257+
conf |= PIO_S_PIO_CFGR_EVTSEL(PIO_S_PIO_CFGR_EVTSEL_HIGH_Val);
258+
} else {
259+
return -ENOTSUP;
260+
}
261+
} else { /* GPIO_INT_MODE_EDGE */
262+
if (trig == GPIO_INT_TRIG_LOW) {
263+
conf |= PIO_S_PIO_CFGR_EVTSEL(PIO_S_PIO_CFGR_EVTSEL_FALLING_Val);
264+
} else if (trig == GPIO_INT_TRIG_HIGH) {
265+
conf |= PIO_S_PIO_CFGR_EVTSEL(PIO_S_PIO_CFGR_EVTSEL_RISING_Val);
266+
} else if (trig == GPIO_INT_TRIG_BOTH) {
267+
conf |= PIO_S_PIO_CFGR_EVTSEL(PIO_S_PIO_CFGR_EVTSEL_BOTH_Val);
268+
} else {
269+
return -ENOTSUP;
270+
}
271+
}
272+
273+
/* Update PIO configuration */
274+
key = k_spin_lock(&context->lock);
275+
pio->PIO_MSKR = mask;
276+
pio->PIO_CFGR = conf;
277+
k_spin_unlock(&context->lock, key);
278+
279+
/* Clear any pending interrupts */
280+
(void)pio->PIO_ISR;
281+
/* Enable the interrupt. */
282+
pio->PIO_IER = mask;
283+
284+
return 0;
285+
}
286+
287+
static void gpio_sam_isr(const struct device *dev)
288+
{
289+
const struct gpio_sam_config * const cfg = dev->config;
290+
pio_group_registers_t * const pio = cfg->regs;
291+
struct gpio_sam_runtime *context = dev->data;
292+
uint32_t int_stat;
293+
294+
int_stat = pio->PIO_ISR;
295+
296+
gpio_fire_callbacks(&context->cb, dev, int_stat);
297+
}
298+
299+
static int gpio_sam_manage_callback(const struct device *port,
300+
struct gpio_callback *callback,
301+
bool set)
302+
{
303+
struct gpio_sam_runtime *context = port->data;
304+
305+
return gpio_manage_callback(&context->cb, callback, set);
306+
}
307+
308+
static DEVICE_API(gpio, gpio_sam_api) = {
309+
.pin_configure = gpio_sam_config,
310+
.port_get_raw = gpio_sam_port_get_raw,
311+
.port_set_masked_raw = gpio_sam_port_set_masked_raw,
312+
.port_set_bits_raw = gpio_sam_port_set_bits_raw,
313+
.port_clear_bits_raw = gpio_sam_port_clear_bits_raw,
314+
.port_toggle_bits = gpio_sam_port_toggle_bits,
315+
.pin_interrupt_configure = gpio_sam_pin_interrupt_configure,
316+
.manage_callback = gpio_sam_manage_callback,
317+
};
318+
319+
int gpio_sam_init(const struct device *dev)
320+
{
321+
const struct gpio_sam_config * const cfg = dev->config;
322+
323+
/* Enable GPIO clock in PMC. This is necessary to enable interrupts */
324+
(void)clock_control_on(SAM_DT_PMC_CONTROLLER,
325+
(clock_control_subsys_t)&cfg->clock_cfg);
326+
327+
cfg->config_func(dev);
328+
329+
return 0;
330+
}
331+
332+
#define GPIO_SAM_INIT(n) \
333+
static void port_##n##_sam_config_func(const struct device *dev); \
334+
\
335+
static const struct gpio_sam_config port_##n##_sam_config = { \
336+
.common = { \
337+
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
338+
}, \
339+
.regs = (pio_group_registers_t *)DT_INST_REG_ADDR(n), \
340+
.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(n), \
341+
.config_func = port_##n##_sam_config_func, \
342+
}; \
343+
\
344+
static struct gpio_sam_runtime port_##n##_sam_runtime; \
345+
\
346+
DEVICE_DT_INST_DEFINE(n, gpio_sam_init, NULL, \
347+
&port_##n##_sam_runtime, \
348+
&port_##n##_sam_config, PRE_KERNEL_1, \
349+
CONFIG_GPIO_INIT_PRIORITY, \
350+
&gpio_sam_api); \
351+
\
352+
static void port_##n##_sam_config_func(const struct device *dev) \
353+
{ \
354+
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
355+
gpio_sam_isr, \
356+
DEVICE_DT_INST_GET(n), 0); \
357+
irq_enable(DT_INST_IRQN(n)); \
358+
}
359+
360+
DT_INST_FOREACH_STATUS_OKAY(GPIO_SAM_INIT)

0 commit comments

Comments
 (0)