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