forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpinctrl_mchp_xec.c
191 lines (160 loc) · 5.56 KB
/
pinctrl_mchp_xec.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
* Copyright (c) 2016 Open-RnD Sp. z o.o.
* Copyright (c) 2021 Linaro Limited
* Copyright (c) 2021 Nordic Semiconductor ASA
* Copyright (c) 2021 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_pinctrl
#include <zephyr/drivers/pinctrl.h>
#include <soc.h>
/*
* Microchip XEC: each GPIO pin has two 32-bit control register.
* The first 32-bit register contains all pin features except
* slew rate and driver strength in the second control register.
* We compute the register index from the beginning of the GPIO
* control address space which is the same range of the PINCTRL
* parent node. A zero value in the PINCTRL pinmux field means
* do not touch.
*/
static void config_drive_slew(struct gpio_regs * const regs, uint32_t idx, uint32_t conf)
{
uint32_t slew = (conf >> MCHP_XEC_SLEW_RATE_POS) & MCHP_XEC_SLEW_RATE_MSK0;
uint32_t drvstr = (conf >> MCHP_XEC_DRV_STR_POS) & MCHP_XEC_DRV_STR_MSK0;
uint32_t msk = 0, val = 0;
if (slew) {
msk |= MCHP_GPIO_CTRL2_SLEW_MASK;
/* slow slew value is 0 */
if (slew == MCHP_XEC_SLEW_RATE_FAST0) {
val |= MCHP_GPIO_CTRL2_SLEW_FAST;
}
}
if (drvstr) {
msk |= MCHP_GPIO_CTRL2_DRV_STR_MASK;
/* drive strength values are 0 based */
val |= ((drvstr - 1u) << MCHP_GPIO_CTRL2_DRV_STR_POS);
}
if (!msk) {
return;
}
regs->CTRL2[idx] = (regs->CTRL2[idx] & ~msk) | (val & msk);
}
/*
* Internal pulls feature:
* None, weak pull-up, weak pull-down, or repeater mode (both pulls enabled).
* We do not touch this field unless one or more of the DT booleans are set.
* If the no-bias boolean is set then disable internal pulls.
* If pull up and/or down is set enable the respective pull or both for what
* MCHP calls repeater(keeper) mode.
*/
static uint32_t prog_pud(uint32_t pcr1, uint32_t conf)
{
if (conf & BIT(MCHP_XEC_NO_PUD_POS)) {
pcr1 &= ~(MCHP_GPIO_CTRL_PUD_MASK);
pcr1 |= MCHP_GPIO_CTRL_PUD_NONE;
return pcr1;
}
if (conf & (BIT(MCHP_XEC_PU_POS) | BIT(MCHP_XEC_PD_POS))) {
pcr1 &= ~(MCHP_GPIO_CTRL_PUD_MASK);
if (conf & BIT(MCHP_XEC_PU_POS)) {
pcr1 |= MCHP_GPIO_CTRL_PUD_PU;
}
if (conf & BIT(MCHP_XEC_PD_POS)) {
pcr1 |= MCHP_GPIO_CTRL_PUD_PD;
}
}
return pcr1;
}
/*
* DT enable booleans take precedence over disable booleans.
* We initially clear alternate output disable allowing us to set output state
* in the control register. Hardware sets output state bit in both control and
* parallel output register bits. Alternate output disable only controls which
* register bit is writable by the EC. We also clear the input pad disable
* bit because we need the input pin state and we don't know if the requested
* alternate function is input or bi-directional.
* Note 1: hardware allows input and output to be simultaneously enabled.
* Note 2: hardware interrupt detection is only on the input path.
*/
static int xec_config_pin(uint32_t portpin, uint32_t conf, uint32_t altf)
{
struct gpio_regs * const regs = (struct gpio_regs * const)DT_INST_REG_ADDR(0);
uint32_t port = MCHP_XEC_PINMUX_PORT(portpin);
uint32_t pin = (uint32_t)MCHP_XEC_PINMUX_PIN(portpin);
uint32_t idx = 0u, pcr1 = 0u;
if (port >= NUM_MCHP_GPIO_PORTS) {
return -EINVAL;
}
/* MCHP XEC family is 32 pins per port */
idx = (port * 32U) + pin;
config_drive_slew(regs, idx, conf);
/* Clear alternate output disable and input pad disable */
regs->CTRL[idx] &= ~(BIT(MCHP_GPIO_CTRL_AOD_POS) | BIT(MCHP_GPIO_CTRL_INPAD_DIS_POS));
pcr1 = regs->CTRL[idx]; /* current configuration including pin input state */
pcr1 = regs->CTRL[idx]; /* read multiple times to allow propagation from pad */
pcr1 = regs->CTRL[idx]; /* Is this necessary? */
pcr1 = prog_pud(pcr1, conf);
/* Touch output enable. We always enable input */
if (conf & BIT(MCHP_XEC_OUT_DIS_POS)) {
pcr1 &= ~(MCHP_GPIO_CTRL_DIR_OUTPUT);
}
if (conf & BIT(MCHP_XEC_OUT_EN_POS)) {
pcr1 |= MCHP_GPIO_CTRL_DIR_OUTPUT;
}
/* Touch output state? Bit can be set even if the direction is input only */
if (conf & BIT(MCHP_XEC_OUT_LO_POS)) {
pcr1 &= ~BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
}
if (conf & BIT(MCHP_XEC_OUT_HI_POS)) {
pcr1 |= BIT(MCHP_GPIO_CTRL_OUTVAL_POS);
}
/* Touch output buffer type? */
if (conf & BIT(MCHP_XEC_PUSH_PULL_POS)) {
pcr1 &= ~(MCHP_GPIO_CTRL_BUFT_OPENDRAIN);
}
if (conf & BIT(MCHP_XEC_OPEN_DRAIN_POS)) {
pcr1 |= MCHP_GPIO_CTRL_BUFT_OPENDRAIN;
}
/* Always touch power gate */
pcr1 &= ~MCHP_GPIO_CTRL_PWRG_MASK;
if (conf & BIT(MCHP_XEC_PIN_LOW_POWER_POS)) {
pcr1 |= MCHP_GPIO_CTRL_PWRG_OFF;
} else {
pcr1 |= MCHP_GPIO_CTRL_PWRG_VTR_IO;
}
/* Always touch MUX (alternate function) */
pcr1 &= ~MCHP_GPIO_CTRL_MUX_MASK;
pcr1 |= (uint32_t)((altf & MCHP_GPIO_CTRL_MUX_MASK0) << MCHP_GPIO_CTRL_MUX_POS);
/* Always touch invert of alternate function. Need another bit to avoid touching */
if (conf & BIT(MCHP_XEC_FUNC_INV_POS)) {
pcr1 |= BIT(MCHP_GPIO_CTRL_POL_POS);
} else {
pcr1 &= ~BIT(MCHP_GPIO_CTRL_POL_POS);
}
/* output state set in control & parallel regs */
regs->CTRL[idx] = pcr1;
/* make output state in control read-only in control and read-write in parallel reg */
regs->CTRL[idx] = pcr1 | BIT(MCHP_GPIO_CTRL_AOD_POS);
return 0;
}
int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
uintptr_t reg)
{
uint32_t portpin, pinmux, func;
int ret;
ARG_UNUSED(reg);
for (uint8_t i = 0U; i < pin_cnt; i++) {
pinmux = pins[i];
func = MCHP_XEC_PINMUX_FUNC(pinmux);
if (func >= MCHP_AFMAX) {
return -EINVAL;
}
portpin = MEC_XEC_PINMUX_PORT_PIN(pinmux);
ret = xec_config_pin(portpin, pinmux, func);
if (ret < 0) {
return ret;
}
}
return 0;
}