blob: ecc71b39532fdae1b0785375c5d7377f2ee9c6ee [file] [log] [blame]
Harsh Shaha1af8822017-05-11 22:06:36 -07001/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
14
15#include <linux/slab.h>
16#include <linux/spinlock.h>
17#include <linux/interrupt.h>
18#include <linux/list.h>
19#include <linux/ratelimit.h>
20#include "cam_tasklet_util.h"
21#include "cam_irq_controller.h"
22
23#undef CDBG
24#define CDBG(fmt, args...) pr_debug(fmt, ##args)
25
26#define CAM_TASKLETQ_SIZE 256
27
28static void cam_tasklet_action(unsigned long data);
29
30/**
31 * struct cam_tasklet_queue_cmd:
32 * @Brief: Structure associated with each slot in the
33 * tasklet queue
34 *
35 * @list: list_head member for each entry in queue
36 * @payload: Payload structure for the event. This will be
37 * passed to the handler function
38 * @bottom_half_handler: Function pointer for event handler in bottom
39 * half context
40 *
41 */
42struct cam_tasklet_queue_cmd {
43 struct list_head list;
44 void *payload;
45 CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler;
46};
47
48/**
49 * struct cam_tasklet_info:
50 * @Brief: Tasklet private structure
51 *
52 * @list: list_head member for each tasklet
53 * @index: Instance id for the tasklet
54 * @tasklet_lock: Spin lock
55 * @tasklet_active: Atomic variable to control tasklet state
56 * @tasklet: Tasklet structure used to schedule bottom half
57 * @free_cmd_list: List of free tasklet queue cmd for use
58 * @used_cmd_list: List of used tasklet queue cmd
59 * @cmd_queue: Array of tasklet cmd for storage
60 * @ctx_priv: Private data passed to the handling function
61 *
62 */
63struct cam_tasklet_info {
64 struct list_head list;
65 uint32_t index;
66 spinlock_t tasklet_lock;
67 atomic_t tasklet_active;
68 struct tasklet_struct tasklet;
69
70 struct list_head free_cmd_list;
71 struct list_head used_cmd_list;
72 struct cam_tasklet_queue_cmd cmd_queue[CAM_TASKLETQ_SIZE];
73
74 void *ctx_priv;
75};
76
77/**
78 * cam_tasklet_get_cmd()
79 *
80 * @brief: Get free cmd from tasklet
81 *
82 * @tasklet: Tasklet Info structure to get cmd from
83 * @tasklet_cmd: Return tasklet_cmd pointer if successful
84 *
85 * @return: 0: Success
86 * Negative: Failure
87 */
88static int cam_tasklet_get_cmd(
89 struct cam_tasklet_info *tasklet,
90 struct cam_tasklet_queue_cmd **tasklet_cmd)
91{
92 int rc = 0;
93 unsigned long flags;
94
95 *tasklet_cmd = NULL;
96
97 if (!atomic_read(&tasklet->tasklet_active)) {
98 pr_err_ratelimited("Tasklet is not active!\n");
99 rc = -EPIPE;
100 return rc;
101 }
102
103 spin_lock_irqsave(&tasklet->tasklet_lock, flags);
104 if (list_empty(&tasklet->free_cmd_list)) {
105 pr_err_ratelimited("No more free tasklet cmd!\n");
106 rc = -ENODEV;
107 goto spin_unlock;
108 } else {
109 *tasklet_cmd = list_first_entry(&tasklet->free_cmd_list,
110 struct cam_tasklet_queue_cmd, list);
111 list_del_init(&(*tasklet_cmd)->list);
112 }
113
114spin_unlock:
115 spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
116 return rc;
117}
118
119/**
120 * cam_tasklet_put_cmd()
121 *
122 * @brief: Put back cmd to free list
123 *
124 * @tasklet: Tasklet Info structure to put cmd into
125 * @tasklet_cmd: tasklet_cmd pointer that needs to be put back
126 *
127 * @return: Void
128 */
129static void cam_tasklet_put_cmd(
130 struct cam_tasklet_info *tasklet,
131 struct cam_tasklet_queue_cmd **tasklet_cmd)
132{
133 unsigned long flags;
134
135 spin_lock_irqsave(&tasklet->tasklet_lock, flags);
136 list_add_tail(&(*tasklet_cmd)->list,
137 &tasklet->free_cmd_list);
138 spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
139}
140
141/**
142 * cam_tasklet_dequeue_cmd()
143 *
144 * @brief: Initialize the tasklet info structure
145 *
146 * @hw_mgr_ctx: Private Ctx data that will be passed to the handler
147 * function
148 * @idx: Index of tasklet used as identity
149 * @tasklet_action: Tasklet callback function that will be called
150 * when tasklet runs on CPU
151 *
152 * @return: 0: Success
153 * Negative: Failure
154 */
155static int cam_tasklet_dequeue_cmd(
156 struct cam_tasklet_info *tasklet,
157 struct cam_tasklet_queue_cmd **tasklet_cmd)
158{
159 int rc = 0;
160 unsigned long flags;
161
162 *tasklet_cmd = NULL;
163
164 if (!atomic_read(&tasklet->tasklet_active)) {
165 pr_err("Tasklet is not active!\n");
166 rc = -EPIPE;
167 return rc;
168 }
169
170 CDBG("Dequeue before lock.\n");
171 spin_lock_irqsave(&tasklet->tasklet_lock, flags);
172 if (list_empty(&tasklet->used_cmd_list)) {
173 CDBG("End of list reached. Exit\n");
174 rc = -ENODEV;
175 goto spin_unlock;
176 } else {
177 *tasklet_cmd = list_first_entry(&tasklet->used_cmd_list,
178 struct cam_tasklet_queue_cmd, list);
179 list_del_init(&(*tasklet_cmd)->list);
180 CDBG("Dequeue Successful\n");
181 }
182
183spin_unlock:
184 spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
185 return rc;
186}
187
188int cam_tasklet_enqueue_cmd(
189 void *bottom_half,
190 void *handler_priv,
191 void *evt_payload_priv,
192 CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler)
193{
194 struct cam_tasklet_info *tasklet = bottom_half;
195 struct cam_tasklet_queue_cmd *tasklet_cmd = NULL;
196 unsigned long flags;
197 int rc;
198
199 if (!bottom_half) {
200 pr_err("NULL bottom half\n");
201 return -EINVAL;
202 }
203
204 rc = cam_tasklet_get_cmd(tasklet, &tasklet_cmd);
205
206 if (tasklet_cmd) {
207 CDBG("%s: Enqueue tasklet cmd\n", __func__);
208 tasklet_cmd->bottom_half_handler = bottom_half_handler;
209 tasklet_cmd->payload = evt_payload_priv;
210 spin_lock_irqsave(&tasklet->tasklet_lock, flags);
211 list_add_tail(&tasklet_cmd->list,
212 &tasklet->used_cmd_list);
213 spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
214 tasklet_schedule(&tasklet->tasklet);
215 } else {
216 pr_err("%s: tasklet cmd is NULL!\n", __func__);
217 }
218
219 return rc;
220}
221
222int cam_tasklet_init(
223 void **tasklet_info,
224 void *hw_mgr_ctx,
225 uint32_t idx)
226{
227 int i;
228 struct cam_tasklet_info *tasklet = NULL;
229
230 tasklet = kzalloc(sizeof(struct cam_tasklet_info), GFP_KERNEL);
231 if (!tasklet) {
232 CDBG("Error! Unable to allocate memory for tasklet");
233 *tasklet_info = NULL;
234 return -ENOMEM;
235 }
236
237 tasklet->ctx_priv = hw_mgr_ctx;
238 tasklet->index = idx;
239 spin_lock_init(&tasklet->tasklet_lock);
240 memset(tasklet->cmd_queue, 0, sizeof(tasklet->cmd_queue));
241 INIT_LIST_HEAD(&tasklet->free_cmd_list);
242 INIT_LIST_HEAD(&tasklet->used_cmd_list);
243 for (i = 0; i < CAM_TASKLETQ_SIZE; i++) {
244 INIT_LIST_HEAD(&tasklet->cmd_queue[i].list);
245 list_add_tail(&tasklet->cmd_queue[i].list,
246 &tasklet->free_cmd_list);
247 }
248 tasklet_init(&tasklet->tasklet, cam_tasklet_action,
249 (unsigned long)tasklet);
250 cam_tasklet_stop(tasklet);
251
252 *tasklet_info = tasklet;
253
254 return 0;
255}
256
257void cam_tasklet_deinit(void **tasklet_info)
258{
259 struct cam_tasklet_info *tasklet = *tasklet_info;
260
261 atomic_set(&tasklet->tasklet_active, 0);
262 tasklet_kill(&tasklet->tasklet);
263 kfree(tasklet);
264 *tasklet_info = NULL;
265}
266
267int cam_tasklet_start(void *tasklet_info)
268{
269 struct cam_tasklet_info *tasklet = tasklet_info;
270 struct cam_tasklet_queue_cmd *tasklet_cmd;
271 struct cam_tasklet_queue_cmd *tasklet_cmd_temp;
272
273 if (atomic_read(&tasklet->tasklet_active)) {
274 pr_err("Tasklet already active. idx = %d\n", tasklet->index);
275 return -EBUSY;
276 }
277 atomic_set(&tasklet->tasklet_active, 1);
278
279 /* flush the command queue first */
280 list_for_each_entry_safe(tasklet_cmd, tasklet_cmd_temp,
281 &tasklet->used_cmd_list, list) {
282 list_del_init(&tasklet_cmd->list);
283 list_add_tail(&tasklet_cmd->list, &tasklet->free_cmd_list);
284 }
285
286 tasklet_enable(&tasklet->tasklet);
287
288 return 0;
289}
290
291void cam_tasklet_stop(void *tasklet_info)
292{
293 struct cam_tasklet_info *tasklet = tasklet_info;
294
295 atomic_set(&tasklet->tasklet_active, 0);
296 tasklet_disable(&tasklet->tasklet);
297}
298
299/*
300 * cam_tasklet_action()
301 *
302 * @brief: Process function that will be called when tasklet runs
303 * on CPU
304 *
305 * @data: Tasklet Info structure that is passed in tasklet_init
306 *
307 * @return: Void
308 */
309static void cam_tasklet_action(unsigned long data)
310{
311 struct cam_tasklet_info *tasklet_info = NULL;
312 struct cam_tasklet_queue_cmd *tasklet_cmd = NULL;
313
314 tasklet_info = (struct cam_tasklet_info *)data;
315
316 while (!cam_tasklet_dequeue_cmd(tasklet_info, &tasklet_cmd)) {
317 tasklet_cmd->bottom_half_handler(tasklet_info->ctx_priv,
318 tasklet_cmd->payload);
319 cam_tasklet_put_cmd(tasklet_info, &tasklet_cmd);
320 }
321}
322