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) 2021 Microchip Technology Inc.
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #define DT_DRV_COMPAT microchip_xec_pinctrl
11 
12 #include <zephyr/drivers/pinctrl.h>
13 #include <soc.h>
14 
15 /*
16  * Microchip XEC: each GPIO pin has two 32-bit control register.
17  * The first 32-bit register contains all pin features except
18  * slew rate and driver strength in the second control register.
19  * We compute the register index from the beginning of the GPIO
20  * control address space which is the same range of the PINCTRL
21  * parent node. A zero value in the PINCTRL pinmux field means
22  * do not touch.
23  */
config_drive_slew(struct gpio_regs * const regs,uint32_t idx,uint32_t conf)24 static void config_drive_slew(struct gpio_regs * const regs, uint32_t idx, uint32_t conf)
25 {
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) {
34 			val |= MCHP_GPIO_CTRL2_SLEW_FAST;
35 		}
36 	}
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);
42 	}
43 
44 	if (!msk) {
45 		return;
46 	}
47 
48 	regs->CTRL2[idx] = (regs->CTRL2[idx] & ~msk) | (val & msk);
49 }
50 
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  */
prog_pud(uint32_t pcr1,uint32_t conf)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.
90  */
xec_config_pin(uint32_t portpin,uint32_t conf,uint32_t altf)91 static int xec_config_pin(uint32_t portpin, uint32_t conf, uint32_t altf)
92 {
93 	struct gpio_regs * const regs = (struct gpio_regs * const)DT_INST_REG_ADDR(0);
94 	uint32_t port = MCHP_XEC_PINMUX_PORT(portpin);
95 	uint32_t pin = (uint32_t)MCHP_XEC_PINMUX_PIN(portpin);
96 	uint32_t idx = 0u, pcr1 = 0u;
97 
98 	if (port >= NUM_MCHP_GPIO_PORTS) {
99 		return -EINVAL;
100 	}
101 
102 	/* MCHP XEC family is 32 pins per port */
103 	idx = (port * 32U) + pin;
104 
105 	config_drive_slew(regs, idx, conf);
106 
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? */
112 
113 	pcr1 = prog_pud(pcr1, conf);
114 
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;
121 	}
122 
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);
129 	}
130 
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;
137 	}
138 
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 	}
146 
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);
150 
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);
156 	}
157 
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);
162 
163 	return 0;
164 }
165 
pinctrl_configure_pins(const pinctrl_soc_pin_t * pins,uint8_t pin_cnt,uintptr_t reg)166 int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
167 			   uintptr_t reg)
168 {
169 	uint32_t portpin, pinmux, func;
170 	int ret;
171 
172 	ARG_UNUSED(reg);
173 
174 	for (uint8_t i = 0U; i < pin_cnt; i++) {
175 		pinmux = pins[i];
176 
177 		func = MCHP_XEC_PINMUX_FUNC(pinmux);
178 		if (func >= MCHP_AFMAX) {
179 			return -EINVAL;
180 		}
181 
182 		portpin = MEC_XEC_PINMUX_PORT_PIN(pinmux);
183 
184 		ret = xec_config_pin(portpin, pinmux, func);
185 		if (ret < 0) {
186 			return ret;
187 		}
188 	}
189 
190 	return 0;
191 }
192