1 /*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 *
4 * HDF is dual licensed: you can use it either under the terms of
5 * the GPL, or the BSD license, at your option.
6 * See the LICENSE file in the root of this repository for complete details.
7 */
8
9 #include "gpio/gpio_core.h"
10 #include "osal_mem.h"
11 #include "platform_core.h"
12
13 #define HDF_LOG_TAG gpio_core
14
15 #define GPIO_IRQ_STACK_SIZE 10000
16
GpioInfoLock(struct GpioInfo * ginfo)17 static inline void GpioInfoLock(struct GpioInfo *ginfo)
18 {
19 (void)OsalSpinLockIrqSave(&ginfo->spin, &ginfo->irqSave);
20 }
21
GpioInfoUnlock(struct GpioInfo * ginfo)22 static inline void GpioInfoUnlock(struct GpioInfo *ginfo)
23 {
24 (void)OsalSpinUnlockIrqRestore(&ginfo->spin, &ginfo->irqSave);
25 }
26
GpioCntlrWrite(struct GpioCntlr * cntlr,uint16_t local,uint16_t val)27 int32_t GpioCntlrWrite(struct GpioCntlr *cntlr, uint16_t local, uint16_t val)
28 {
29 if (cntlr == NULL) {
30 return HDF_ERR_INVALID_OBJECT;
31 }
32 if (cntlr->ops == NULL || cntlr->ops->write == NULL) {
33 return HDF_ERR_NOT_SUPPORT;
34 }
35
36 return cntlr->ops->write(cntlr, local, val);
37 }
38
GpioCntlrRead(struct GpioCntlr * cntlr,uint16_t local,uint16_t * val)39 int32_t GpioCntlrRead(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val)
40 {
41 if (cntlr == NULL) {
42 return HDF_ERR_INVALID_OBJECT;
43 }
44 if (cntlr->ops == NULL || cntlr->ops->read == NULL) {
45 return HDF_ERR_NOT_SUPPORT;
46 }
47 if (val == NULL) {
48 return HDF_ERR_INVALID_PARAM;
49 }
50
51 return cntlr->ops->read(cntlr, local, val);
52 }
53
GpioCntlrSetDir(struct GpioCntlr * cntlr,uint16_t local,uint16_t dir)54 int32_t GpioCntlrSetDir(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir)
55 {
56 if (cntlr == NULL) {
57 return HDF_ERR_INVALID_OBJECT;
58 }
59 if (cntlr->ops == NULL || cntlr->ops->setDir == NULL) {
60 return HDF_ERR_NOT_SUPPORT;
61 }
62
63 return cntlr->ops->setDir(cntlr, local, dir);
64 }
65
GpioCntlrGetDir(struct GpioCntlr * cntlr,uint16_t local,uint16_t * dir)66 int32_t GpioCntlrGetDir(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir)
67 {
68 if (cntlr == NULL) {
69 return HDF_ERR_INVALID_OBJECT;
70 }
71 if (cntlr->ops == NULL || cntlr->ops->getDir == NULL) {
72 return HDF_ERR_NOT_SUPPORT;
73 }
74 if (dir == NULL) {
75 return HDF_ERR_INVALID_PARAM;
76 }
77
78 return cntlr->ops->getDir(cntlr, local, dir);
79 }
80
GpioCntlrToIrq(struct GpioCntlr * cntlr,uint16_t local,uint16_t * irq)81 int32_t GpioCntlrToIrq(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq)
82 {
83 if (cntlr == NULL) {
84 return HDF_ERR_INVALID_OBJECT;
85 }
86 if (cntlr->ops == NULL || cntlr->ops->toIrq == NULL) {
87 return HDF_ERR_NOT_SUPPORT;
88 }
89
90 return cntlr->ops->toIrq(cntlr, local, irq);
91 }
92
GpioCntlrIrqCallback(struct GpioCntlr * cntlr,uint16_t local)93 void GpioCntlrIrqCallback(struct GpioCntlr *cntlr, uint16_t local)
94 {
95 struct GpioInfo *ginfo = NULL;
96 struct GpioIrqRecord *irqRecord = NULL;
97
98 if (cntlr == NULL || cntlr->ginfos == NULL || local >= cntlr->count) {
99 PLAT_LOGW("GpioCntlrIrqCallback: invalid cntlr(ginfos) or loal num:%u!", local);
100 return;
101 }
102 ginfo = &cntlr->ginfos[local];
103 if (ginfo == NULL) {
104 PLAT_LOGW("GpioCntlrIrqCallback: ginfo null(start:%u, local:%u)", cntlr->start, local);
105 return;
106 }
107
108 GpioInfoLock(ginfo);
109 irqRecord = ginfo->irqRecord;
110
111 if (irqRecord == NULL) {
112 PLAT_LOGW("GpioCntlrIrqCallback: irq not set (start:%u, local:%u)", cntlr->start, local);
113 GpioInfoUnlock(ginfo);
114 return;
115 }
116 GpioIrqRecordTrigger(irqRecord);
117 GpioInfoUnlock(ginfo);
118 }
119
GpioCntlrIrqThreadHandler(void * data)120 static int32_t GpioCntlrIrqThreadHandler(void *data)
121 {
122 int32_t ret;
123 uint32_t irqSave;
124 struct GpioIrqRecord *irqRecord = (struct GpioIrqRecord *)data;
125
126 while (true) {
127 ret = OsalSemWait(&irqRecord->sem, HDF_WAIT_FOREVER);
128 if (irqRecord->removed) {
129 break;
130 }
131 if (ret != HDF_SUCCESS) {
132 continue;
133 }
134 if (irqRecord->btmFunc != NULL) {
135 (void)irqRecord->btmFunc(irqRecord->global, irqRecord->irqData);
136 }
137 }
138 (void)OsalSpinLockIrqSave(&irqRecord->spin, &irqSave);
139 (void)OsalSpinUnlockIrqRestore(&irqRecord->spin, &irqSave); // last post done
140 PLAT_LOGI("GpioCntlrIrqThreadHandler: gpio(%u) thread exit", irqRecord->global);
141 (void)OsalSemDestroy(&irqRecord->sem);
142 (void)OsalSpinDestroy(&irqRecord->spin);
143 (void)OsalThreadDestroy(&irqRecord->thread);
144 OsalMemFree(irqRecord);
145 return HDF_SUCCESS;
146 }
147
GpioCntlrSetIrqInner(struct GpioInfo * ginfo,struct GpioIrqRecord * irqRecord)148 static int32_t GpioCntlrSetIrqInner(struct GpioInfo *ginfo, struct GpioIrqRecord *irqRecord)
149 {
150 int32_t ret;
151 uint16_t local = GpioInfoToLocal(ginfo);
152 struct GpioCntlr *cntlr = ginfo->cntlr;
153
154 if (cntlr == NULL) {
155 PLAT_LOGE("GpioCntlrSetIrqInner: cntlr is NULL");
156 return HDF_ERR_INVALID_PARAM;
157 }
158 GpioInfoLock(ginfo);
159
160 if (ginfo->irqRecord != NULL) {
161 GpioInfoUnlock(ginfo);
162 PLAT_LOGE("GpioCntlrSetIrqInner: gpio(%u+%u) irq already set", cntlr->start, local);
163 return HDF_ERR_NOT_SUPPORT;
164 }
165
166 ginfo->irqRecord = irqRecord;
167 GpioInfoUnlock(ginfo);
168 ret = cntlr->ops->setIrq(cntlr, local, irqRecord->mode);
169 if (ret != HDF_SUCCESS) {
170 GpioInfoLock(ginfo);
171 PLAT_LOGE("GpioCntlrSetIrqInner: gpio setIrq fail");
172 ginfo->irqRecord = NULL;
173 GpioInfoUnlock(ginfo);
174 }
175 return ret;
176 }
177
GpioIrqRecordCreate(struct GpioInfo * ginfo,uint16_t mode,GpioIrqFunc func,void * arg,struct GpioIrqRecord ** new)178 static int32_t GpioIrqRecordCreate(struct GpioInfo *ginfo, uint16_t mode, GpioIrqFunc func, void *arg,
179 struct GpioIrqRecord **new)
180 {
181 int32_t ret;
182 struct GpioIrqRecord *irqRecord = NULL;
183 struct OsalThreadParam cfg;
184
185 irqRecord = (struct GpioIrqRecord *)OsalMemCalloc(sizeof(*irqRecord));
186 if (irqRecord == NULL) {
187 PLAT_LOGE("GpioIrqRecordCreate: alloc irq record failed");
188 return HDF_ERR_MALLOC_FAIL;
189 }
190 irqRecord->removed = false;
191 irqRecord->mode = mode;
192 irqRecord->irqFunc = ((mode & GPIO_IRQ_USING_THREAD) == 0) ? func : NULL;
193 irqRecord->btmFunc = ((mode & GPIO_IRQ_USING_THREAD) != 0) ? func : NULL;
194 irqRecord->irqData = arg;
195 irqRecord->global = GpioInfoToGlobal(ginfo);
196 if (irqRecord->btmFunc != NULL) {
197 ret = OsalThreadCreate(&irqRecord->thread, GpioCntlrIrqThreadHandler, irqRecord);
198 if (ret != HDF_SUCCESS) {
199 OsalMemFree(irqRecord);
200 PLAT_LOGE("GpioIrqRecordCreate: create irq thread failed:%d", ret);
201 return ret;
202 }
203 (void)OsalSpinInit(&irqRecord->spin);
204 (void)OsalSemInit(&irqRecord->sem, 0);
205 cfg.name = (char *)ginfo->name;
206 cfg.priority = OSAL_THREAD_PRI_HIGHEST;
207 cfg.stackSize = GPIO_IRQ_STACK_SIZE;
208 ret = OsalThreadStart(&irqRecord->thread, &cfg);
209 if (ret != HDF_SUCCESS) {
210 (void)OsalSemDestroy(&irqRecord->sem);
211 (void)OsalSpinDestroy(&irqRecord->spin);
212 (void)OsalThreadDestroy(&irqRecord->thread);
213 OsalMemFree(irqRecord);
214 PLAT_LOGE("GpioIrqRecordCreate: start irq thread failed:%d", ret);
215 return ret;
216 }
217 PLAT_LOGI("GpioIrqRecordCreate: gpio(%u) thread started", GpioInfoToGlobal(ginfo));
218 }
219
220 *new = irqRecord;
221 return HDF_SUCCESS;
222 }
223
GpioCntlrSetIrq(struct GpioCntlr * cntlr,uint16_t local,uint16_t mode,GpioIrqFunc func,void * arg)224 int32_t GpioCntlrSetIrq(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg)
225 {
226 int32_t ret;
227 struct GpioInfo *ginfo = NULL;
228 struct GpioIrqRecord *irqRecord = NULL;
229
230 if (cntlr == NULL || cntlr->ginfos == NULL) {
231 return HDF_ERR_INVALID_OBJECT;
232 }
233 if (local >= cntlr->count || func == NULL) {
234 return HDF_ERR_INVALID_PARAM;
235 }
236 if (cntlr->ops == NULL || cntlr->ops->setIrq == NULL) {
237 return HDF_ERR_NOT_SUPPORT;
238 }
239
240 ginfo = &cntlr->ginfos[local];
241 ret = GpioIrqRecordCreate(ginfo, mode, func, arg, &irqRecord);
242 if (ret != HDF_SUCCESS) {
243 PLAT_LOGE("GpioCntlrSetIrq: create irq record failed:%d", ret);
244 return ret;
245 }
246
247 ret = GpioCntlrSetIrqInner(ginfo, irqRecord);
248 if (ret != HDF_SUCCESS) {
249 GpioIrqRecordDestroy(irqRecord);
250 }
251 return ret;
252 }
253
GpioCntlrUnsetIrq(struct GpioCntlr * cntlr,uint16_t local,void * arg)254 int32_t GpioCntlrUnsetIrq(struct GpioCntlr *cntlr, uint16_t local, void *arg)
255 {
256 int32_t ret;
257 struct GpioInfo *ginfo = NULL;
258 struct GpioIrqRecord *irqRecord = NULL;
259
260 if (cntlr == NULL || cntlr->ginfos == NULL) {
261 return HDF_ERR_INVALID_OBJECT;
262 }
263 if (local >= cntlr->count) {
264 return HDF_ERR_INVALID_PARAM;
265 }
266 if (cntlr->ops == NULL || cntlr->ops->unsetIrq == NULL) {
267 return HDF_ERR_NOT_SUPPORT;
268 }
269
270 ginfo = &cntlr->ginfos[local];
271 GpioInfoLock(ginfo);
272 if (ginfo->irqRecord == NULL) {
273 GpioInfoUnlock(ginfo);
274 PLAT_LOGE("GpioCntlrUnsetIrq: gpio(%u+%u) irq not set yet", cntlr->start, local);
275 return HDF_ERR_NOT_SUPPORT;
276 }
277 irqRecord = ginfo->irqRecord;
278 if (arg != irqRecord->irqData) {
279 GpioInfoUnlock(ginfo);
280 PLAT_LOGE("GpioCntlrUnsetIrq: gpio(%u+%u) arg not match", cntlr->start, local);
281 return HDF_ERR_INVALID_PARAM;
282 }
283 ret = cntlr->ops->unsetIrq(cntlr, local);
284 if (ret == HDF_SUCCESS) {
285 ginfo->irqRecord = NULL;
286 }
287 GpioInfoUnlock(ginfo);
288
289 if (ret == HDF_SUCCESS) {
290 GpioIrqRecordDestroy(irqRecord);
291 }
292 return ret;
293 }
294
GpioCntlrEnableIrq(struct GpioCntlr * cntlr,uint16_t local)295 int32_t GpioCntlrEnableIrq(struct GpioCntlr *cntlr, uint16_t local)
296 {
297 if (cntlr == NULL) {
298 return HDF_ERR_INVALID_OBJECT;
299 }
300 if (cntlr->ops == NULL || cntlr->ops->enableIrq == NULL) {
301 return HDF_ERR_NOT_SUPPORT;
302 }
303
304 return cntlr->ops->enableIrq(cntlr, local);
305 }
306
GpioCntlrDisableIrq(struct GpioCntlr * cntlr,uint16_t local)307 int32_t GpioCntlrDisableIrq(struct GpioCntlr *cntlr, uint16_t local)
308 {
309 if (cntlr == NULL) {
310 return HDF_ERR_INVALID_OBJECT;
311 }
312 if (cntlr->ops == NULL || cntlr->ops->disableIrq == NULL) {
313 return HDF_ERR_NOT_SUPPORT;
314 }
315
316 return cntlr->ops->disableIrq(cntlr, local);
317 }
318