• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * gpio_adapter.h
3  *
4  * gpio driver adapter of linux
5  *
6  * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
7  *
8  * This software is licensed under the terms of the GNU General Public
9  * License version 2, as published by the Free Software Foundation, and
10  * may be copied, distributed, and modified under those terms.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  */
18 
19 #include <linux/gpio.h>
20 #include <linux/gpio/driver.h>
21 #include <linux/interrupt.h>
22 #include <linux/list.h>
23 #include "device_resource_if.h"
24 #include "gpio/gpio_core.h"
25 #include "hdf_base.h"
26 #include "hdf_device_desc.h"
27 #include "hdf_dlist.h"
28 #include "hdf_log.h"
29 #include "osal_mem.h"
30 #include "osal_mutex.h"
31 
32 #define HDF_LOG_TAG linux_gpio_adapter
33 
34 #define LINUX_GPIO_NUM_MAX 0x7FFF
35 
LinuxGpioWrite(struct GpioCntlr * cntlr,uint16_t local,uint16_t val)36 static int32_t LinuxGpioWrite(struct GpioCntlr *cntlr, uint16_t local, uint16_t val)
37 {
38     if (cntlr == NULL) {
39         return HDF_ERR_INVALID_OBJECT;
40     }
41     gpio_set_value(cntlr->start + local, val);
42     return HDF_SUCCESS;
43 }
44 
LinuxGpioRead(struct GpioCntlr * cntlr,uint16_t local,uint16_t * val)45 static int32_t LinuxGpioRead(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val)
46 {
47     if (cntlr == NULL) {
48         return HDF_ERR_INVALID_OBJECT;
49     }
50     if (val != NULL) {
51         *val = (gpio_get_value(cntlr->start + local) == 0) ?
52             GPIO_VAL_LOW : GPIO_VAL_HIGH;
53         return HDF_SUCCESS;
54     }
55     HDF_LOGE("%s: val is NULL!\n", __func__);
56     return HDF_ERR_BSP_PLT_API_ERR;
57 }
58 
LinuxGpioSetDir(struct GpioCntlr * cntlr,uint16_t local,uint16_t dir)59 static int32_t LinuxGpioSetDir(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir)
60 {
61     int32_t ret;
62     int val;
63 
64     if (cntlr == NULL) {
65         return HDF_ERR_INVALID_OBJECT;
66     }
67     switch (dir) {
68         case GPIO_DIR_IN:
69             ret = gpio_direction_input(cntlr->start + local);
70             if (ret < 0) {
71                 return HDF_ERR_BSP_PLT_API_ERR;
72             }
73             break;
74         case GPIO_DIR_OUT:
75             val = gpio_get_value(cntlr->start + local);
76             if (val < 0) {
77                 return HDF_ERR_BSP_PLT_API_ERR;
78             }
79             ret = gpio_direction_output(cntlr->start + local, val);
80             if (ret < 0) {
81                 return HDF_ERR_BSP_PLT_API_ERR;
82             }
83             break;
84         default:
85             HDF_LOGE("%s: invalid dir:%d\n", __func__, dir);
86             return HDF_ERR_INVALID_PARAM;
87     }
88     return HDF_SUCCESS;
89 }
90 
LinuxGpioGetDir(struct GpioCntlr * cntlr,uint16_t local,uint16_t * dir)91 static int32_t LinuxGpioGetDir(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir)
92 {
93     int dirGet;
94 
95     if (cntlr == NULL) {
96         return HDF_ERR_INVALID_OBJECT;
97     }
98     dirGet = gpiod_get_direction(gpio_to_desc(cntlr->start + local));
99     if (dirGet < 0) {
100         return HDF_ERR_BSP_PLT_API_ERR;
101     }
102     *dir = (dirGet == GPIOF_DIR_IN) ? GPIO_DIR_IN : GPIO_DIR_OUT;
103     return HDF_SUCCESS;
104 }
105 
LinuxGpioIrqDummy(int irq,void * data)106 static irqreturn_t LinuxGpioIrqDummy(int irq, void *data)
107 {
108     (void)irq;
109     (void)data;
110     return IRQ_HANDLED;
111 }
112 
LinuxGpioIrqBridge(int irq,void * data)113 static irqreturn_t LinuxGpioIrqBridge(int irq, void *data)
114 {
115     int gpio = (int)(uintptr_t)data;
116     struct GpioCntlr *cntlr = NULL;
117 
118     cntlr = GpioCntlrGetByGpio(gpio);
119     GpioCntlrIrqCallback(cntlr, GpioCntlrGetLocal(cntlr, gpio));
120     GpioCntlrPut(cntlr);
121     return IRQ_HANDLED;
122 }
123 
LinuxGpioSetIrq(struct GpioCntlr * cntlr,uint16_t local,uint16_t mode)124 static int32_t LinuxGpioSetIrq(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode)
125 {
126     int ret, irq;
127     unsigned long flags = 0;
128     uint16_t gpio;
129 
130     if (cntlr == NULL) {
131         return HDF_ERR_INVALID_OBJECT;
132     }
133     gpio = cntlr->start + local;
134 
135     irq = gpio_to_irq(gpio);
136     if (irq < 0) {
137         HDF_LOGE("%s: gpio(%u) to irq fail:%d", __func__, gpio, irq);
138         return HDF_ERR_BSP_PLT_API_ERR;
139     }
140     flags |= (mode & GPIO_IRQ_TRIGGER_RISING) == 0 ? 0 : IRQF_TRIGGER_RISING;
141     flags |= (mode & GPIO_IRQ_TRIGGER_FALLING) == 0 ? 0 : IRQF_TRIGGER_FALLING;
142     flags |= (mode & GPIO_IRQ_TRIGGER_HIGH) == 0 ? 0 : IRQF_TRIGGER_HIGH;
143     flags |= (mode & GPIO_IRQ_TRIGGER_LOW) == 0 ? 0 : IRQF_TRIGGER_LOW;
144     HDF_LOGI("%s: gona request normal irq:%d(%u)\n", __func__, irq, gpio);
145     ret = request_irq(irq, LinuxGpioIrqBridge, flags,
146         "LinuxIrqBridge", (void *)(uintptr_t)gpio);
147     if (ret != 0) {
148         HDF_LOGI("%s: gona request threaded irq:%d(%u)\n", __func__, irq, gpio);
149         flags |= IRQF_ONESHOT;
150         ret = request_threaded_irq(irq, LinuxGpioIrqBridge, LinuxGpioIrqDummy, flags,
151             "LinuxIrqBridge", (void *)(uintptr_t)gpio);
152     }
153     if (ret == 0) {
154         disable_irq_nosync(irq); // disable on set
155     }
156     return (ret == 0) ? HDF_SUCCESS : HDF_ERR_BSP_PLT_API_ERR;
157 }
158 
LinuxGpioUnsetIrq(struct GpioCntlr * cntlr,uint16_t local)159 static int32_t LinuxGpioUnsetIrq(struct GpioCntlr *cntlr, uint16_t local)
160 {
161     int irq;
162     uint16_t gpio;
163 
164     if (cntlr == NULL) {
165         return HDF_ERR_INVALID_OBJECT;
166     }
167     gpio = cntlr->start + local;
168     irq = gpio_to_irq(gpio);
169     if (irq < 0) {
170         HDF_LOGE("%s: gpio(%u) to irq fail:%d", __func__, gpio, irq);
171         return HDF_ERR_BSP_PLT_API_ERR;
172     }
173     HDF_LOGI("%s: gona free irq:%d\n", __func__, irq);
174     free_irq(irq, (void *)(uintptr_t)gpio);
175     return HDF_SUCCESS;
176 }
177 
LinuxGpioEnableIrq(struct GpioCntlr * cntlr,uint16_t local)178 static inline int32_t LinuxGpioEnableIrq(struct GpioCntlr *cntlr, uint16_t local)
179 {
180     int irq;
181     uint16_t gpio;
182 
183     if (cntlr == NULL) {
184         return HDF_ERR_INVALID_OBJECT;
185     }
186     gpio = cntlr->start + local;
187     irq = gpio_to_irq(gpio);
188     if (irq < 0) {
189         HDF_LOGE("%s: gpio(%u) to irq fail:%d", __func__, gpio, irq);
190         return HDF_ERR_BSP_PLT_API_ERR;
191     }
192     enable_irq(irq);
193     return HDF_SUCCESS;
194 }
195 
LinuxGpioDisableIrq(struct GpioCntlr * cntlr,uint16_t local)196 static inline int32_t LinuxGpioDisableIrq(struct GpioCntlr *cntlr, uint16_t local)
197 {
198     int irq;
199     uint16_t gpio;
200 
201     if (cntlr == NULL) {
202         return HDF_ERR_INVALID_OBJECT;
203     }
204     gpio = cntlr->start + local;
205     irq = gpio_to_irq(gpio);
206     if (irq < 0) {
207         HDF_LOGE("%s: gpio(%u) to irq fail:%d", __func__, gpio, irq);
208         return HDF_ERR_BSP_PLT_API_ERR;
209     }
210     disable_irq_nosync(irq); // nosync default in case used in own irq
211     return HDF_SUCCESS;
212 }
213 
214 static struct GpioMethod g_method = {
215     .write = LinuxGpioWrite,
216     .read = LinuxGpioRead,
217     .setDir = LinuxGpioSetDir,
218     .getDir = LinuxGpioGetDir,
219     .setIrq = LinuxGpioSetIrq,
220     .unsetIrq = LinuxGpioUnsetIrq,
221     .enableIrq = LinuxGpioEnableIrq,
222     .disableIrq = LinuxGpioDisableIrq,
223 };
224 
LinuxGpioBind(struct HdfDeviceObject * device)225 static int32_t LinuxGpioBind(struct HdfDeviceObject *device)
226 {
227     (void)device;
228     return HDF_SUCCESS;
229 }
230 
LinuxGpioMatchProbe(struct gpio_chip * chip,void * data)231 static int LinuxGpioMatchProbe(struct gpio_chip *chip, void *data)
232 {
233     int32_t ret;
234     struct GpioCntlr *cntlr = NULL;
235 
236     (void)data;
237     if (chip == NULL) {
238         return 0;
239     }
240     HDF_LOGI("%s: find gpio chip(start:%d, count:%u)", __func__, chip->base, chip->ngpio);
241     if (chip->base >= LINUX_GPIO_NUM_MAX || (chip->base + chip->ngpio) > LINUX_GPIO_NUM_MAX) {
242         HDF_LOGW("%s: chip(base:%d-num:%u) exceed range", __func__, chip->base, chip->ngpio);
243         return 0;
244     }
245 
246     cntlr = (struct GpioCntlr *)OsalMemCalloc(sizeof(*cntlr));
247     if (cntlr == NULL) {
248         HDF_LOGE("%s: malloc cntlr fail!", __func__);
249         return HDF_ERR_MALLOC_FAIL;
250     }
251 
252     cntlr->ops = &g_method;
253     cntlr->start = (uint16_t)chip->base;
254     cntlr->count = (uint16_t)chip->ngpio;
255     ret = GpioCntlrAdd(cntlr);
256     if (ret != HDF_SUCCESS) {
257         HDF_LOGE("%s: add gpio controller(start:%d, count:%u) fail:%d!",
258             __func__, cntlr->start, cntlr->count, ret);
259         OsalMemFree(cntlr);
260         return ret;
261     }
262 
263     HDF_LOGI("%s: add gpio controller(start:%d, count:%u) succeed",
264         __func__, cntlr->start, cntlr->count);
265     return 0; // return 0 to continue
266 }
267 
LinuxGpioInit(struct HdfDeviceObject * device)268 static int32_t LinuxGpioInit(struct HdfDeviceObject *device)
269 {
270     if (device == NULL) {
271         HDF_LOGE("%s: Fail, device is NULL.", __func__);
272         return HDF_ERR_INVALID_OBJECT;
273     }
274 
275     (void)gpiochip_find(device, LinuxGpioMatchProbe);
276     HDF_LOGI("%s: dev service:%s init done!", __func__, HdfDeviceGetServiceName(device));
277     return HDF_SUCCESS;
278 }
279 
LinuxGpioMatchRelease(struct gpio_chip * chip,void * data)280 static int LinuxGpioMatchRelease(struct gpio_chip *chip, void *data)
281 {
282     int32_t ret;
283     struct GpioCntlr *cntlr = NULL;
284     struct PlatformManager *manager = GpioManagerGet();
285 
286     if (chip == NULL) {
287         return 0;
288     }
289     HDF_LOGI("%s: find gpio chip(start:%d, count:%u)", __func__, chip->base, chip->ngpio);
290     if (chip->base >= LINUX_GPIO_NUM_MAX || (chip->base + chip->ngpio) > LINUX_GPIO_NUM_MAX) {
291         HDF_LOGW("%s: chip(base:%d-num:%u) exceed range", __func__, chip->base, chip->ngpio);
292         return 0;
293     }
294 
295     cntlr = GpioCntlrGetByGpio((uint16_t)chip->base);
296     if (cntlr == NULL) {
297         HDF_LOGW("%s: get cntlr failed for base:%d!", __func__, chip->base);
298         return 0;
299     }
300     GpioCntlrPut(cntlr); // !!! be careful to keep the reference count balanced
301 
302     HDF_LOGI("%s: gona remove gpio controller(start:%d, count:%u)",
303         __func__, cntlr->start, cntlr->count);
304     GpioCntlrRemove(cntlr);
305     OsalMemFree(cntlr);
306     return 0; // return 0 to continue
307 }
308 
LinuxGpioRelease(struct HdfDeviceObject * device)309 static void LinuxGpioRelease(struct HdfDeviceObject *device)
310 {
311     struct GpioCntlr *cntlr = NULL;
312 
313     if (device == NULL) {
314         HDF_LOGE("%s: device is null!", __func__);
315         return;
316     }
317 
318     (void)gpiochip_find(device, LinuxGpioMatchRelease);
319 }
320 
321 struct HdfDriverEntry g_gpioLinuxDriverEntry = {
322     .moduleVersion = 1,
323     .Bind = LinuxGpioBind,
324     .Init = LinuxGpioInit,
325     .Release = LinuxGpioRelease,
326     .moduleName = "linux_gpio_adapter",
327 };
328 HDF_INIT(g_gpioLinuxDriverEntry);
329