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