blob: fab10b83df8862bd4661840fc54cfc281dc1a8de [file] [log] [blame]
Satya Durga Srinivasu Prabhalaf38015e2013-10-29 13:50:42 -07001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Naveen Ramaraj88f23632012-10-10 12:23:29 -07002 *
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#include <linux/workqueue.h>
14#include <linux/types.h>
15#include <linux/delay.h>
16#include <linux/bitops.h>
17#include <linux/wait.h>
18#include <linux/sched.h>
19#include <linux/notifier.h>
20#include <linux/slab.h>
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/cdev.h>
24#include <linux/fs.h>
25#include <linux/platform_device.h>
26#include <linux/err.h>
27#include <linux/io.h>
28#include <linux/ctype.h>
29#include <linux/of_device.h>
Naveen Ramaraj93060a42012-11-15 23:23:55 -080030#include <linux/msm_dsps.h>
31#include <linux/uaccess.h>
Naveen Ramaraj88f23632012-10-10 12:23:29 -070032#include <asm/mach-types.h>
Naveen Ramaraj93060a42012-11-15 23:23:55 -080033#include <asm/arch_timer.h>
Naveen Ramaraj88f23632012-10-10 12:23:29 -070034#include <mach/subsystem_restart.h>
35#include <mach/ocmem.h>
36#include <mach/msm_smd.h>
37#include <mach/sensors_adsp.h>
38#include <mach/msm_bus.h>
39#include <mach/msm_bus_board.h>
40
Naveen Ramaraj93060a42012-11-15 23:23:55 -080041#define DRV_NAME "sensors"
Naveen Ramaraj88f23632012-10-10 12:23:29 -070042#define DRV_VERSION "1.00"
43
44#define SNS_OCMEM_SMD_CHANNEL "SENSOR"
45#define SNS_OCMEM_CLIENT_ID OCMEM_SENSORS
Neeti Desaia07e87d2012-12-18 18:26:31 -080046#define SNS_OCMEM_SIZE SZ_256K
47#define SMD_BUF_SIZE 1024
48#define SNS_TIMEOUT_MS 1000
Naveen Ramaraj88f23632012-10-10 12:23:29 -070049
50#define SNS_OCMEM_ALLOC_GROW 0x00000001
51#define SNS_OCMEM_ALLOC_SHRINK 0x00000002
52#define SNS_OCMEM_MAP_DONE 0x00000004
53#define SNS_OCMEM_MAP_FAIL 0x00000008
54#define SNS_OCMEM_UNMAP_DONE 0x00000010
55#define SNS_OCMEM_UNMAP_FAIL 0x00000020
56
57#define DSPS_HAS_CLIENT 0x00000100
58#define DSPS_HAS_NO_CLIENT 0x00000200
59#define DSPS_BW_VOTE_ON 0x00000400
60#define DSPS_BW_VOTE_OFF 0x00000800
61#define DSPS_PHYS_ADDR_SET 0x00001000
62
Neeti Desaia07e87d2012-12-18 18:26:31 -080063/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -070064 * Structure contains all state used by the sensors driver
65 */
66struct sns_adsp_control_s {
67 wait_queue_head_t sns_wait;
68 spinlock_t sns_lock;
69 struct workqueue_struct *sns_workqueue;
70 struct work_struct sns_work;
Neeti Desaia07e87d2012-12-18 18:26:31 -080071 struct workqueue_struct *smd_wq;
72 struct work_struct smd_read_work;
Naveen Ramaraj88f23632012-10-10 12:23:29 -070073 smd_channel_t *smd_ch;
74 uint32_t sns_ocmem_status;
75 uint32_t mem_segments_size;
76 struct sns_mem_segment_s_v01 mem_segments[SNS_OCMEM_MAX_NUM_SEG_V01];
77 struct ocmem_buf *buf;
78 struct ocmem_map_list map_list;
79 struct ocmem_notifier *ocmem_handle;
80 bool ocmem_enabled;
81 struct notifier_block ocmem_nb;
82 uint32_t sns_ocmem_bus_client;
83 struct platform_device *pdev;
84 void *pil;
Naveen Ramaraj93060a42012-11-15 23:23:55 -080085 struct class *dev_class;
86 dev_t dev_num;
87 struct device *dev;
88 struct cdev *cdev;
Naveen Ramaraj88f23632012-10-10 12:23:29 -070089};
90
91static struct sns_adsp_control_s sns_ctl;
92
Neeti Desaia07e87d2012-12-18 18:26:31 -080093/*
94 * All asynchronous responses from the OCMEM driver are received
95 * by this function
96 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -070097int sns_ocmem_drv_cb(struct notifier_block *self,
98 unsigned long action,
99 void *dev)
100{
101 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700102
103 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
104
105 pr_debug("%s: Received OCMEM callback: action=%li\n",
106 __func__, action);
107
108 switch (action) {
109 case OCMEM_MAP_DONE:
110 sns_ctl.sns_ocmem_status |= SNS_OCMEM_MAP_DONE;
111 sns_ctl.sns_ocmem_status &= (~OCMEM_MAP_FAIL &
112 ~SNS_OCMEM_UNMAP_DONE &
113 ~SNS_OCMEM_UNMAP_FAIL);
114 break;
115 case OCMEM_MAP_FAIL:
116 sns_ctl.sns_ocmem_status |= SNS_OCMEM_MAP_FAIL;
117 sns_ctl.sns_ocmem_status &= (~OCMEM_MAP_DONE &
118 ~SNS_OCMEM_UNMAP_DONE &
119 ~SNS_OCMEM_UNMAP_FAIL);
120 break;
121 case OCMEM_UNMAP_DONE:
122 sns_ctl.sns_ocmem_status |= SNS_OCMEM_UNMAP_DONE;
123 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_FAIL &
124 ~SNS_OCMEM_MAP_DONE &
125 ~OCMEM_MAP_FAIL);
126 break;
127 case OCMEM_UNMAP_FAIL:
128 sns_ctl.sns_ocmem_status |= SNS_OCMEM_UNMAP_FAIL;
129 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_DONE &
130 ~SNS_OCMEM_MAP_DONE &
131 ~OCMEM_MAP_FAIL);
132 break;
133 case OCMEM_ALLOC_GROW:
134 sns_ctl.sns_ocmem_status |= SNS_OCMEM_ALLOC_GROW;
135 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_SHRINK;
136 break;
137 case OCMEM_ALLOC_SHRINK:
138 sns_ctl.sns_ocmem_status |= SNS_OCMEM_ALLOC_SHRINK;
139 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_GROW;
140 break;
141 default:
142 pr_err("%s: Unknown action received in OCMEM callback %lu\n",
143 __func__, action);
144 break;
145 }
146
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700147 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700148 wake_up(&sns_ctl.sns_wait);
149
150 return 0;
151}
152
Neeti Desaia07e87d2012-12-18 18:26:31 -0800153/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700154 * Processes messages received through SMD from the ADSP
155 *
156 * @param hdr The message header
157 * @param msg Message pointer
158 *
Neeti Desaia07e87d2012-12-18 18:26:31 -0800159 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700160void sns_ocmem_smd_process(struct sns_ocmem_hdr_s *hdr, void *msg)
161{
162 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700163
164 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
165
166 pr_debug("%s: Received message from ADSP; id: %i type: %i (%08x)\n",
167 __func__, hdr->msg_id, hdr->msg_type,
168 sns_ctl.sns_ocmem_status);
169
170 if (hdr->msg_id == SNS_OCMEM_PHYS_ADDR_RESP_V01 &&
171 hdr->msg_type == SNS_OCMEM_MSG_TYPE_RESP) {
172 struct sns_ocmem_phys_addr_resp_msg_v01 *msg_ptr =
173 (struct sns_ocmem_phys_addr_resp_msg_v01 *)msg;
174 pr_debug("%s: Received SNS_OCMEM_PHYS_ADDR_RESP_V01\n",
175 __func__);
176 pr_debug("%s: segments_valid=%d, segments_len=%d\n", __func__,
177 msg_ptr->segments_valid, msg_ptr->segments_len);
178
179 if (msg_ptr->segments_valid) {
180 sns_ctl.mem_segments_size = msg_ptr->segments_len;
181 memcpy(sns_ctl.mem_segments, msg_ptr->segments,
182 sizeof(struct sns_mem_segment_s_v01) *
183 msg_ptr->segments_len);
184
185 sns_ctl.sns_ocmem_status |= DSPS_PHYS_ADDR_SET;
186 } else {
187 pr_err("%s: Received invalid segment list\n", __func__);
188 }
189 } else if (hdr->msg_id == SNS_OCMEM_HAS_CLIENT_IND_V01 &&
190 hdr->msg_type == SNS_OCMEM_MSG_TYPE_IND) {
191 struct sns_ocmem_has_client_ind_msg_v01 *msg_ptr =
192 (struct sns_ocmem_has_client_ind_msg_v01 *)msg;
193
194 pr_debug("%s: Received SNS_OCMEM_HAS_CLIENT_IND_V01\n",
195 __func__);
196 pr_debug("%s: ADSP has %i client(s)\n", __func__,
197 msg_ptr->num_clients);
198 if (msg_ptr->num_clients > 0) {
199 sns_ctl.sns_ocmem_status |= DSPS_HAS_CLIENT;
200 sns_ctl.sns_ocmem_status &= ~DSPS_HAS_NO_CLIENT;
201 } else {
202 sns_ctl.sns_ocmem_status |= DSPS_HAS_NO_CLIENT;
203 sns_ctl.sns_ocmem_status &= ~DSPS_HAS_CLIENT;
204 }
205 } else if (hdr->msg_id == SNS_OCMEM_BW_VOTE_RESP_V01 &&
206 hdr->msg_type == SNS_OCMEM_MSG_TYPE_RESP) {
207 /* no need to handle this response msg, just return */
208 pr_debug("%s: Received SNS_OCMEM_BW_VOTE_RESP_V01\n", __func__);
209 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
210 return;
211 } else if (hdr->msg_id == SNS_OCMEM_BW_VOTE_IND_V01 &&
212 hdr->msg_type == SNS_OCMEM_MSG_TYPE_IND) {
213 struct sns_ocmem_bw_vote_ind_msg_v01 *msg_ptr =
214 (struct sns_ocmem_bw_vote_ind_msg_v01 *)msg;
215 pr_debug("%s: Received BW_VOTE_IND_V01, is_vote_on=%d\n",
216 __func__, msg_ptr->is_vote_on);
217
218 if (msg_ptr->is_vote_on) {
219 sns_ctl.sns_ocmem_status |= DSPS_BW_VOTE_ON;
220 sns_ctl.sns_ocmem_status &= ~DSPS_BW_VOTE_OFF;
221 } else {
222 sns_ctl.sns_ocmem_status |= DSPS_BW_VOTE_OFF;
223 sns_ctl.sns_ocmem_status &= ~DSPS_BW_VOTE_ON;
224 }
225 } else {
226 pr_err("%s: Unknown message type received. id: %i; type: %i\n",
227 __func__, hdr->msg_id, hdr->msg_type);
228 }
229
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700230 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
231
232 wake_up(&sns_ctl.sns_wait);
233}
234
Neeti Desaia07e87d2012-12-18 18:26:31 -0800235static void sns_ocmem_smd_read(struct work_struct *ws)
236{
237 struct smd_channel *ch = sns_ctl.smd_ch;
238 unsigned char *buf = NULL;
239 int sz, len;
240
241 for (;;) {
242 sz = smd_cur_packet_size(ch);
243 BUG_ON(sz > SMD_BUF_SIZE);
244 len = smd_read_avail(ch);
245 pr_debug("%s: sz=%d, len=%d\n", __func__, sz, len);
246 if (len == 0 || len < sz)
247 break;
248 buf = kzalloc(SMD_BUF_SIZE, GFP_KERNEL);
249 if (buf == NULL) {
250 pr_err("%s: malloc failed", __func__);
251 break;
252 }
253
254 if (smd_read(ch, buf, sz) != sz) {
255 pr_err("%s: not enough data?!\n", __func__);
256 kfree(buf);
257 continue;
258 }
259
260 sns_ocmem_smd_process((struct sns_ocmem_hdr_s *)buf,
261 (void *)((char *)buf +
262 sizeof(struct sns_ocmem_hdr_s)));
263
264 kfree(buf);
265
266 }
267}
268
269/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700270 * All SMD notifications and messages from Sensors on ADSP are
271 * received by this function
272 *
Neeti Desaia07e87d2012-12-18 18:26:31 -0800273 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700274void sns_ocmem_smd_notify_data(void *data, unsigned int event)
275{
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700276 if (event == SMD_EVENT_DATA) {
Neeti Desaia07e87d2012-12-18 18:26:31 -0800277 int sz;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700278 pr_debug("%s: Received SMD event Data\n", __func__);
Neeti Desaia07e87d2012-12-18 18:26:31 -0800279 sz = smd_cur_packet_size(sns_ctl.smd_ch);
280 if ((sz > 0) && (sz <= smd_read_avail(sns_ctl.smd_ch)))
281 queue_work(sns_ctl.smd_wq, &sns_ctl.smd_read_work);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700282 } else if (event == SMD_EVENT_OPEN) {
283 pr_debug("%s: Received SMD event Open\n", __func__);
284 } else if (event == SMD_EVENT_CLOSE) {
285 pr_debug("%s: Received SMD event Close\n", __func__);
286 }
287}
288
289static bool sns_ocmem_is_status_set(uint32_t sns_ocmem_status)
290{
291 unsigned long flags;
292 bool is_set;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700293
294 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
295 is_set = sns_ctl.sns_ocmem_status & sns_ocmem_status;
296 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700297 return is_set;
298}
299
Neeti Desaia07e87d2012-12-18 18:26:31 -0800300/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700301 * Wait for a response from ADSP or OCMEM Driver, timeout if necessary
302 *
303 * @param sns_ocmem_status Status flags to wait for.
304 * @param timeout_sec Seconds to wait before timeout
305 * @param timeout_nsec Nanoseconds to wait. Total timeout = nsec + sec
306 *
307 * @return 0 If any status flag is set at any time prior to a timeout.
308 * 0 if success or timedout ; <0 for failures
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700309 */
310static int sns_ocmem_wait(uint32_t sns_ocmem_status,
311 uint32_t timeout_ms)
312{
313 int err;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700314 if (timeout_ms) {
315 err = wait_event_interruptible_timeout(sns_ctl.sns_wait,
316 sns_ocmem_is_status_set(sns_ocmem_status),
317 msecs_to_jiffies(timeout_ms));
318
319 if (err == 0)
320 pr_err("%s: interruptible_timeout timeout err=%i\n",
321 __func__, err);
322 else if (err < 0)
323 pr_err("%s: interruptible_timeout failed err=%i\n",
324 __func__, err);
325 } else { /* no timeout */
326 err = wait_event_interruptible(sns_ctl.sns_wait,
327 sns_ocmem_is_status_set(sns_ocmem_status));
328 if (err < 0)
329 pr_err("%s: wait_event_interruptible failed err=%i\n",
330 __func__, err);
331 }
332
333 return err;
334}
335
Neeti Desaia07e87d2012-12-18 18:26:31 -0800336/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700337 * Sends a message to the ADSP via SMD.
338 *
339 * @param hdr Specifies message type and other meta data
340 * @param msg_ptr Pointer to the message contents.
341 * Must be freed within this function if no error is returned.
342 *
343 * @return 0 upon success; < 0 upon error
344 */
345static int
346sns_ocmem_send_msg(struct sns_ocmem_hdr_s *hdr, void const *msg_ptr)
347{
348 int rv = 0;
349 int err = 0;
350 void *temp = NULL;
351 int size = sizeof(struct sns_ocmem_hdr_s) + hdr->msg_size;
352
353 temp = kzalloc(sizeof(struct sns_ocmem_hdr_s) + hdr->msg_size,
354 GFP_KERNEL);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700355 if (temp == NULL) {
356 pr_err("%s: allocation failure\n", __func__);
357 rv = -ENOMEM;
Satya Durga Srinivasu Prabhalaebcdc4b2013-10-29 14:01:30 -0700358 goto out;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700359 }
360
361 hdr->dst_module = SNS_OCMEM_MODULE_ADSP;
362 hdr->src_module = SNS_OCMEM_MODULE_KERNEL;
363
364 memcpy(temp, hdr, sizeof(struct sns_ocmem_hdr_s));
365 memcpy((char *)temp + sizeof(struct sns_ocmem_hdr_s),
366 msg_ptr, hdr->msg_size);
367 pr_debug("%s: send msg type: %i size: %i id: %i dst: %i src: %i\n",
368 __func__, hdr->msg_type, hdr->msg_size,
369 hdr->msg_id, hdr->dst_module, hdr->src_module);
370
371 if (hdr == NULL) {
372 pr_err("%s: NULL message header\n", __func__);
373 rv = -EINVAL;
374 } else {
375 if (sns_ctl.smd_ch == NULL) {
376 pr_err("%s: null smd_ch\n", __func__);
377 rv = -EINVAL;
378 }
379 err = smd_write(sns_ctl.smd_ch, temp, size);
380 if (err < 0) {
381 pr_err("%s: smd_write failed %i\n", __func__, err);
382 rv = -ECOMM;
383 } else {
384 pr_debug("%s smd_write successful ret=%d\n",
385 __func__, err);
386 }
387 }
388
389 kfree(temp);
390
Satya Durga Srinivasu Prabhalaebcdc4b2013-10-29 14:01:30 -0700391out:
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700392 return rv;
393}
394
Neeti Desaia07e87d2012-12-18 18:26:31 -0800395/*
396 * Load ADSP Firmware.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700397 */
398
399static int sns_load_adsp(void)
400{
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700401 sns_ctl.pil = subsystem_get("adsp");
402 if (IS_ERR(sns_ctl.pil)) {
403 pr_err("%s: fail to load ADSP firmware\n", __func__);
404 return -ENODEV;
405 }
406
407 pr_debug("%s: Q6/ADSP image is loaded\n", __func__);
408
409 return 0;
410}
411
412static int sns_ocmem_platform_data_populate(struct platform_device *pdev)
413{
414 int ret;
415 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
416 struct msm_bus_vectors *sns_ocmem_bus_vectors = NULL;
417 struct msm_bus_paths *ocmem_sns_bus_paths = NULL;
418 u32 val;
419
420 if (!pdev->dev.of_node) {
421 pr_err("%s: device tree information missing\n", __func__);
422 return -ENODEV;
423 }
424
425 sns_ocmem_bus_vectors = kzalloc(sizeof(struct msm_bus_vectors),
426 GFP_KERNEL);
427 if (!sns_ocmem_bus_vectors) {
428 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
429 return -ENOMEM;
430 }
431
432 ret = of_property_read_u32(pdev->dev.of_node,
433 "qcom,src-id", &val);
434 if (ret) {
435 dev_err(&pdev->dev, "%s: qcom,src-id missing in DT node\n",
436 __func__);
437 goto fail1;
438 }
439 sns_ocmem_bus_vectors->src = val;
440 ret = of_property_read_u32(pdev->dev.of_node,
441 "qcom,dst-id", &val);
442 if (ret) {
443 dev_err(&pdev->dev, "%s: qcom,dst-id missing in DT node\n",
444 __func__);
445 goto fail1;
446 }
447 sns_ocmem_bus_vectors->dst = val;
448 ret = of_property_read_u32(pdev->dev.of_node,
449 "qcom,ab", &val);
450 if (ret) {
451 dev_err(&pdev->dev, "%s: qcom,ab missing in DT node\n",
452 __func__);
453 goto fail1;
454 }
455 sns_ocmem_bus_vectors->ab = val;
456 ret = of_property_read_u32(pdev->dev.of_node,
457 "qcom,ib", &val);
458 if (ret) {
459 dev_err(&pdev->dev, "%s: qcom,ib missing in DT node\n",
460 __func__);
461 goto fail1;
462 }
463 sns_ocmem_bus_vectors->ib = val;
464 ocmem_sns_bus_paths = kzalloc(sizeof(struct msm_bus_paths),
465 GFP_KERNEL);
466
467 if (!ocmem_sns_bus_paths) {
468 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
469 goto fail1;
470 }
471 ocmem_sns_bus_paths->num_paths = 1;
472 ocmem_sns_bus_paths->vectors = sns_ocmem_bus_vectors;
473
474 sns_ocmem_bus_scale_pdata =
475 kzalloc(sizeof(struct msm_bus_scale_pdata), GFP_KERNEL);
476 if (!sns_ocmem_bus_scale_pdata) {
477 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
478 goto fail2;
479 }
480
481 sns_ocmem_bus_scale_pdata->usecase = ocmem_sns_bus_paths;
482 sns_ocmem_bus_scale_pdata->num_usecases = 1;
483 sns_ocmem_bus_scale_pdata->name = "sensors-ocmem";
484
485 dev_set_drvdata(&pdev->dev, sns_ocmem_bus_scale_pdata);
486 return ret;
487
488fail2:
489 kfree(ocmem_sns_bus_paths);
490fail1:
491 kfree(sns_ocmem_bus_vectors);
492 return ret;
493}
494
495
Neeti Desaia07e87d2012-12-18 18:26:31 -0800496/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700497 * Initialize all sensors ocmem driver data fields and register with the
498 * ocmem driver.
499 *
500 * @return 0 upon success; < 0 upon error
501 */
502static int sns_ocmem_init(void)
503{
504 int i, err, ret;
505 struct sns_ocmem_hdr_s addr_req_hdr;
506 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
507
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700508 /* register from OCMEM callack */
509 sns_ctl.ocmem_handle =
510 ocmem_notifier_register(SNS_OCMEM_CLIENT_ID,
511 &sns_ctl.ocmem_nb);
512 if (sns_ctl.ocmem_handle == NULL) {
513 pr_err("OCMEM notifier registration failed\n");
514 return -EFAULT;
515 }
516
517 /* populate platform data */
518 ret = sns_ocmem_platform_data_populate(sns_ctl.pdev);
519 if (ret) {
520 dev_err(&sns_ctl.pdev->dev,
521 "%s: failed to populate platform data, rc = %d\n",
522 __func__, ret);
523 return -ENODEV;
524 }
525 sns_ocmem_bus_scale_pdata = dev_get_drvdata(&sns_ctl.pdev->dev);
526
527 sns_ctl.sns_ocmem_bus_client =
528 msm_bus_scale_register_client(sns_ocmem_bus_scale_pdata);
529
530 if (!sns_ctl.sns_ocmem_bus_client) {
531 pr_err("%s: msm_bus_scale_register_client() failed\n",
532 __func__);
533 return -EFAULT;
534 }
535
536 /* load ADSP first */
537 if (sns_load_adsp() != 0) {
538 pr_err("%s: sns_load_adsp failed\n", __func__);
539 return -EFAULT;
540 }
541
Neeti Desaia07e87d2012-12-18 18:26:31 -0800542 /*
543 * wait before open SMD channel from kernel to ensure
544 * channel has been openned already from ADSP side
545 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700546 msleep(1000);
547
548 err = smd_named_open_on_edge(SNS_OCMEM_SMD_CHANNEL,
549 SMD_APPS_QDSP,
550 &sns_ctl.smd_ch,
551 NULL,
552 sns_ocmem_smd_notify_data);
553 if (err != 0) {
554 pr_err("%s: smd_named_open_on_edge failed %i\n", __func__, err);
555 return -EFAULT;
556 }
557
558 pr_debug("%s: SMD channel openned successfuly!\n", __func__);
559 /* wait for the channel ready before writing data */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700560 msleep(1000);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700561 addr_req_hdr.msg_id = SNS_OCMEM_PHYS_ADDR_REQ_V01;
562 addr_req_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
563 addr_req_hdr.msg_size = 0;
564
565 err = sns_ocmem_send_msg(&addr_req_hdr, NULL);
566 if (err != 0) {
567 pr_err("%s: sns_ocmem_send_msg failed %i\n", __func__, err);
568 return -ECOMM;
569 }
570
571 err = sns_ocmem_wait(DSPS_PHYS_ADDR_SET, 0);
572 if (err != 0) {
573 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
574 return -EFAULT;
575 }
576
577 sns_ctl.map_list.num_chunks = sns_ctl.mem_segments_size;
578 for (i = 0; i < sns_ctl.mem_segments_size; i++) {
579 sns_ctl.map_list.chunks[i].ro =
580 sns_ctl.mem_segments[i].type == 1 ? true : false;
581 sns_ctl.map_list.chunks[i].ddr_paddr =
582 sns_ctl.mem_segments[i].start_address;
583 sns_ctl.map_list.chunks[i].size =
584 sns_ctl.mem_segments[i].size;
585
586 pr_debug("%s: chunks[%d]: ro=%d, ddr_paddr=0x%lx, size=%li",
587 __func__, i,
588 sns_ctl.map_list.chunks[i].ro,
589 sns_ctl.map_list.chunks[i].ddr_paddr,
590 sns_ctl.map_list.chunks[i].size);
591 }
592
593 return 0;
594}
595
Neeti Desaia07e87d2012-12-18 18:26:31 -0800596/*
597 * Unmaps memory in ocmem back to DDR, indicates to the ADSP its completion,
598 * and waits for it to finish removing its bandwidth vote.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700599 */
600static void sns_ocmem_unmap(void)
601{
602 unsigned long flags;
603 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700604
605 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
606 sns_ctl.buf, OCMEM_ON);
607
608 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
609 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_FAIL &
610 ~SNS_OCMEM_UNMAP_DONE);
611 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
612
613 err = ocmem_unmap(SNS_OCMEM_CLIENT_ID,
614 sns_ctl.buf,
615 &sns_ctl.map_list);
616
617 if (err != 0) {
618 pr_err("ocmem_unmap failed %i\n", err);
619 } else {
620 err = sns_ocmem_wait(SNS_OCMEM_UNMAP_DONE |
621 SNS_OCMEM_UNMAP_FAIL, 0);
622
623 if (err == 0) {
624 if (sns_ocmem_is_status_set(SNS_OCMEM_UNMAP_DONE))
625 pr_debug("%s: OCMEM_UNMAP_DONE\n", __func__);
626 else if (sns_ocmem_is_status_set(
627 SNS_OCMEM_UNMAP_FAIL)) {
628 pr_err("%s: OCMEM_UNMAP_FAIL\n", __func__);
629 BUG_ON(true);
630 } else
631 pr_err("%s: status flag not set\n", __func__);
632 } else {
633 pr_err("%s: sns_ocmem_wait failed %i\n",
634 __func__, err);
635 }
636 }
637
638 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
639 sns_ctl.buf, OCMEM_OFF);
640}
641
Neeti Desaia07e87d2012-12-18 18:26:31 -0800642/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700643 * Waits for allocation to succeed. This may take considerable time if the device
644 * is presently in a high-power use case.
645 *
646 * @return 0 on success; < 0 upon error
647 */
648static int sns_ocmem_wait_for_alloc(void)
649{
650 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700651
652 err = sns_ocmem_wait(SNS_OCMEM_ALLOC_GROW |
653 DSPS_HAS_NO_CLIENT, 0);
654
655 if (err == 0) {
656 if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
657 pr_debug("%s: Lost client while waiting for GROW\n",
658 __func__);
659 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
660 sns_ctl.buf = NULL;
661 return -EPIPE;
662 }
663 } else {
664 pr_err("sns_ocmem_wait failed %i\n", err);
665 return -EFAULT;
666 }
667
668 return 0;
669}
670
Neeti Desaia07e87d2012-12-18 18:26:31 -0800671/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700672 * Kicks-off the mapping of memory from DDR to ocmem. Waits for the process
673 * to complete, then indicates so to the ADSP.
674 *
675 * @return 0: Success; < 0: Other error
676 */
677static int sns_ocmem_map(void)
678{
679 int err = 0;
680 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700681
682 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
683 sns_ctl.sns_ocmem_status &=
684 (~SNS_OCMEM_MAP_FAIL & ~SNS_OCMEM_MAP_DONE);
685 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
686
687 /* vote for ocmem bus bandwidth */
688 err = msm_bus_scale_client_update_request(
689 sns_ctl.sns_ocmem_bus_client,
690 0);
691 if (err)
692 pr_err("%s: failed to vote for bus bandwidth\n", __func__);
693
694 err = ocmem_map(SNS_OCMEM_CLIENT_ID,
695 sns_ctl.buf,
696 &sns_ctl.map_list);
697
698 if (err != 0) {
699 pr_debug("ocmem_map failed %i\n", err);
700 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
701 sns_ctl.buf, OCMEM_OFF);
702 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
703 sns_ctl.buf = NULL;
704 } else {
705 err = sns_ocmem_wait(SNS_OCMEM_ALLOC_SHRINK |
706 DSPS_HAS_NO_CLIENT |
707 SNS_OCMEM_MAP_DONE |
708 SNS_OCMEM_MAP_FAIL, 0);
709
710 if (err == 0) {
711 if (sns_ocmem_is_status_set(SNS_OCMEM_MAP_DONE))
712 pr_debug("%s: OCMEM mapping DONE\n", __func__);
713 else if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
714 pr_debug("%s: Lost client while waiting for MAP\n",
715 __func__);
716 sns_ocmem_unmap();
717 ocmem_free(SNS_OCMEM_CLIENT_ID,
718 sns_ctl.buf);
719 sns_ctl.buf = NULL;
720 err = -EPIPE;
721 } else if (sns_ocmem_is_status_set(
722 SNS_OCMEM_ALLOC_SHRINK)) {
723 pr_debug("%s: SHRINK while wait for MAP\n",
724 __func__);
725 sns_ocmem_unmap();
726 err = ocmem_shrink(SNS_OCMEM_CLIENT_ID,
727 sns_ctl.buf, 0);
728 BUG_ON(err != 0);
729 err = -EFAULT;
730 } else if (sns_ocmem_is_status_set(
731 SNS_OCMEM_MAP_FAIL)) {
732 pr_err("%s: OCMEM mapping fails\n", __func__);
733 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
734 sns_ctl.buf,
735 OCMEM_OFF);
736 ocmem_free(SNS_OCMEM_CLIENT_ID,
737 sns_ctl.buf);
738 sns_ctl.buf = NULL;
739 } else
740 pr_err("%s: status flag not set\n", __func__);
741 } else {
742 pr_err("sns_ocmem_wait failed %i\n", err);
743 }
744 }
745
746 return err;
747}
748
Neeti Desaia07e87d2012-12-18 18:26:31 -0800749/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700750 * Allocates memory in ocmem and maps to it from DDR.
751 *
752 * @return 0 upon success; <0 upon failure;
753 */
754static int sns_ocmem_alloc(void)
755{
756 int err = 0;
757 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700758
759 if (sns_ctl.buf == NULL) {
760 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
761 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_GROW &
762 ~SNS_OCMEM_ALLOC_SHRINK;
763 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
764 sns_ctl.buf = ocmem_allocate_nb(SNS_OCMEM_CLIENT_ID,
765 SNS_OCMEM_SIZE);
766
767 if (sns_ctl.buf == NULL) {
768 pr_err("ocmem_allocate_nb returned NULL\n");
769 sns_ctl.ocmem_enabled = false;
770 err = -EFAULT;
771 } else if (sns_ctl.buf->len != 0 &&
772 SNS_OCMEM_SIZE > sns_ctl.buf->len) {
773 pr_err("ocmem_allocate_nb: invalid len %li, Req: %i)\n",
774 sns_ctl.buf->len, SNS_OCMEM_SIZE);
775 sns_ctl.ocmem_enabled = false;
776 err = -EFAULT;
777 }
778 }
779
780 pr_debug("%s OCMEM buf=%lx, buffer len=%li\n", __func__,
781 sns_ctl.buf->addr, sns_ctl.buf->len);
782
783 while (sns_ctl.ocmem_enabled) {
784 if (sns_ctl.buf->len == 0) {
785 pr_debug("%s: Waiting for memory allocation\n",
786 __func__);
787 err = sns_ocmem_wait_for_alloc();
788 if (err == -EPIPE) {
789 pr_debug("%s:Lost client while wait for alloc\n",
790 __func__);
791 break;
792 } else if (err != 0) {
793 pr_err("sns_ocmem_wait_for_alloc failed %i\n",
794 err);
795 break;
796 }
797 }
798
799 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
800 sns_ctl.buf,
801 OCMEM_ON);
802
803 err = sns_ocmem_map();
804
805 if (err == -EPIPE) {
806 pr_debug("%s: Lost client while waiting for mapping\n",
807 __func__);
808 break;
809 } else if (err < 0) {
810 pr_debug("%s: Mapping failed, will try again\n",
811 __func__);
812 break;
813 } else if (err == 0) {
814 pr_debug("%s: Mapping finished\n", __func__);
815 break;
816 }
817 }
818
819 return err;
820}
821
Neeti Desaia07e87d2012-12-18 18:26:31 -0800822/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700823 * Indicate to the ADSP that unmapping has completed, and wait for the response
824 * that its bandwidth vote has been removed.
825 *
826 * @return 0 Upon success; < 0 upon error
827 */
828static int sns_ocmem_unmap_send(void)
829{
830 int err;
831 struct sns_ocmem_hdr_s msg_hdr;
832 struct sns_ocmem_bw_vote_req_msg_v01 msg;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700833
834 memset(&msg, 0, sizeof(struct sns_ocmem_bw_vote_req_msg_v01));
835
836 msg_hdr.msg_id = SNS_OCMEM_BW_VOTE_REQ_V01;
837 msg_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
838 msg_hdr.msg_size = sizeof(struct sns_ocmem_bw_vote_req_msg_v01);
839 msg.is_map = 0;
840 msg.vectors_valid = 0;
841 msg.vectors_len = 0;
842
843 pr_debug("%s: send bw_vote OFF\n", __func__);
844 err = sns_ocmem_send_msg(&msg_hdr, &msg);
845 if (err != 0) {
846 pr_err("%s: sns_ocmem_send_msg failed %i\n",
847 __func__, err);
848 } else {
849 err = sns_ocmem_wait(DSPS_BW_VOTE_OFF, 0);
850 if (err != 0)
851 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
852 }
853
854 return err;
855}
856
Neeti Desaia07e87d2012-12-18 18:26:31 -0800857/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700858 * Indicate to the ADSP that mapping has completed, and wait for the response
859 * that its bandwidth vote has been made.
860 *
861 * @return 0 Upon success; < 0 upon error
862 */
863static int sns_ocmem_map_send(void)
864{
865 int err;
866 struct sns_ocmem_hdr_s msg_hdr;
867 struct sns_ocmem_bw_vote_req_msg_v01 msg;
868 struct ocmem_vectors *vectors;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700869
870 memset(&msg, 0, sizeof(struct sns_ocmem_bw_vote_req_msg_v01));
871
872 msg_hdr.msg_id = SNS_OCMEM_BW_VOTE_REQ_V01;
873 msg_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
874 msg_hdr.msg_size = sizeof(struct sns_ocmem_bw_vote_req_msg_v01);
875 msg.is_map = 1;
876
877 vectors = ocmem_get_vectors(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
878 if ((vectors != NULL)) {
Satya Durga Srinivasu Prabhalaf38015e2013-10-29 13:50:42 -0700879 memcpy(&msg.vectors, vectors, sizeof(*vectors));
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700880 /* TODO: set vectors_len */
881 msg.vectors_valid = true;
882 msg.vectors_len = 0;
883 }
884
885 pr_debug("%s: send bw_vote ON\n", __func__);
886 err = sns_ocmem_send_msg(&msg_hdr, &msg);
887 if (err != 0) {
888 pr_err("%s: sns_ocmem_send_msg failed %i\n", __func__, err);
889 } else {
890 err = sns_ocmem_wait(DSPS_BW_VOTE_ON |
891 SNS_OCMEM_ALLOC_SHRINK, 0);
892 if (err != 0)
893 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
894 }
895
896 return err;
897}
898
Neeti Desaia07e87d2012-12-18 18:26:31 -0800899/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700900 * Perform the encessary operations to clean-up OCMEM after being notified that
901 * there is no longer a client; if sensors was evicted; or if some error
902 * has occurred.
903 *
904 * @param[i] do_free Whether the memory should be freed (true) or if shrink
905 * should be called instead (false).
906 */
907static void sns_ocmem_evicted(bool do_free)
908{
909 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700910
911 sns_ocmem_unmap();
912 if (do_free) {
913 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
914 sns_ctl.buf = NULL;
915 } else {
916 err = ocmem_shrink(SNS_OCMEM_CLIENT_ID, sns_ctl.buf, 0);
917 BUG_ON(err != 0);
918 }
919
920 err = sns_ocmem_unmap_send();
921 if (err != 0)
922 pr_err("sns_ocmem_unmap_send failed %i\n", err);
923}
924
Neeti Desaia07e87d2012-12-18 18:26:31 -0800925/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700926 * After mapping has completed and the ADSP has reacted appropriately, wait
927 * for a shrink command or word from the ADSP that it no longer has a client.
928 *
929 * @return 0 If no clients; < 0 upon error;
930 */
931static int sns_ocmem_map_done(void)
932{
933 int err = 0;
934 unsigned long flags;
935
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700936 err = sns_ocmem_map_send();
937 if (err != 0) {
938 pr_err("sns_ocmem_map_send failed %i\n", err);
939 sns_ocmem_evicted(true);
940 } else {
941 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
942 sns_ctl.buf, OCMEM_OFF);
943
944 pr_debug("%s: Waiting for shrink or 'no client' updates\n",
945 __func__);
946 err = sns_ocmem_wait(DSPS_HAS_NO_CLIENT |
947 SNS_OCMEM_ALLOC_SHRINK, 0);
948 if (err == 0) {
949 if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
950 pr_debug("%s: No longer have a client\n",
951 __func__);
952 sns_ocmem_evicted(true);
953 } else if (sns_ocmem_is_status_set(
954 SNS_OCMEM_ALLOC_SHRINK)) {
955 pr_debug("%s: Received SHRINK\n", __func__);
956 sns_ocmem_evicted(false);
957
958 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
959 sns_ctl.sns_ocmem_status &=
960 ~SNS_OCMEM_ALLOC_SHRINK;
961 spin_unlock_irqrestore(&sns_ctl.sns_lock,
962 flags);
963 err = -EFAULT;
964 }
965 } else {
966 pr_err("sns_ocmem_wait failed %i\n", err);
967 }
968 }
969
970 return err;
971}
972
Neeti Desaia07e87d2012-12-18 18:26:31 -0800973/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700974 * Main function.
975 * Initializes sensors ocmem feature, and waits for an ADSP client.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700976 */
977static void sns_ocmem_main(struct work_struct *work)
978{
979 int err = 0;
980 pr_debug("%s\n", __func__);
981
982 err = sns_ocmem_init();
983 if (err != 0) {
984 pr_err("%s: sns_ocmem_init failed %i\n", __func__, err);
985 return;
986 }
987
988 while (true) {
989 pr_debug("%s: Waiting for sensor client\n", __func__);
990 if (sns_ocmem_is_status_set(DSPS_HAS_CLIENT) ||
991 !sns_ocmem_wait(DSPS_HAS_CLIENT, 0)) {
992 pr_debug("%s: DSPS_HAS_CLIENT\n", __func__);
993
994 err = sns_ocmem_alloc();
995 if (err != 0) {
996 pr_err("sns_ocmem_alloc failed %i\n", err);
997 return;
998 } else {
999 err = sns_ocmem_map_done();
1000 if (err != 0) {
1001 pr_err("sns_ocmem_map_done failed %i",
1002 err);
1003 return;
1004 }
1005 }
1006 }
1007 }
1008
1009 ocmem_notifier_unregister(sns_ctl.ocmem_handle,
1010 &sns_ctl.ocmem_nb);
1011}
1012
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001013static int sensors_adsp_open(struct inode *ip, struct file *fp)
1014{
1015 int ret = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001016 return ret;
1017}
1018
1019static int sensors_adsp_release(struct inode *inode, struct file *file)
1020{
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001021 return 0;
1022}
1023
Neeti Desaia07e87d2012-12-18 18:26:31 -08001024/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001025 * Read QTimer clock ticks and scale down to 32KHz clock as used
1026 * in DSPS
1027 */
1028static u32 sns_read_qtimer(void)
1029{
1030 u64 val;
1031 val = arch_counter_get_cntpct();
1032 /*
1033 * To convert ticks from 19.2 Mhz clock to 32768 Hz clock:
1034 * x = (value * 32768) / 19200000
1035 * This is same as first left shift the value by 4 bits, i.e. mutiply
1036 * by 16, and then divide by 9375. The latter is preferable since
1037 * QTimer tick (value) is 56-bit, so (value * 32768) could overflow,
1038 * while (value * 16) will never do
1039 */
1040 val <<= 4;
1041 do_div(val, 9375);
1042
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001043 return (u32)val;
1044}
1045
Neeti Desaia07e87d2012-12-18 18:26:31 -08001046/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001047 * IO Control - handle commands from client.
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001048 */
1049static long sensors_adsp_ioctl(struct file *file,
1050 unsigned int cmd, unsigned long arg)
1051{
1052 int ret = 0;
1053 u32 val = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001054
1055 switch (cmd) {
1056 case DSPS_IOCTL_READ_SLOW_TIMER:
1057 val = sns_read_qtimer();
1058 ret = put_user(val, (u32 __user *) arg);
1059 break;
1060
1061 default:
1062 ret = -EINVAL;
1063 break;
1064 }
1065
1066 return ret;
1067}
1068
Neeti Desaia07e87d2012-12-18 18:26:31 -08001069/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001070 * platform driver
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001071 */
1072const struct file_operations sensors_adsp_fops = {
1073 .owner = THIS_MODULE,
1074 .open = sensors_adsp_open,
1075 .release = sensors_adsp_release,
1076 .unlocked_ioctl = sensors_adsp_ioctl,
1077};
1078
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001079static int sensors_adsp_probe(struct platform_device *pdev)
1080{
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001081 int ret = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001082 sns_ctl.dev_class = class_create(THIS_MODULE, DRV_NAME);
1083 if (sns_ctl.dev_class == NULL) {
1084 pr_err("%s: class_create fail.\n", __func__);
1085 goto res_err;
1086 }
1087
1088 ret = alloc_chrdev_region(&sns_ctl.dev_num, 0, 1, DRV_NAME);
1089 if (ret) {
1090 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
1091 goto alloc_chrdev_region_err;
1092 }
1093
1094 sns_ctl.dev = device_create(sns_ctl.dev_class, NULL,
1095 sns_ctl.dev_num,
1096 &sns_ctl, DRV_NAME);
1097 if (IS_ERR(sns_ctl.dev)) {
1098 pr_err("%s: device_create fail.\n", __func__);
1099 goto device_create_err;
1100 }
1101
1102 sns_ctl.cdev = cdev_alloc();
1103 if (sns_ctl.cdev == NULL) {
1104 pr_err("%s: cdev_alloc fail.\n", __func__);
1105 goto cdev_alloc_err;
1106 }
1107 cdev_init(sns_ctl.cdev, &sensors_adsp_fops);
1108 sns_ctl.cdev->owner = THIS_MODULE;
1109
1110 ret = cdev_add(sns_ctl.cdev, sns_ctl.dev_num, 1);
1111 if (ret) {
1112 pr_err("%s: cdev_add fail.\n", __func__);
1113 goto cdev_add_err;
1114 }
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001115
1116 sns_ctl.sns_workqueue =
1117 alloc_workqueue("sns_ocmem", WQ_NON_REENTRANT, 0);
1118 if (!sns_ctl.sns_workqueue) {
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001119 pr_err("%s: Failed to create work queue\n",
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001120 __func__);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001121 goto cdev_add_err;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001122 }
1123
Neeti Desaia07e87d2012-12-18 18:26:31 -08001124 sns_ctl.smd_wq =
1125 alloc_workqueue("smd_wq", WQ_NON_REENTRANT, 0);
1126 if (!sns_ctl.smd_wq) {
1127 pr_err("%s: Failed to create work queue\n",
1128 __func__);
1129 goto cdev_add_err;
1130 }
1131
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001132 init_waitqueue_head(&sns_ctl.sns_wait);
1133 spin_lock_init(&sns_ctl.sns_lock);
1134
1135 sns_ctl.ocmem_handle = NULL;
1136 sns_ctl.buf = NULL;
1137 sns_ctl.sns_ocmem_status = 0;
1138 sns_ctl.ocmem_enabled = true;
1139 sns_ctl.ocmem_nb.notifier_call = sns_ocmem_drv_cb;
1140 sns_ctl.smd_ch = NULL;
1141 sns_ctl.pdev = pdev;
1142
1143 INIT_WORK(&sns_ctl.sns_work, sns_ocmem_main);
Neeti Desaia07e87d2012-12-18 18:26:31 -08001144 INIT_WORK(&sns_ctl.smd_read_work, sns_ocmem_smd_read);
1145
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001146 queue_work(sns_ctl.sns_workqueue, &sns_ctl.sns_work);
1147
1148 return 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001149
1150cdev_add_err:
1151 kfree(sns_ctl.cdev);
1152cdev_alloc_err:
1153 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
1154device_create_err:
1155 unregister_chrdev_region(sns_ctl.dev_num, 1);
1156alloc_chrdev_region_err:
1157 class_destroy(sns_ctl.dev_class);
1158res_err:
1159 return -ENODEV;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001160}
1161
1162static int sensors_adsp_remove(struct platform_device *pdev)
1163{
1164 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001165
1166 sns_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
1167 dev_get_drvdata(&pdev->dev);
1168
1169 kfree(sns_ocmem_bus_scale_pdata->usecase->vectors);
1170 kfree(sns_ocmem_bus_scale_pdata->usecase);
1171 kfree(sns_ocmem_bus_scale_pdata);
1172
1173 ocmem_notifier_unregister(sns_ctl.ocmem_handle,
1174 &sns_ctl.ocmem_nb);
1175 destroy_workqueue(sns_ctl.sns_workqueue);
Neeti Desaia07e87d2012-12-18 18:26:31 -08001176 destroy_workqueue(sns_ctl.smd_wq);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001177
1178 cdev_del(sns_ctl.cdev);
1179 kfree(sns_ctl.cdev);
1180 sns_ctl.cdev = NULL;
1181 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
1182 unregister_chrdev_region(sns_ctl.dev_num, 1);
1183 class_destroy(sns_ctl.dev_class);
1184
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001185 return 0;
1186}
1187
1188static const struct of_device_id msm_adsp_sensors_dt_match[] = {
1189 {.compatible = "qcom,msm-adsp-sensors"},
1190 {}
1191};
1192MODULE_DEVICE_TABLE(of, msm_adsp_sensors_dt_match);
1193
1194
1195static struct platform_driver sensors_adsp_driver = {
1196 .driver = {
1197 .name = "sensors-adsp",
1198 .owner = THIS_MODULE,
1199 .of_match_table = msm_adsp_sensors_dt_match,
1200 },
1201 .probe = sensors_adsp_probe,
1202 .remove = sensors_adsp_remove,
1203};
1204
Neeti Desaia07e87d2012-12-18 18:26:31 -08001205/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001206 * Module Init.
1207 */
1208static int sensors_adsp_init(void)
1209{
1210 int rc;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001211 pr_debug("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
1212
1213 rc = platform_driver_register(&sensors_adsp_driver);
1214
1215 if (rc) {
1216 pr_err("%s: Failed to register sensors adsp driver\n",
1217 __func__);
1218 return rc;
1219 }
1220
1221 return 0;
1222}
1223
Neeti Desaia07e87d2012-12-18 18:26:31 -08001224/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001225 * Module Exit.
1226 */
1227static void sensors_adsp_exit(void)
1228{
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001229 platform_driver_unregister(&sensors_adsp_driver);
1230}
1231
1232module_init(sensors_adsp_init);
1233module_exit(sensors_adsp_exit);
1234
1235MODULE_LICENSE("GPL v2");
1236MODULE_DESCRIPTION("Sensors ADSP driver");