1 /*
2 * Copyright (c) 2016 Open-RnD Sp. z o.o.
3 * Copyright (c) 2021 Linaro Limited
4 * Copyright (c) 2021 Nordic Semiconductor ASA
5 * Copyright (c) 2024 Microchip Technology Inc.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #define DT_DRV_COMPAT microchip_mec5_pinctrl
11
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/dt-bindings/pinctrl/mchp-xec-pinctrl.h>
14 #include <mec_gpio_api.h>
15
16 static const struct mec_gpio_props cfg1[] = {
17 {MEC_GPIO_OSEL_PROP_ID, MEC_GPIO_PROP_OSEL_CTRL},
18 {MEC_GPIO_INPAD_DIS_PROP_ID, MEC_GPIO_PROP_INPAD_EN},
19 };
20
21 /* DT enable booleans take precedence over disable booleans.
22 * We initially clear alternate output disable allowing us to set output state
23 * in the control register. Hardware sets output state bit in both control and
24 * parallel output register bits. Alternate output disable only controls which
25 * register bit is writable by the EC. We also clear the input pad disable
26 * bit because we need the input pin state and we don't know if the requested
27 * alternate function is input or bi-directional.
28 * Note 1: hardware allows input and output to be simultaneously enabled.
29 * Note 2: hardware interrupt detection is only on the input path.
30 */
mec5_config_pin(uint32_t pinmux,uint32_t altf)31 static int mec5_config_pin(uint32_t pinmux, uint32_t altf)
32 {
33 uint32_t conf = pinmux;
34 uint32_t pin = 0, temp = 0;
35 int ret = 0;
36 size_t idx = 0;
37 struct mec_gpio_props cfg2[12];
38
39 ret = mec_hal_gpio_pin_num(MCHP_XEC_PINMUX_PORT(pinmux), MCHP_XEC_PINMUX_PIN(pinmux), &pin);
40 if (ret) {
41 return -EINVAL;
42 }
43
44 ret = mec_hal_gpio_set_props(pin, cfg1, ARRAY_SIZE(cfg1));
45 if (ret) {
46 return -EIO;
47 }
48
49 /* slew rate */
50 temp = (conf >> MCHP_XEC_SLEW_RATE_POS) & MCHP_XEC_SLEW_RATE_MSK0;
51 if (temp != MCHP_XEC_SLEW_RATE_MSK0) {
52 cfg2[idx].prop = MEC_GPIO_SLEW_RATE_ID;
53 cfg2[idx].val = (uint8_t)MEC_GPIO_SLEW_RATE_SLOW;
54 if (temp == MCHP_XEC_SLEW_RATE_FAST0) {
55 cfg2[idx].val = (uint8_t)MEC_GPIO_SLEW_RATE_FAST;
56 }
57 idx++;
58 }
59
60 /* drive strength */
61 temp = (conf >> MCHP_XEC_DRV_STR_POS) & MCHP_XEC_DRV_STR_MSK0;
62 if (temp != MCHP_XEC_DRV_STR_MSK0) {
63 cfg2[idx].prop = MEC_GPIO_DRV_STR_ID;
64 cfg2[idx].val = (uint8_t)(temp - 1u);
65 idx++;
66 }
67
68 /* Touch internal pull-up/pull-down? */
69 cfg2[idx].prop = MEC_GPIO_PUD_PROP_ID;
70 if (conf & BIT(MCHP_XEC_NO_PUD_POS)) {
71 cfg2[idx++].val = MEC_GPIO_PROP_NO_PUD;
72 } else if (conf & BIT(MCHP_XEC_PU_POS)) {
73 cfg2[idx++].val = MEC_GPIO_PROP_PULL_UP;
74 } else if (conf & BIT(MCHP_XEC_PD_POS)) {
75 cfg2[idx++].val = MEC_GPIO_PROP_PULL_DN;
76 }
77
78 /* Touch output enable. We always enable input */
79 if (conf & (BIT(MCHP_XEC_OUT_DIS_POS) | BIT(MCHP_XEC_OUT_EN_POS))) {
80 cfg2[idx].prop = MEC_GPIO_DIR_PROP_ID;
81 cfg2[idx].val = MEC_GPIO_PROP_DIR_IN;
82 if (conf & BIT(MCHP_XEC_OUT_EN_POS)) {
83 cfg2[idx].val = MEC_GPIO_PROP_DIR_OUT;
84 }
85 idx++;
86 }
87
88 /* Touch output state? Bit can be set even if the direction is input only */
89 if (conf & (BIT(MCHP_XEC_OUT_LO_POS) | BIT(MCHP_XEC_OUT_HI_POS))) {
90 cfg2[idx].prop = MEC_GPIO_CTRL_OUT_VAL_ID;
91 cfg2[idx].val = 0u;
92 if (conf & BIT(MCHP_XEC_OUT_HI_POS)) {
93 cfg2[idx].val = 1u;
94 }
95 idx++;
96 }
97
98 /* Touch output buffer type? */
99 if (conf & (BIT(MCHP_XEC_PUSH_PULL_POS) | BIT(MCHP_XEC_OPEN_DRAIN_POS))) {
100 cfg2[idx].prop = MEC_GPIO_OBUFT_PROP_ID;
101 cfg2[idx].val = MEC_GPIO_PROP_PUSH_PULL;
102 if (conf & BIT(MCHP_XEC_OPEN_DRAIN_POS)) {
103 cfg2[idx].val = MEC_GPIO_PROP_OPEN_DRAIN;
104 }
105 idx++;
106 }
107
108 /* Always touch power gate */
109 cfg2[idx].prop = MEC_GPIO_PWRGT_PROP_ID;
110 cfg2[idx].val = MEC_GPIO_PROP_PWRGT_VTR;
111 if (conf & BIT(MCHP_XEC_PIN_LOW_POWER_POS)) {
112 cfg2[idx].val = MEC_GPIO_PROP_PWRGT_OFF;
113 }
114 idx++;
115
116 /* Always touch MUX (alternate function) */
117 cfg2[idx].prop = MEC_GPIO_MUX_PROP_ID;
118 cfg2[idx].val = (uint8_t)altf;
119 idx++;
120
121 /* Always touch invert of alternate function. Need another bit to avoid touching */
122 cfg2[idx].prop = MEC_GPIO_FUNC_POL_PROP_ID;
123 cfg2[idx].val = MEC_GPIO_PROP_FUNC_OUT_NON_INV;
124 if (conf & BIT(MCHP_XEC_FUNC_INV_POS)) {
125 cfg2[idx].val = MEC_GPIO_PROP_FUNC_OUT_INV;
126 }
127 idx++;
128
129 /* HW sets output state set in control & parallel regs */
130 ret = mec_hal_gpio_set_props(pin, cfg2, idx);
131 if (ret) {
132 return -EIO;
133 }
134
135 /* make output state in control read-only in control and read-write in parallel reg */
136 ret = mec_hal_gpio_set_property(pin, MEC_GPIO_OSEL_PROP_ID, MEC_GPIO_PROP_OSEL_PAROUT);
137 if (ret) {
138 return -EIO;
139 }
140
141 return 0;
142 }
143
pinctrl_configure_pins(const pinctrl_soc_pin_t * pins,uint8_t pin_cnt,uintptr_t reg)144 int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg)
145 {
146 uint32_t pinmux, func;
147 int ret;
148
149 ARG_UNUSED(reg);
150
151 for (uint8_t i = 0U; i < pin_cnt; i++) {
152 pinmux = pins[i];
153
154 func = MCHP_XEC_PINMUX_FUNC(pinmux);
155 if (func >= MCHP_AFMAX) {
156 return -EINVAL;
157 }
158
159 ret = mec5_config_pin(pinmux, func);
160 if (ret < 0) {
161 return ret;
162 }
163 }
164
165 return 0;
166 }
167