1 /*
2  * Copyright (c) 2021 Teslabs Engineering S.L.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/init.h>
8 #include <zephyr/drivers/clock_control.h>
9 #include <zephyr/drivers/clock_control/gd32.h>
10 #include <zephyr/drivers/pinctrl.h>
11 
12 #include <gd32_gpio.h>
13 
14 /** AFIO DT node */
15 #define AFIO_NODE DT_NODELABEL(afio)
16 
17 /** GPIO mode: input floating (CTL bits) */
18 #define GPIO_MODE_INP_FLOAT 0x4U
19 /** GPIO mode: input with pull-up/down (CTL bits) */
20 #define GPIO_MODE_INP_PUPD 0x8U
21 /** GPIO mode: output push-pull (CTL bits) */
22 #define GPIO_MODE_ALT_PP 0x8U
23 /** GPIO mode: output open-drain (CTL bits) */
24 #define GPIO_MODE_ALT_OD 0xCU
25 
26 /** Utility macro that expands to the GPIO port address if it exists */
27 #define GD32_PORT_ADDR_OR_NONE(nodelabel)				       \
28 	COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)),		       \
29 		   (DT_REG_ADDR(DT_NODELABEL(nodelabel)),), ())
30 
31 /** Utility macro that expands to the GPIO clock id if it exists */
32 #define GD32_PORT_CLOCK_ID_OR_NONE(nodelabel)				       \
33 	COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)),		       \
34 		   (DT_CLOCKS_CELL(DT_NODELABEL(nodelabel), id),), ())
35 
36 /** GD32 port addresses */
37 static const uint32_t gd32_port_addrs[] = {
38 	GD32_PORT_ADDR_OR_NONE(gpioa)
39 	GD32_PORT_ADDR_OR_NONE(gpiob)
40 	GD32_PORT_ADDR_OR_NONE(gpioc)
41 	GD32_PORT_ADDR_OR_NONE(gpiod)
42 	GD32_PORT_ADDR_OR_NONE(gpioe)
43 	GD32_PORT_ADDR_OR_NONE(gpiof)
44 	GD32_PORT_ADDR_OR_NONE(gpiog)
45 };
46 
47 /** GD32 port clock identifiers */
48 static const uint16_t gd32_port_clkids[] = {
49 	GD32_PORT_CLOCK_ID_OR_NONE(gpioa)
50 	GD32_PORT_CLOCK_ID_OR_NONE(gpiob)
51 	GD32_PORT_CLOCK_ID_OR_NONE(gpioc)
52 	GD32_PORT_CLOCK_ID_OR_NONE(gpiod)
53 	GD32_PORT_CLOCK_ID_OR_NONE(gpioe)
54 	GD32_PORT_CLOCK_ID_OR_NONE(gpiof)
55 	GD32_PORT_CLOCK_ID_OR_NONE(gpiog)
56 };
57 
58 /**
59  * @brief Initialize AFIO
60  *
61  * This function enables AFIO clock and configures the I/O compensation if
62  * available and enabled in Devicetree.
63  *
64  * @retval 0 Always
65  */
afio_init(void)66 static int afio_init(void)
67 {
68 	uint16_t clkid = DT_CLOCKS_CELL(AFIO_NODE, id);
69 
70 
71 	(void)clock_control_on(GD32_CLOCK_CONTROLLER,
72 			       (clock_control_subsys_t)&clkid);
73 
74 #ifdef AFIO_CPSCTL
75 	if (DT_PROP(AFIO_NODE, enable_cps)) {
76 		gpio_compensation_config(GPIO_COMPENSATION_ENABLE);
77 		while (gpio_compensation_flag_get() == RESET)
78 			;
79 	}
80 #endif /* AFIO_CPSCTL */
81 
82 	return 0;
83 }
84 
85 SYS_INIT(afio_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
86 
87 /**
88  * @brief Helper function to configure the SPD register if available.
89  *
90  * @param port GPIO port address.
91  * @param pin_bit GPIO pin, set as bit position.
92  * @param speed GPIO speed.
93  *
94  * @return Value of the mode register (speed) that should be set later.
95  */
configure_spd(uint32_t port,uint32_t pin_bit,uint8_t speed)96 static inline uint8_t configure_spd(uint32_t port, uint32_t pin_bit,
97 				    uint8_t speed)
98 {
99 	switch (speed) {
100 	case GD32_OSPEED_MAX:
101 #ifdef GPIOx_SPD
102 		GPIOx_SPD(port) |= pin_bit;
103 #endif /* GPIOx_SPD */
104 		return speed;
105 	default:
106 #ifdef GPIOx_SPD
107 		GPIOx_SPD(port) &= ~pin_bit;
108 #endif /* GPIOx_SPD */
109 		return speed + 1U;
110 	}
111 }
112 
113 /**
114  * @brief Configure a pin.
115  *
116  * @param pin The pin to configure.
117  */
configure_pin(pinctrl_soc_pin_t pin)118 static void configure_pin(pinctrl_soc_pin_t pin)
119 {
120 	uint8_t port_idx, mode, pin_num;
121 	uint32_t port, pin_bit, reg_val;
122 	volatile uint32_t *reg;
123 	uint16_t clkid;
124 
125 	port_idx = GD32_PORT_GET(pin);
126 	__ASSERT_NO_MSG(port_idx < ARRAY_SIZE(gd32_port_addrs));
127 
128 	clkid = gd32_port_clkids[port_idx];
129 	port = gd32_port_addrs[port_idx];
130 	pin_num = GD32_PIN_GET(pin);
131 	pin_bit = BIT(pin_num);
132 	mode = GD32_MODE_GET(pin);
133 
134 	if (pin_num < 8U) {
135 		reg = &GPIO_CTL0(port);
136 	} else {
137 		reg = &GPIO_CTL1(port);
138 		pin_num -= 8U;
139 	}
140 
141 	(void)clock_control_on(GD32_CLOCK_CONTROLLER,
142 			       (clock_control_subsys_t)&clkid);
143 
144 	reg_val = *reg;
145 	reg_val &= ~GPIO_MODE_MASK(pin_num);
146 
147 	if (mode == GD32_MODE_ALTERNATE) {
148 		uint8_t new_mode;
149 
150 		new_mode = configure_spd(port, pin_bit, GD32_OSPEED_GET(pin));
151 
152 		if (GD32_OTYPE_GET(pin) == GD32_OTYPE_PP) {
153 			new_mode |= GPIO_MODE_ALT_PP;
154 		} else {
155 			new_mode |= GPIO_MODE_ALT_OD;
156 		}
157 
158 		reg_val |= GPIO_MODE_SET(pin_num, new_mode);
159 	} else if (mode == GD32_MODE_GPIO_IN) {
160 		uint8_t pupd = GD32_PUPD_GET(pin);
161 
162 		if (pupd == GD32_PUPD_NONE) {
163 			reg_val |= GPIO_MODE_SET(pin_num, GPIO_MODE_INP_FLOAT);
164 		} else {
165 			reg_val |= GPIO_MODE_SET(pin_num, GPIO_MODE_INP_PUPD);
166 
167 			if (pupd == GD32_PUPD_PULLDOWN) {
168 				GPIO_BC(port) = pin_bit;
169 			} else if (pupd == GD32_PUPD_PULLUP) {
170 				GPIO_BOP(port) = pin_bit;
171 			}
172 		}
173 	}
174 
175 	*reg = reg_val;
176 }
177 
178 /**
179  * @brief Configure remap.
180  *
181  * @param remap Remap bit field as encoded by #GD32_REMAP.
182  */
configure_remap(uint16_t remap)183 static void configure_remap(uint16_t remap)
184 {
185 	uint8_t pos;
186 	uint32_t reg_val;
187 	volatile uint32_t *reg;
188 
189 	/* not remappable */
190 	if (remap == GD32_NORMP) {
191 		return;
192 	}
193 
194 	if (GD32_REMAP_REG_GET(remap) == 0U) {
195 		reg = &AFIO_PCF0;
196 	} else {
197 		reg = &AFIO_PCF1;
198 	}
199 
200 	pos = GD32_REMAP_POS_GET(remap);
201 
202 	reg_val = *reg;
203 	reg_val &= ~(GD32_REMAP_MSK_GET(remap) << pos);
204 	reg_val |= GD32_REMAP_VAL_GET(remap) << pos;
205 	*reg = reg_val;
206 }
207 
pinctrl_configure_pins(const pinctrl_soc_pin_t * pins,uint8_t pin_cnt,uintptr_t reg)208 int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
209 			   uintptr_t reg)
210 {
211 	ARG_UNUSED(reg);
212 
213 	if (pin_cnt == 0U) {
214 		return -EINVAL;
215 	}
216 
217 	/* same remap is encoded in all pins, so just pick the first */
218 	configure_remap(GD32_REMAP_GET(pins[0]));
219 
220 	/* configure all pins */
221 	for (uint8_t i = 0U; i < pin_cnt; i++) {
222 		configure_pin(pins[i]);
223 	}
224 
225 	return 0;
226 }
227