Skip to content

Commit 79ee5a8

Browse files
Manimaran-Acfriedt
authored andcommitted
drivers: gpio: Microchip MEC172x GPIO driver glitch fix
A glitch was observed if a GPIO PIN was configured to a non-default state by ROM and then Zephyr programs the pin for the same configuration. Root cause is GPIO hardware implementing two output bits for each pin. The alternate output bit is in the pin control register and is r/w by default. The other bit exists in the GPIO parallel ouput register and is read-only by default. The hardware actually reflects the pin's output value into both bits. The fix is to configure the pin with alternate output bit read-write and the last step is to disable alternate output which enabled read-write of the parallel bit. GPIO API's can then use the GPIO parallel out registers. Add logic to return an error from the GPIO interrupt configure API if a pin is not configured as an input. Hardware only performs interrupt detection if the input pad is enabled. Hardware supports a pin being configured for both input and output. Applications should add the GPIO_INPUT flag to all pin configuration requiring interrupt detection. The interpretation of input and output flags for the get configuration API appears to be only one of the flags can be set. Please refer to the GPIO driver tests. Updated GPIO interrupt configure to clear the input pad disable bit due to interrupt detection HW is connected only to input side of pin. Signed-off-by: Manimaran A <[email protected]>
1 parent 7039f8c commit 79ee5a8

File tree

2 files changed

+220
-76
lines changed

2 files changed

+220
-76
lines changed

drivers/gpio/gpio_mchp_xec_v2.c

Lines changed: 185 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <zephyr/arch/cpu.h>
1111
#include <zephyr/device.h>
1212
#include <zephyr/drivers/gpio.h>
13+
#include <zephyr/dt-bindings/gpio/gpio.h>
1314
#include <zephyr/dt-bindings/pinctrl/mchp-xec-pinctrl.h>
1415
#include <soc.h>
1516
#include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h>
@@ -82,102 +83,120 @@ static inline void xec_mask_write32(uintptr_t addr, uint32_t mask, uint32_t val)
8283
}
8384

8485
/*
85-
* notes: The GPIO parallel output bits are read-only until the
86-
* Alternate-Output-Disable (AOD) bit is set in the pin's control
87-
* register. To preload a parallel output value to prevent certain
88-
* classes of glitching for output pins we must:
89-
* Set GPIO control AOD=1 with the pin direction set to input.
90-
* Program the new pin value in the respective GPIO parallel output
91-
* register.
92-
* Program other GPIO control bits except direction.
93-
* Last step set the GPIO control register direction bit to output.
86+
* NOTE: gpio_flags_t b[15:0] are defined in the dt-binding gpio header.
87+
* b[31:16] are defined in the driver gpio header.
88+
* Hardware only supports push-pull or open-drain.
9489
*/
95-
static int gpio_xec_configure(const struct device *dev,
96-
gpio_pin_t pin, gpio_flags_t flags)
90+
static int gpio_xec_validate_flags(gpio_flags_t flags)
9791
{
98-
const struct gpio_xec_config *config = dev->config;
99-
uintptr_t pcr1_addr = pin_ctrl_addr(dev, pin);
100-
uintptr_t pout_addr = pin_parout_addr(dev);
101-
uint32_t pcr1 = 0U;
102-
uint32_t mask = 0U;
103-
104-
/* Validate pin number range in terms of current port */
105-
if ((valid_ctrl_masks[config->port_num] & BIT(pin)) == 0U) {
106-
return -EINVAL;
92+
if ((flags & (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN))
93+
== (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE)) {
94+
return -ENOTSUP;
10795
}
10896

109-
/* Don't support "open source" mode */
110-
if (((flags & GPIO_SINGLE_ENDED) != 0U) &&
111-
((flags & GPIO_LINE_OPEN_DRAIN) == 0U)) {
112-
return -ENOTSUP;
97+
if ((flags & GPIO_OUTPUT_INIT_LOW) && (flags & GPIO_OUTPUT_INIT_HIGH)) {
98+
return -EINVAL;
11399
}
114100

115-
/* The flags contain options that require touching registers in the
116-
* PCRs for a given GPIO. There are no GPIO modules in Microchip SOCs!
117-
* Keep direction as input until last.
118-
* Clear input pad disable allowing input pad to operate.
119-
* Clear Power gate to allow pads to operate.
120-
*/
121-
mask |= MCHP_GPIO_CTRL_DIR_MASK;
122-
mask |= MCHP_GPIO_CTRL_INPAD_DIS_MASK;
123-
mask |= MCHP_GPIO_CTRL_PWRG_MASK;
124-
pcr1 |= MCHP_GPIO_CTRL_DIR_INPUT;
101+
return 0;
102+
}
125103

126-
/* Figure out the pullup/pulldown configuration and keep it in the
127-
* pcr1 variable
128-
*/
129-
mask |= MCHP_GPIO_CTRL_PUD_MASK;
130-
131-
if ((flags & GPIO_PULL_UP) != 0U) {
132-
/* Enable the pull and select the pullup resistor. */
133-
pcr1 |= MCHP_GPIO_CTRL_PUD_PU;
134-
} else if ((flags & GPIO_PULL_DOWN) != 0U) {
135-
/* Enable the pull and select the pulldown resistor */
136-
pcr1 |= MCHP_GPIO_CTRL_PUD_PD;
104+
/*
105+
* Each GPIO pin has two 32-bit control registers. Control 1 configures pin
106+
* features except for drive strength and slew rate in Control 2.
107+
* A pin's input and output state can be read/written from either the Control 1
108+
* register or from corresponding bits in the GPIO parallel input/output registers.
109+
* The parallel input and output registers group 32 pins into each register.
110+
* The GPIO hardware restricts the pin output state to Control 1 or the parallel bit.
111+
* Both output bits reflect each other on read and writes but only one is writable
112+
* selected by the output control select bit in Control 1. In the configuration API
113+
* we use Control 1 to configure all pin features and output state. Before exiting,
114+
* we set the output select for parallel mode enabling writes to the parallel output bit.
115+
*/
116+
static int gpio_xec_configure(const struct device *dev,
117+
gpio_pin_t pin, gpio_flags_t flags)
118+
{
119+
const struct gpio_xec_config *config = dev->config;
120+
uintptr_t pcr1_addr = 0u;
121+
uint32_t pcr1 = 0u, pcr1_new = 0u;
122+
uint32_t msk = (MCHP_GPIO_CTRL_PWRG_MASK
123+
| MCHP_GPIO_CTRL_BUFT_MASK | MCHP_GPIO_CTRL_DIR_MASK
124+
| MCHP_GPIO_CTRL_AOD_MASK | BIT(MCHP_GPIO_CTRL_POL_POS)
125+
| MCHP_GPIO_CTRL_MUX_MASK | MCHP_GPIO_CTRL_INPAD_DIS_MASK);
126+
127+
if (!(valid_ctrl_masks[config->port_num] & BIT(pin))) {
128+
return -EINVAL;
137129
}
138130

139-
/* Push-pull or open drain */
140-
mask |= MCHP_GPIO_CTRL_BUFT_MASK;
131+
int ret = gpio_xec_validate_flags(flags);
141132

142-
if ((flags & GPIO_OPEN_DRAIN) != 0U) {
143-
/* Open drain */
144-
pcr1 |= MCHP_GPIO_CTRL_BUFT_OPENDRAIN;
145-
} else {
146-
/* Push-pull */
147-
pcr1 |= MCHP_GPIO_CTRL_BUFT_PUSHPULL;
133+
if (ret) {
134+
return ret;
148135
}
149136

150-
/* Use GPIO output register to control pin output, instead of
151-
* using the control register (=> alternate output disable).
152-
*/
153-
mask |= MCHP_GPIO_CTRL_AOD_MASK;
154-
pcr1 |= MCHP_GPIO_CTRL_AOD_DIS;
137+
pcr1_addr = pin_ctrl_addr(dev, pin);
138+
pcr1 = sys_read32(pcr1_addr);
155139

156-
/* Make sure disconnected on first control register write */
157140
if (flags == GPIO_DISCONNECTED) {
158-
pcr1 |= MCHP_GPIO_CTRL_PWRG_OFF;
141+
pcr1 = (pcr1 & ~MCHP_GPIO_CTRL_PWRG_MASK) | MCHP_GPIO_CTRL_PWRG_OFF;
142+
sys_write32(pcr1, pcr1_addr);
143+
return 0;
159144
}
160145

161-
/* Now write contents of pcr1 variable to the PCR1 register that
162-
* corresponds to the GPIO being configured.
163-
* AOD is 1 and direction is input. HW will allow use to set the
164-
* GPIO parallel output bit for this pin and with the pin direction
165-
* as input no glitch will occur.
166-
*/
167-
xec_mask_write32(pcr1_addr, mask, pcr1);
146+
/* final pin state will be powered */
147+
pcr1_new = MCHP_GPIO_CTRL_PWRG_VTR_IO;
168148

169-
if ((flags & GPIO_OUTPUT) != 0U) {
170-
if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
171-
sys_set_bit(pout_addr, pin);
172-
} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
173-
sys_clear_bit(pout_addr, pin);
149+
/* always enable input pad */
150+
if (pcr1 & BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS)) {
151+
pcr1 &= ~BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS);
152+
sys_write32(pcr1, pcr1_addr);
153+
}
154+
155+
if (flags & GPIO_OUTPUT) {
156+
pcr1_new |= BIT(MCHP_GPIO_CTRL_DIR_POS);
157+
msk |= BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
158+
if (flags & GPIO_OUTPUT_INIT_HIGH) {
159+
pcr1_new |= BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
160+
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
161+
pcr1_new &= ~BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
162+
} else { /* copy current input state to output state */
163+
if ((pcr1 & MCHP_GPIO_CTRL_PWRG_MASK) == MCHP_GPIO_CTRL_PWRG_OFF) {
164+
pcr1 &= ~(MCHP_GPIO_CTRL_PWRG_MASK);
165+
pcr1 |= MCHP_GPIO_CTRL_PWRG_VTR_IO;
166+
sys_write32(pcr1, pcr1_addr);
167+
}
168+
pcr1 = sys_read32(pcr1_addr);
169+
if (pcr1 & BIT(MCHP_GPIO_CTRL_INPAD_VAL_POS)) {
170+
pcr1_new |= BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
171+
} else {
172+
pcr1_new &= ~BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
173+
}
174174
}
175+
if (flags & GPIO_LINE_OPEN_DRAIN) {
176+
pcr1_new |= BIT(MCHP_GPIO_CTRL_BUFT_POS);
177+
}
178+
}
175179

176-
mask = MCHP_GPIO_CTRL_DIR_MASK;
177-
pcr1 = MCHP_GPIO_CTRL_DIR_OUTPUT;
178-
xec_mask_write32(pcr1_addr, mask, pcr1);
180+
if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) {
181+
msk |= MCHP_GPIO_CTRL_PUD_MASK;
182+
/* both bits specifies repeater mode */
183+
if (flags & GPIO_PULL_UP) {
184+
pcr1_new |= MCHP_GPIO_CTRL_PUD_PU;
185+
}
186+
if (flags & GPIO_PULL_DOWN) {
187+
pcr1_new |= MCHP_GPIO_CTRL_PUD_PD;
188+
}
179189
}
180190

191+
/*
192+
* Problem, if pin was power gated off we can't read input.
193+
* How to turn on pin to read input but not glitch it?
194+
*/
195+
pcr1 = (pcr1 & ~msk) | (pcr1_new & msk);
196+
sys_write32(pcr1, pcr1_addr); /* configuration. may generate a single edge */
197+
/* Control output bit becomes read-only and parallel out register bit becomes r/w */
198+
sys_write32(pcr1 | BIT(MCHP_GPIO_CTRL_AOD_POS), pcr1_addr);
199+
181200
return 0;
182201
}
183202

@@ -257,6 +276,8 @@ static int gpio_xec_pin_interrupt_configure(const struct device *dev,
257276

258277
/* pin configuration matches requested detection mode? */
259278
pcr1 = sys_read32(pcr1_addr);
279+
/* HW detects interrupt on input. Make sure input pad disable is cleared */
280+
pcr1 &= ~BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS);
260281

261282
if ((pcr1 & MCHP_GPIO_CTRL_IDET_MASK) == pcr1_req) {
262283
gpio_xec_intr_en(pin, mode, config->girq_id);
@@ -354,13 +375,95 @@ static int gpio_xec_manage_callback(const struct device *dev,
354375
return 0;
355376
}
356377

378+
#ifdef CONFIG_GPIO_GET_DIRECTION
379+
static int gpio_xec_get_direction(const struct device *port, gpio_port_pins_t map,
380+
gpio_port_pins_t *inputs, gpio_port_pins_t *outputs)
381+
{
382+
if (!port) {
383+
return -EINVAL;
384+
}
385+
386+
const struct gpio_xec_config *config = port->config;
387+
uint32_t valid_msk = valid_ctrl_masks[config->port_num];
388+
389+
*inputs = 0u;
390+
*outputs = 0u;
391+
for (uint8_t pin = 0; pin < 32; pin++) {
392+
if (!map) {
393+
break;
394+
}
395+
if ((map & BIT(pin)) && (valid_msk & BIT(pin))) {
396+
uintptr_t pcr1_addr = pin_ctrl_addr(port, pin);
397+
uint32_t pcr1 = sys_read32(pcr1_addr);
398+
399+
if (!((pcr1 & MCHP_GPIO_CTRL_PWRG_MASK) == MCHP_GPIO_CTRL_PWRG_OFF)) {
400+
if (outputs && (pcr1 & BIT(MCHP_GPIO_CTRL_DIR_POS))) {
401+
*outputs |= BIT(pin);
402+
} else if (inputs && !(pcr1 & BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS))) {
403+
*inputs |= BIT(pin);
404+
}
405+
}
406+
407+
map &= ~BIT(pin);
408+
}
409+
}
410+
411+
return 0;
412+
}
413+
#endif
414+
415+
#ifdef CONFIG_GPIO_GET_CONFIG
416+
int gpio_xec_get_config(const struct device *port, gpio_pin_t pin, gpio_flags_t *flags)
417+
{
418+
if (!port || !flags) {
419+
return -EINVAL;
420+
}
421+
422+
const struct gpio_xec_config *config = port->config;
423+
uint32_t valid_msk = valid_ctrl_masks[config->port_num];
424+
425+
if (!(valid_msk & BIT(pin))) {
426+
return -EINVAL;
427+
/* Or should we set *flags = GPIO_DISCONNECTED and return success? */
428+
}
429+
430+
uintptr_t pcr1_addr = pin_ctrl_addr(port, pin);
431+
uint32_t pcr1 = sys_read32(pcr1_addr);
432+
uint32_t pin_flags = 0u;
433+
434+
if (pcr1 & BIT(MCHP_GPIO_CTRL_DIR_POS)) {
435+
pin_flags |= GPIO_OUTPUT;
436+
if (pcr1 & BIT(MCHP_GPIO_CTRL_OUTVAL_POS)) {
437+
pin_flags |= GPIO_OUTPUT_INIT_HIGH;
438+
} else {
439+
pin_flags |= GPIO_OUTPUT_INIT_LOW;
440+
}
441+
442+
if (pcr1 & BIT(MCHP_GPIO_CTRL_BUFT_POS)) {
443+
pin_flags |= GPIO_OPEN_DRAIN;
444+
}
445+
} else if (!(pcr1 & BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS))) {
446+
pin_flags |= GPIO_INPUT;
447+
}
448+
449+
if (pin_flags) {
450+
*flags = pin_flags;
451+
} else {
452+
*flags = GPIO_DISCONNECTED;
453+
}
454+
455+
return 0;
456+
}
457+
#endif
458+
357459
static void gpio_gpio_xec_port_isr(const struct device *dev)
358460
{
359461
const struct gpio_xec_config *config = dev->config;
360462
struct gpio_xec_data *data = dev->data;
361463
uint32_t girq_result;
362464

363-
/* Figure out which interrupts have been triggered from the EC
465+
/*
466+
* Figure out which interrupts have been triggered from the EC
364467
* aggregator result register
365468
*/
366469
girq_result = mchp_soc_ecia_girq_result(config->girq_id);
@@ -381,6 +484,12 @@ static const struct gpio_driver_api gpio_xec_driver_api = {
381484
.port_toggle_bits = gpio_xec_port_toggle_bits,
382485
.pin_interrupt_configure = gpio_xec_pin_interrupt_configure,
383486
.manage_callback = gpio_xec_manage_callback,
487+
#ifdef CONFIG_GPIO_GET_DIRECTION
488+
.port_get_direction = gpio_xec_get_direction,
489+
#endif
490+
#ifdef CONFIG_GPIO_GET_CONFIG
491+
.pin_get_config = gpio_xec_get_config,
492+
#endif
384493
};
385494

386495
#define XEC_GPIO_PORT_FLAGS(n) \
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2023 Microchip Technology Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/dt-bindings/gpio/gpio.h>
8+
9+
/ {
10+
zephyr,user {
11+
output-high-gpios = <&gpio_000_036 13 GPIO_ACTIVE_LOW>;
12+
output-low-gpios = <&gpio_000_036 14 GPIO_ACTIVE_HIGH>;
13+
input-gpios = <&gpio_000_036 11 GPIO_ACTIVE_LOW>;
14+
};
15+
};
16+
17+
&gpio_000_036 {
18+
hog1 {
19+
gpio-hog;
20+
gpios = <13 GPIO_ACTIVE_LOW>;
21+
output-high;
22+
};
23+
24+
hog2 {
25+
gpio-hog;
26+
gpios = <14 GPIO_ACTIVE_HIGH>;
27+
output-low;
28+
};
29+
30+
hog3 {
31+
gpio-hog;
32+
gpios = <11 GPIO_ACTIVE_LOW>;
33+
input;
34+
};
35+
};

0 commit comments

Comments
 (0)