1 /*
2  * Copyright (C) 2020-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <aos/gpioc_csi.h>
6 
gpioc_csi_unregister(aos_gpioc_t * gpioc)7 static void gpioc_csi_unregister(aos_gpioc_t *gpioc)
8 {
9     aos_gpioc_csi_t *gpioc_csi;
10 
11     gpioc_csi = aos_container_of(gpioc, aos_gpioc_csi_t, gpioc);
12     (void)csi_gpio_detach_callback(&gpioc_csi->csi_gpio);
13     (void)csi_gpio_uninit(&gpioc_csi->csi_gpio);
14 }
15 
set_dir(aos_gpioc_csi_t * gpioc_csi,uint32_t pin,uint32_t dir)16 static aos_status_t set_dir(aos_gpioc_csi_t *gpioc_csi, uint32_t pin, uint32_t dir)
17 {
18     uint32_t mask = (uint32_t)1 << pin;
19     csi_error_t r;
20 
21     if (dir == AOS_GPIO_DIR_NONE)
22         r = CSI_OK;
23     else if (dir == AOS_GPIO_DIR_INPUT)
24         r = csi_gpio_dir(&gpioc_csi->csi_gpio, mask, GPIO_DIRECTION_INPUT);
25     else if (dir == AOS_GPIO_DIR_OUTPUT)
26         r = csi_gpio_dir(&gpioc_csi->csi_gpio, mask, GPIO_DIRECTION_OUTPUT);
27     else
28         r = CSI_ERROR;
29 
30     return (r == CSI_OK) ? 0 : -EIO;
31 }
32 
set_input_cfg(aos_gpioc_csi_t * gpioc_csi,uint32_t pin,uint32_t cfg)33 static aos_status_t set_input_cfg(aos_gpioc_csi_t *gpioc_csi, uint32_t pin, uint32_t cfg)
34 {
35     uint32_t mask = (uint32_t)1 << pin;
36     csi_error_t r;
37 
38     if (cfg == AOS_GPIO_INPUT_CFG_HI)
39         r = csi_gpio_mode(&gpioc_csi->csi_gpio, mask, GPIO_MODE_PULLNONE);
40     else if (cfg == AOS_GPIO_INPUT_CFG_PU)
41         r = csi_gpio_mode(&gpioc_csi->csi_gpio, mask, GPIO_MODE_PULLUP);
42     else if (cfg == AOS_GPIO_INPUT_CFG_PD)
43         r = csi_gpio_mode(&gpioc_csi->csi_gpio, mask, GPIO_MODE_PULLDOWN);
44     else
45         r = CSI_ERROR;
46 
47     return (r == CSI_OK) ? 0 : -EIO;
48 }
49 
set_irq_trig(aos_gpioc_csi_t * gpioc_csi,uint32_t pin,uint32_t trig)50 static aos_status_t set_irq_trig(aos_gpioc_csi_t *gpioc_csi, uint32_t pin, uint32_t trig)
51 {
52     csi_gpio_t *csi_gpio = &gpioc_csi->csi_gpio;
53     uint32_t mask = (uint32_t)1 << pin;
54     csi_error_t r;
55 
56     if (trig == AOS_GPIO_IRQ_TRIG_NONE)
57         r = CSI_OK;
58     else if (trig == AOS_GPIO_IRQ_TRIG_EDGE_RISING)
59         r = csi_gpio_irq_mode(csi_gpio, mask, GPIO_IRQ_MODE_RISING_EDGE);
60     else if (trig == AOS_GPIO_IRQ_TRIG_EDGE_FALLING)
61         r = csi_gpio_irq_mode(csi_gpio, mask, GPIO_IRQ_MODE_FALLING_EDGE);
62     else if (trig == AOS_GPIO_IRQ_TRIG_EDGE_BOTH)
63         r = csi_gpio_irq_mode(csi_gpio, mask, GPIO_IRQ_MODE_BOTH_EDGE);
64     else if (trig == AOS_GPIO_IRQ_TRIG_LEVEL_HIGH)
65         r = csi_gpio_irq_mode(csi_gpio, mask, GPIO_IRQ_MODE_HIGH_LEVEL);
66     else if (trig == AOS_GPIO_IRQ_TRIG_LEVEL_LOW)
67         r = csi_gpio_irq_mode(csi_gpio, mask, GPIO_IRQ_MODE_LOW_LEVEL);
68     else
69         r = CSI_ERROR;
70 
71     return (r == CSI_OK) ? 0 : -EIO;
72 }
73 
set_output_cfg(aos_gpioc_csi_t * gpioc_csi,uint32_t pin,uint32_t cfg)74 static aos_status_t set_output_cfg(aos_gpioc_csi_t *gpioc_csi, uint32_t pin, uint32_t cfg)
75 {
76     uint32_t mask = (uint32_t)1 << pin;
77     csi_error_t r;
78 
79     if (cfg == AOS_GPIO_OUTPUT_CFG_PP)
80         r = csi_gpio_mode(&gpioc_csi->csi_gpio, mask, GPIO_MODE_PUSH_PULL);
81     else if (cfg == AOS_GPIO_OUTPUT_CFG_ODNP)
82         r = csi_gpio_mode(&gpioc_csi->csi_gpio, mask, GPIO_MODE_OPEN_DRAIN);
83     else if (cfg == AOS_GPIO_OUTPUT_CFG_ODPU)
84         r = csi_gpio_mode(&gpioc_csi->csi_gpio, mask, GPIO_MODE_OPEN_DRAIN);
85     else
86         r = CSI_ERROR;
87 
88     return (r == CSI_OK) ? 0 : -EIO;
89 }
90 
restore_mode(aos_gpioc_csi_t * gpioc_csi,uint32_t pin)91 static void restore_mode(aos_gpioc_csi_t *gpioc_csi, uint32_t pin)
92 {
93     uint32_t mode = gpioc_csi->modes[pin];
94     uint32_t dir = mode & AOS_GPIO_DIR_MASK;
95 
96     if (dir == AOS_GPIO_DIR_INPUT) {
97         uint32_t cfg = mode & AOS_GPIO_INPUT_CFG_MASK;
98         uint32_t trig = mode & AOS_GPIO_IRQ_TRIG_MASK;
99         (void)set_dir(gpioc_csi, pin, dir);
100         (void)set_input_cfg(gpioc_csi, pin, cfg);
101         (void)set_irq_trig(gpioc_csi, pin, trig);
102     } else if (dir == AOS_GPIO_DIR_OUTPUT) {
103         uint32_t cfg = mode & AOS_GPIO_OUTPUT_CFG_MASK;
104         (void)set_dir(gpioc_csi, pin, dir);
105         (void)set_output_cfg(gpioc_csi, pin, cfg);
106     }
107 }
108 
gpioc_csi_set_mode(aos_gpioc_t * gpioc,uint32_t pin)109 static aos_status_t gpioc_csi_set_mode(aos_gpioc_t *gpioc, uint32_t pin)
110 {
111     aos_gpioc_csi_t *gpioc_csi;
112     uint32_t mode;
113     uint32_t dir;
114     aos_status_t ret;
115 
116     gpioc_csi = aos_container_of(gpioc, aos_gpioc_csi_t, gpioc);
117     mode = gpioc->pins[pin].mode;
118     dir = mode & AOS_GPIO_DIR_MASK;
119 
120     if (dir == AOS_GPIO_DIR_INPUT) {
121         uint32_t cfg = mode & AOS_GPIO_INPUT_CFG_MASK;
122         uint32_t trig = mode & AOS_GPIO_IRQ_TRIG_MASK;
123 
124         if (cfg == AOS_GPIO_INPUT_CFG_DEFAULT) {
125             cfg = gpioc_csi->default_input_cfg;
126             mode &= ~AOS_GPIO_INPUT_CFG_MASK;
127             mode |= cfg;
128         }
129 
130         ret = set_dir(gpioc_csi, pin, dir);
131         if (ret) {
132             restore_mode(gpioc_csi, pin);
133             return ret;
134         }
135 
136         ret = set_input_cfg(gpioc_csi, pin, cfg);
137         if (ret) {
138             restore_mode(gpioc_csi, pin);
139             return ret;
140         }
141 
142         ret = set_irq_trig(gpioc_csi, pin, trig);
143         if (ret) {
144             restore_mode(gpioc_csi, pin);
145             return ret;
146         }
147     } else if (dir == AOS_GPIO_DIR_OUTPUT) {
148         uint32_t cfg = mode & AOS_GPIO_OUTPUT_CFG_MASK;
149         uint32_t mask = (uint32_t)1 << pin;
150         csi_gpio_pin_state_t val;
151 
152         if (cfg == AOS_GPIO_OUTPUT_CFG_DEFAULT) {
153             cfg = gpioc_csi->default_output_cfg;
154             mode &= ~AOS_GPIO_OUTPUT_CFG_MASK;
155             mode |= cfg;
156         }
157 
158         ret = set_dir(gpioc_csi, pin, dir);
159         if (ret) {
160             restore_mode(gpioc_csi, pin);
161             return ret;
162         }
163 
164         ret = set_output_cfg(gpioc_csi, pin, cfg);
165         if (ret) {
166             restore_mode(gpioc_csi, pin);
167             return ret;
168         }
169 
170         val = gpioc->pins[pin].value ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
171         csi_gpio_write(&gpioc_csi->csi_gpio, mask, val);
172     }
173 
174     gpioc_csi->modes[pin] = mode;
175 
176     return 0;
177 }
178 
gpioc_csi_enable_irq(aos_gpioc_t * gpioc,uint32_t pin)179 static void gpioc_csi_enable_irq(aos_gpioc_t *gpioc, uint32_t pin)
180 {
181     aos_gpioc_csi_t *gpioc_csi;
182     uint32_t mask = (uint32_t)1 << pin;
183 
184     gpioc_csi = aos_container_of(gpioc, aos_gpioc_csi_t, gpioc);
185     (void)csi_gpio_irq_enable(&gpioc_csi->csi_gpio, mask, true);
186 }
187 
gpioc_csi_disable_irq(aos_gpioc_t * gpioc,uint32_t pin)188 static void gpioc_csi_disable_irq(aos_gpioc_t *gpioc, uint32_t pin)
189 {
190     aos_gpioc_csi_t *gpioc_csi;
191     uint32_t mask = (uint32_t)1 << pin;
192 
193     gpioc_csi = aos_container_of(gpioc, aos_gpioc_csi_t, gpioc);
194     (void)csi_gpio_irq_enable(&gpioc_csi->csi_gpio, mask, false);
195 }
196 
gpioc_csi_get_value(aos_gpioc_t * gpioc,uint32_t pin)197 static int gpioc_csi_get_value(aos_gpioc_t *gpioc, uint32_t pin)
198 {
199     aos_gpioc_csi_t *gpioc_csi;
200     uint32_t mask = (uint32_t)1 << pin;
201 
202     gpioc_csi = aos_container_of(gpioc, aos_gpioc_csi_t, gpioc);
203 
204     return !!(csi_gpio_read(&gpioc_csi->csi_gpio, mask) & mask);
205 }
206 
gpioc_csi_set_value(aos_gpioc_t * gpioc,uint32_t pin)207 static void gpioc_csi_set_value(aos_gpioc_t *gpioc, uint32_t pin)
208 {
209     aos_gpioc_csi_t *gpioc_csi;
210     uint32_t mask = (uint32_t)1 << pin;
211     csi_gpio_pin_state_t val;
212 
213     gpioc_csi = aos_container_of(gpioc, aos_gpioc_csi_t, gpioc);
214     val = gpioc->pins[pin].value ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
215     csi_gpio_write(&gpioc_csi->csi_gpio, mask, val);
216 }
217 
218 static const aos_gpioc_ops_t gpioc_csi_ops = {
219     .unregister         = gpioc_csi_unregister,
220     .set_mode           = gpioc_csi_set_mode,
221     .enable_irq         = gpioc_csi_enable_irq,
222     .disable_irq        = gpioc_csi_disable_irq,
223     .get_value          = gpioc_csi_get_value,
224     .set_value          = gpioc_csi_set_value,
225 };
226 
227 #define get_polarity(gpio, pin) (!!(csi_gpio_read(gpio, (uint32_t)1 << (pin)) & ((uint32_t)1 << (pin))))
228 
irq_handler(csi_gpio_t * csi_gpio,uint32_t pin_mask,void * arg)229 static void irq_handler(csi_gpio_t *csi_gpio, uint32_t pin_mask, void *arg)
230 {
231     aos_gpioc_csi_t *gpioc_csi;
232     aos_gpioc_t *gpioc;
233 
234     gpioc_csi = aos_container_of(csi_gpio, aos_gpioc_csi_t, csi_gpio);
235     gpioc = &gpioc_csi->gpioc;
236 
237     for (uint32_t i = 0; i < gpioc->num_pins; i++) {
238         if (pin_mask & ((uint32_t)1 << i))
239             aos_gpioc_hard_irq_handler(gpioc, i, get_polarity(csi_gpio, i));
240     }
241 }
242 
aos_gpioc_csi_register(aos_gpioc_csi_t * gpioc_csi)243 aos_status_t aos_gpioc_csi_register(aos_gpioc_csi_t *gpioc_csi)
244 {
245     aos_gpioc_t *gpioc;
246     csi_gpio_t *csi_gpio;
247     aos_status_t ret;
248 
249     if (!gpioc_csi)
250         return -EINVAL;
251 
252     gpioc = &gpioc_csi->gpioc;
253 
254     if (gpioc->num_pins > AOS_GPIOC_CSI_MAX_NUM_PINS)
255         return -EINVAL;
256 
257     gpioc->ops = &gpioc_csi_ops;
258 
259     for (uint32_t i = 0; i < gpioc->num_pins; i++)
260         gpioc_csi->modes[i] = AOS_GPIO_DIR_NONE;
261 
262     csi_gpio = &gpioc_csi->csi_gpio;
263 
264     if (csi_gpio_init(csi_gpio, gpioc->dev.id) != CSI_OK)
265         return -EIO;
266 
267     if (csi_gpio_attach_callback(csi_gpio, irq_handler, NULL) != CSI_OK) {
268         (void)csi_gpio_uninit(csi_gpio);
269         return -EIO;
270     }
271 
272     ret = aos_gpioc_register(gpioc);
273     if (ret) {
274         (void)csi_gpio_detach_callback(csi_gpio);
275         (void)csi_gpio_uninit(csi_gpio);
276         return ret;
277     }
278 
279     return 0;
280 }
281 
aos_gpioc_csi_unregister(uint32_t id)282 aos_status_t aos_gpioc_csi_unregister(uint32_t id)
283 {
284     return aos_gpioc_unregister(id);
285 }
286