Skip to content

Commit f8c8ee6

Browse files
Manimaran-Acfriedt
authored andcommitted
drivers: pinctrl: Microchip XEC PINCTRL glitch fix
Glitches were observed if a GPIO pin was configured by ROM to a non-default state and then Zephyr PINCTRL reconfigured the pin. The fix involves using the correct PINCTRL YAML output enable and state flags. Reading the current spin state and reflecting into new pin configuration if the pin is output and the drive low/high properties are not present. We also take advantage of GPIO hardware reflecing the alternate output value in the parallel output bit before enabling parallel output mode. Interpret boolean flags with both enable and disable as do not touch if neither flag is present. We give precedence to enable over disable if both flags mistakenly appear. Note, PINCTRL always clears the GPIO control input pad disable bit. Signed-off-by: Manimaran A <[email protected]>
1 parent 79ee5a8 commit f8c8ee6

File tree

7 files changed

+175
-167
lines changed

7 files changed

+175
-167
lines changed

boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,13 @@
148148

149149
&i2c00_scl_gpio004 {
150150
drive-open-drain;
151+
output-enable;
151152
output-high;
152153
};
153154

154155
&i2c00_sda_gpio003 {
155156
drive-open-drain;
157+
output-enable;
156158
output-high;
157159
};
158160

@@ -181,11 +183,13 @@
181183

182184
&i2c01_scl_gpio131 {
183185
drive-open-drain;
186+
output-enable;
184187
output-high;
185188
};
186189

187190
&i2c01_sda_gpio130 {
188191
drive-open-drain;
192+
output-enable;
189193
output-high;
190194
};
191195

@@ -198,11 +202,13 @@
198202

199203
&i2c07_scl_gpio013 {
200204
drive-open-drain;
205+
output-enable;
201206
output-high;
202207
};
203208

204209
&i2c07_sda_gpio012 {
205210
drive-open-drain;
211+
output-enable;
206212
output-high;
207213
};
208214

boards/arm/mec172xmodular_assy6930/mec172xmodular_assy6930.dts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,13 @@
158158

159159
&i2c00_scl_gpio004 {
160160
drive-open-drain;
161+
output-enable;
161162
output-high;
162163
};
163164

164165
&i2c00_sda_gpio003 {
165166
drive-open-drain;
167+
output-enable;
166168
output-high;
167169
};
168170

@@ -175,11 +177,13 @@
175177

176178
&i2c01_scl_gpio131 {
177179
drive-open-drain;
180+
output-enable;
178181
output-high;
179182
};
180183

181184
&i2c01_sda_gpio130 {
182185
drive-open-drain;
186+
output-enable;
183187
output-high;
184188
};
185189

@@ -192,11 +196,13 @@
192196

193197
&i2c07_scl_gpio013 {
194198
drive-open-drain;
199+
output-enable;
195200
output-high;
196201
};
197202

198203
&i2c07_sda_gpio012 {
199204
drive-open-drain;
205+
output-enable;
200206
output-high;
201207
};
202208

drivers/pinctrl/pinctrl_mchp_xec.c

Lines changed: 107 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -12,56 +12,88 @@
1212
#include <zephyr/drivers/pinctrl.h>
1313
#include <soc.h>
1414

15-
/* Microchip XEC: each GPIO pin has two 32-bit control register.
15+
/*
16+
* Microchip XEC: each GPIO pin has two 32-bit control register.
1617
* The first 32-bit register contains all pin features except
1718
* slew rate and driver strength in the second control register.
1819
* We compute the register index from the beginning of the GPIO
1920
* control address space which is the same range of the PINCTRL
20-
* parent node.
21+
* parent node. A zero value in the PINCTRL pinmux field means
22+
* do not touch.
2123
*/
22-
2324
static void config_drive_slew(struct gpio_regs * const regs, uint32_t idx, uint32_t conf)
2425
{
25-
uint32_t slew = conf & (MCHP_XEC_OSPEEDR_MASK << MCHP_XEC_OSPEEDR_POS);
26-
uint32_t drvstr = conf & (MCHP_XEC_ODRVSTR_MASK << MCHP_XEC_ODRVSTR_POS);
27-
uint32_t val = 0;
28-
uint32_t mask = 0;
29-
30-
if (slew != MCHP_XEC_OSPEEDR_NO_CHG) {
31-
mask |= MCHP_GPIO_CTRL2_SLEW_MASK;
32-
if (slew == MCHP_XEC_OSPEEDR_FAST) {
26+
uint32_t slew = (conf >> MCHP_XEC_SLEW_RATE_POS) & MCHP_XEC_SLEW_RATE_MSK0;
27+
uint32_t drvstr = (conf >> MCHP_XEC_DRV_STR_POS) & MCHP_XEC_DRV_STR_MSK0;
28+
uint32_t msk = 0, val = 0;
29+
30+
if (slew) {
31+
msk |= MCHP_GPIO_CTRL2_SLEW_MASK;
32+
/* slow slew value is 0 */
33+
if (slew == MCHP_XEC_SLEW_RATE_FAST0) {
3334
val |= MCHP_GPIO_CTRL2_SLEW_FAST;
3435
}
3536
}
36-
if (drvstr != MCHP_XEC_ODRVSTR_NO_CHG) {
37-
mask |= MCHP_GPIO_CTRL2_DRV_STR_MASK;
38-
val |= (drvstr << MCHP_GPIO_CTRL2_DRV_STR_POS);
37+
38+
if (drvstr) {
39+
msk |= MCHP_GPIO_CTRL2_DRV_STR_MASK;
40+
/* drive strength values are 0 based */
41+
val |= ((drvstr - 1u) << MCHP_GPIO_CTRL2_DRV_STR_POS);
3942
}
4043

41-
if (!mask) {
44+
if (!msk) {
4245
return;
4346
}
4447

45-
regs->CTRL2[idx] = (regs->CTRL2[idx] & ~mask) | (val & mask);
48+
regs->CTRL2[idx] = (regs->CTRL2[idx] & ~msk) | (val & msk);
4649
}
4750

48-
/* Configure pin by writing GPIO Control and Control2 registers.
49-
* NOTE: Disable alternate output feature since the GPIO driver does.
50-
* While alternate output is enabled (default state of pin) HW does not
51-
* ignores writes to the parallel output bit for the pin. To set parallel
52-
* output value we must keep pin direction as input, set alternate output
53-
* disable, program pin value to parallel output bit, and then disable
54-
* alternate output mode.
51+
/*
52+
* Internal pulls feature:
53+
* None, weak pull-up, weak pull-down, or repeater mode (both pulls enabled).
54+
* We do not touch this field unless one or more of the DT booleans are set.
55+
* If the no-bias boolean is set then disable internal pulls.
56+
* If pull up and/or down is set enable the respective pull or both for what
57+
* MCHP calls repeater(keeper) mode.
58+
*/
59+
static uint32_t prog_pud(uint32_t pcr1, uint32_t conf)
60+
{
61+
if (conf & BIT(MCHP_XEC_NO_PUD_POS)) {
62+
pcr1 &= ~(MCHP_GPIO_CTRL_PUD_MASK);
63+
pcr1 |= MCHP_GPIO_CTRL_PUD_NONE;
64+
return pcr1;
65+
}
66+
67+
if (conf & (BIT(MCHP_XEC_PU_POS) | BIT(MCHP_XEC_PD_POS))) {
68+
pcr1 &= ~(MCHP_GPIO_CTRL_PUD_MASK);
69+
if (conf & BIT(MCHP_XEC_PU_POS)) {
70+
pcr1 |= MCHP_GPIO_CTRL_PUD_PU;
71+
}
72+
if (conf & BIT(MCHP_XEC_PD_POS)) {
73+
pcr1 |= MCHP_GPIO_CTRL_PUD_PD;
74+
}
75+
}
76+
77+
return pcr1;
78+
}
79+
80+
/*
81+
* DT enable booleans take precedence over disable booleans.
82+
* We initially clear alternate output disable allowing us to set output state
83+
* in the control register. Hardware sets output state bit in both control and
84+
* parallel output register bits. Alternate output disable only controls which
85+
* register bit is writable by the EC. We also clear the input pad disable
86+
* bit because we need the input pin state and we don't know if the requested
87+
* alternate function is input or bi-directional.
88+
* Note 1: hardware allows input and output to be simultaneously enabled.
89+
* Note 2: hardware interrupt detection is only on the input path.
5590
*/
5691
static int xec_config_pin(uint32_t portpin, uint32_t conf, uint32_t altf)
5792
{
5893
struct gpio_regs * const regs = (struct gpio_regs * const)DT_INST_REG_ADDR(0);
5994
uint32_t port = MCHP_XEC_PINMUX_PORT(portpin);
6095
uint32_t pin = (uint32_t)MCHP_XEC_PINMUX_PIN(portpin);
61-
uint32_t msk = MCHP_GPIO_CTRL_AOD_MASK;
62-
uint32_t val = MCHP_GPIO_CTRL_AOD_DIS;
63-
uint32_t idx = 0u;
64-
uint32_t temp = 0u;
96+
uint32_t idx = 0u, pcr1 = 0u;
6597

6698
if (port >= NUM_MCHP_GPIO_PORTS) {
6799
return -EINVAL;
@@ -72,81 +104,84 @@ static int xec_config_pin(uint32_t portpin, uint32_t conf, uint32_t altf)
72104

73105
config_drive_slew(regs, idx, conf);
74106

75-
/* default input pad enabled, buffer type push-pull, no internal pulls,
76-
* and invert polarity normal.
77-
*/
78-
msk |= (BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS) | MCHP_GPIO_CTRL_BUFT_MASK |
79-
MCHP_GPIO_CTRL_PUD_MASK | MCHP_GPIO_CTRL_MUX_MASK
80-
| BIT(MCHP_GPIO_CTRL_POL_POS));
107+
/* Clear alternate output disable and input pad disable */
108+
regs->CTRL[idx] &= ~(BIT(MCHP_GPIO_CTRL_AOD_POS) | BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS));
109+
pcr1 = regs->CTRL[idx]; /* current configuration including pin input state */
110+
pcr1 = regs->CTRL[idx]; /* read multiple times to allow propagation from pad */
111+
pcr1 = regs->CTRL[idx]; /* Is this necessary? */
81112

82-
if (conf & BIT(MCHP_XEC_PIN_LOW_POWER_POS)) {
83-
msk |= MCHP_GPIO_CTRL_PWRG_MASK;
84-
val |= MCHP_GPIO_CTRL_PWRG_OFF;
85-
}
113+
pcr1 = prog_pud(pcr1, conf);
86114

87-
temp = (conf & MCHP_XEC_PUPDR_MASK) >> MCHP_XEC_PUPDR_POS;
88-
switch (temp) {
89-
case MCHP_XEC_PULL_UP:
90-
val |= MCHP_GPIO_CTRL_PUD_PU;
91-
break;
92-
case MCHP_XEC_PULL_DOWN:
93-
val |= MCHP_GPIO_CTRL_PUD_PD;
94-
break;
95-
case MCHP_XEC_REPEATER:
96-
val |= MCHP_GPIO_CTRL_PUD_RPT;
97-
break;
98-
default:
99-
val |= MCHP_GPIO_CTRL_PUD_NONE;
100-
break;
115+
/* Touch output enable. We always enable input */
116+
if (conf & BIT(MCHP_XEC_OUT_DIS_POS)) {
117+
pcr1 &= ~(MCHP_GPIO_CTRL_DIR_OUTPUT);
118+
}
119+
if (conf & BIT(MCHP_XEC_OUT_EN_POS)) {
120+
pcr1 |= MCHP_GPIO_CTRL_DIR_OUTPUT;
101121
}
102122

103-
if ((conf >> MCHP_XEC_OTYPER_POS) & MCHP_XEC_OTYPER_MASK) {
104-
val |= MCHP_GPIO_CTRL_BUFT_OPENDRAIN;
123+
/* Touch output state? Bit can be set even if the direction is input only */
124+
if (conf & BIT(MCHP_XEC_OUT_LO_POS)) {
125+
pcr1 &= ~BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
126+
}
127+
if (conf & BIT(MCHP_XEC_OUT_HI_POS)) {
128+
pcr1 |= BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
105129
}
106130

107-
if (conf & MCHP_XEC_FUNC_INV_MSK) {
108-
val |= BIT(MCHP_GPIO_CTRL_POL_POS);
131+
/* Touch output buffer type? */
132+
if (conf & BIT(MCHP_XEC_PUSH_PULL_POS)) {
133+
pcr1 &= ~(MCHP_GPIO_CTRL_BUFT_OPENDRAIN);
134+
}
135+
if (conf & BIT(MCHP_XEC_OPEN_DRAIN_POS)) {
136+
pcr1 |= MCHP_GPIO_CTRL_BUFT_OPENDRAIN;
109137
}
110138

111-
regs->CTRL[idx] = (regs->CTRL[idx] & ~msk) | val;
139+
/* Always touch power gate */
140+
pcr1 &= ~MCHP_GPIO_CTRL_PWRG_MASK;
141+
if (conf & BIT(MCHP_XEC_PIN_LOW_POWER_POS)) {
142+
pcr1 |= MCHP_GPIO_CTRL_PWRG_OFF;
143+
} else {
144+
pcr1 |= MCHP_GPIO_CTRL_PWRG_VTR_IO;
145+
}
112146

113-
temp = (conf >> MCHP_XEC_OVAL_POS) & MCHP_XEC_OVAL_MASK;
114-
if (temp) {
115-
if (temp == MCHP_XEC_OVAL_DRV_HIGH) {
116-
regs->PAROUT[port] |= BIT(pin);
117-
} else {
118-
regs->PAROUT[port] &= ~BIT(pin);
119-
}
147+
/* Always touch MUX (alternate function) */
148+
pcr1 &= ~MCHP_GPIO_CTRL_MUX_MASK;
149+
pcr1 |= (uint32_t)((altf & MCHP_GPIO_CTRL_MUX_MASK0) << MCHP_GPIO_CTRL_MUX_POS);
120150

121-
regs->CTRL[idx] |= MCHP_GPIO_CTRL_DIR_OUTPUT;
151+
/* Always touch invert of alternate function. Need another bit to avoid touching */
152+
if (conf & BIT(MCHP_XEC_FUNC_INV_POS)) {
153+
pcr1 |= BIT(MCHP_GPIO_CTRL_POL_POS);
154+
} else {
155+
pcr1 &= ~BIT(MCHP_GPIO_CTRL_POL_POS);
122156
}
123157

124-
val = (uint32_t)((altf & MCHP_GPIO_CTRL_MUX_MASK0) << MCHP_GPIO_CTRL_MUX_POS);
125-
regs->CTRL[idx] |= val;
158+
/* output state set in control & parallel regs */
159+
regs->CTRL[idx] = pcr1;
160+
/* make output state in control read-only in control and read-write in parallel reg */
161+
regs->CTRL[idx] = pcr1 | BIT(MCHP_GPIO_CTRL_AOD_POS);
126162

127163
return 0;
128164
}
129165

130166
int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
131167
uintptr_t reg)
132168
{
133-
uint32_t portpin, mux, cfg, func;
169+
uint32_t portpin, pinmux, func;
134170
int ret;
135171

136172
ARG_UNUSED(reg);
137173

138174
for (uint8_t i = 0U; i < pin_cnt; i++) {
139-
mux = pins[i].pinmux;
175+
pinmux = pins[i];
140176

141-
func = MCHP_XEC_PINMUX_FUNC(mux);
177+
func = MCHP_XEC_PINMUX_FUNC(pinmux);
142178
if (func >= MCHP_AFMAX) {
143179
return -EINVAL;
144180
}
145181

146-
cfg = pins[i].pincfg;
147-
portpin = MEC_XEC_PINMUX_PORT_PIN(mux);
182+
portpin = MEC_XEC_PINMUX_PORT_PIN(pinmux);
148183

149-
ret = xec_config_pin(portpin, cfg, func);
184+
ret = xec_config_pin(portpin, pinmux, func);
150185
if (ret < 0) {
151186
return ret;
152187
}

dts/bindings/pinctrl/microchip,xec-pinctrl.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ child-binding:
8383
- drive-push-pull
8484
- drive-open-drain
8585
- low-power-enable
86+
- output-disable
87+
- output-enable
8688
- output-high
8789
- output-low
8890

@@ -94,8 +96,9 @@ child-binding:
9496

9597
slew-rate:
9698
type: string
97-
default: "low-speed"
99+
default: "no-change"
98100
enum:
101+
- "no-change"
99102
- "low-speed"
100103
- "high-speed"
101104
description: |
@@ -106,8 +109,9 @@ child-binding:
106109
107110
drive-strength:
108111
type: string
109-
default: "1x"
112+
default: "no-change"
110113
enum:
114+
- "no-change"
111115
- "1x"
112116
- "2x"
113117
- "4x"

0 commit comments

Comments
 (0)