blob: eb0026c635d79f5e7e615d732f255a38d2969333 [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;
358 }
359
360 hdr->dst_module = SNS_OCMEM_MODULE_ADSP;
361 hdr->src_module = SNS_OCMEM_MODULE_KERNEL;
362
363 memcpy(temp, hdr, sizeof(struct sns_ocmem_hdr_s));
364 memcpy((char *)temp + sizeof(struct sns_ocmem_hdr_s),
365 msg_ptr, hdr->msg_size);
366 pr_debug("%s: send msg type: %i size: %i id: %i dst: %i src: %i\n",
367 __func__, hdr->msg_type, hdr->msg_size,
368 hdr->msg_id, hdr->dst_module, hdr->src_module);
369
370 if (hdr == NULL) {
371 pr_err("%s: NULL message header\n", __func__);
372 rv = -EINVAL;
373 } else {
374 if (sns_ctl.smd_ch == NULL) {
375 pr_err("%s: null smd_ch\n", __func__);
376 rv = -EINVAL;
377 }
378 err = smd_write(sns_ctl.smd_ch, temp, size);
379 if (err < 0) {
380 pr_err("%s: smd_write failed %i\n", __func__, err);
381 rv = -ECOMM;
382 } else {
383 pr_debug("%s smd_write successful ret=%d\n",
384 __func__, err);
385 }
386 }
387
388 kfree(temp);
389
390 return rv;
391}
392
Neeti Desaia07e87d2012-12-18 18:26:31 -0800393/*
394 * Load ADSP Firmware.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700395 */
396
397static int sns_load_adsp(void)
398{
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700399 sns_ctl.pil = subsystem_get("adsp");
400 if (IS_ERR(sns_ctl.pil)) {
401 pr_err("%s: fail to load ADSP firmware\n", __func__);
402 return -ENODEV;
403 }
404
405 pr_debug("%s: Q6/ADSP image is loaded\n", __func__);
406
407 return 0;
408}
409
410static int sns_ocmem_platform_data_populate(struct platform_device *pdev)
411{
412 int ret;
413 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
414 struct msm_bus_vectors *sns_ocmem_bus_vectors = NULL;
415 struct msm_bus_paths *ocmem_sns_bus_paths = NULL;
416 u32 val;
417
418 if (!pdev->dev.of_node) {
419 pr_err("%s: device tree information missing\n", __func__);
420 return -ENODEV;
421 }
422
423 sns_ocmem_bus_vectors = kzalloc(sizeof(struct msm_bus_vectors),
424 GFP_KERNEL);
425 if (!sns_ocmem_bus_vectors) {
426 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
427 return -ENOMEM;
428 }
429
430 ret = of_property_read_u32(pdev->dev.of_node,
431 "qcom,src-id", &val);
432 if (ret) {
433 dev_err(&pdev->dev, "%s: qcom,src-id missing in DT node\n",
434 __func__);
435 goto fail1;
436 }
437 sns_ocmem_bus_vectors->src = val;
438 ret = of_property_read_u32(pdev->dev.of_node,
439 "qcom,dst-id", &val);
440 if (ret) {
441 dev_err(&pdev->dev, "%s: qcom,dst-id missing in DT node\n",
442 __func__);
443 goto fail1;
444 }
445 sns_ocmem_bus_vectors->dst = val;
446 ret = of_property_read_u32(pdev->dev.of_node,
447 "qcom,ab", &val);
448 if (ret) {
449 dev_err(&pdev->dev, "%s: qcom,ab missing in DT node\n",
450 __func__);
451 goto fail1;
452 }
453 sns_ocmem_bus_vectors->ab = val;
454 ret = of_property_read_u32(pdev->dev.of_node,
455 "qcom,ib", &val);
456 if (ret) {
457 dev_err(&pdev->dev, "%s: qcom,ib missing in DT node\n",
458 __func__);
459 goto fail1;
460 }
461 sns_ocmem_bus_vectors->ib = val;
462 ocmem_sns_bus_paths = kzalloc(sizeof(struct msm_bus_paths),
463 GFP_KERNEL);
464
465 if (!ocmem_sns_bus_paths) {
466 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
467 goto fail1;
468 }
469 ocmem_sns_bus_paths->num_paths = 1;
470 ocmem_sns_bus_paths->vectors = sns_ocmem_bus_vectors;
471
472 sns_ocmem_bus_scale_pdata =
473 kzalloc(sizeof(struct msm_bus_scale_pdata), GFP_KERNEL);
474 if (!sns_ocmem_bus_scale_pdata) {
475 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
476 goto fail2;
477 }
478
479 sns_ocmem_bus_scale_pdata->usecase = ocmem_sns_bus_paths;
480 sns_ocmem_bus_scale_pdata->num_usecases = 1;
481 sns_ocmem_bus_scale_pdata->name = "sensors-ocmem";
482
483 dev_set_drvdata(&pdev->dev, sns_ocmem_bus_scale_pdata);
484 return ret;
485
486fail2:
487 kfree(ocmem_sns_bus_paths);
488fail1:
489 kfree(sns_ocmem_bus_vectors);
490 return ret;
491}
492
493
Neeti Desaia07e87d2012-12-18 18:26:31 -0800494/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700495 * Initialize all sensors ocmem driver data fields and register with the
496 * ocmem driver.
497 *
498 * @return 0 upon success; < 0 upon error
499 */
500static int sns_ocmem_init(void)
501{
502 int i, err, ret;
503 struct sns_ocmem_hdr_s addr_req_hdr;
504 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
505
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700506 /* register from OCMEM callack */
507 sns_ctl.ocmem_handle =
508 ocmem_notifier_register(SNS_OCMEM_CLIENT_ID,
509 &sns_ctl.ocmem_nb);
510 if (sns_ctl.ocmem_handle == NULL) {
511 pr_err("OCMEM notifier registration failed\n");
512 return -EFAULT;
513 }
514
515 /* populate platform data */
516 ret = sns_ocmem_platform_data_populate(sns_ctl.pdev);
517 if (ret) {
518 dev_err(&sns_ctl.pdev->dev,
519 "%s: failed to populate platform data, rc = %d\n",
520 __func__, ret);
521 return -ENODEV;
522 }
523 sns_ocmem_bus_scale_pdata = dev_get_drvdata(&sns_ctl.pdev->dev);
524
525 sns_ctl.sns_ocmem_bus_client =
526 msm_bus_scale_register_client(sns_ocmem_bus_scale_pdata);
527
528 if (!sns_ctl.sns_ocmem_bus_client) {
529 pr_err("%s: msm_bus_scale_register_client() failed\n",
530 __func__);
531 return -EFAULT;
532 }
533
534 /* load ADSP first */
535 if (sns_load_adsp() != 0) {
536 pr_err("%s: sns_load_adsp failed\n", __func__);
537 return -EFAULT;
538 }
539
Neeti Desaia07e87d2012-12-18 18:26:31 -0800540 /*
541 * wait before open SMD channel from kernel to ensure
542 * channel has been openned already from ADSP side
543 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700544 msleep(1000);
545
546 err = smd_named_open_on_edge(SNS_OCMEM_SMD_CHANNEL,
547 SMD_APPS_QDSP,
548 &sns_ctl.smd_ch,
549 NULL,
550 sns_ocmem_smd_notify_data);
551 if (err != 0) {
552 pr_err("%s: smd_named_open_on_edge failed %i\n", __func__, err);
553 return -EFAULT;
554 }
555
556 pr_debug("%s: SMD channel openned successfuly!\n", __func__);
557 /* wait for the channel ready before writing data */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700558 msleep(1000);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700559 addr_req_hdr.msg_id = SNS_OCMEM_PHYS_ADDR_REQ_V01;
560 addr_req_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
561 addr_req_hdr.msg_size = 0;
562
563 err = sns_ocmem_send_msg(&addr_req_hdr, NULL);
564 if (err != 0) {
565 pr_err("%s: sns_ocmem_send_msg failed %i\n", __func__, err);
566 return -ECOMM;
567 }
568
569 err = sns_ocmem_wait(DSPS_PHYS_ADDR_SET, 0);
570 if (err != 0) {
571 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
572 return -EFAULT;
573 }
574
575 sns_ctl.map_list.num_chunks = sns_ctl.mem_segments_size;
576 for (i = 0; i < sns_ctl.mem_segments_size; i++) {
577 sns_ctl.map_list.chunks[i].ro =
578 sns_ctl.mem_segments[i].type == 1 ? true : false;
579 sns_ctl.map_list.chunks[i].ddr_paddr =
580 sns_ctl.mem_segments[i].start_address;
581 sns_ctl.map_list.chunks[i].size =
582 sns_ctl.mem_segments[i].size;
583
584 pr_debug("%s: chunks[%d]: ro=%d, ddr_paddr=0x%lx, size=%li",
585 __func__, i,
586 sns_ctl.map_list.chunks[i].ro,
587 sns_ctl.map_list.chunks[i].ddr_paddr,
588 sns_ctl.map_list.chunks[i].size);
589 }
590
591 return 0;
592}
593
Neeti Desaia07e87d2012-12-18 18:26:31 -0800594/*
595 * Unmaps memory in ocmem back to DDR, indicates to the ADSP its completion,
596 * and waits for it to finish removing its bandwidth vote.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700597 */
598static void sns_ocmem_unmap(void)
599{
600 unsigned long flags;
601 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700602
603 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
604 sns_ctl.buf, OCMEM_ON);
605
606 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
607 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_FAIL &
608 ~SNS_OCMEM_UNMAP_DONE);
609 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
610
611 err = ocmem_unmap(SNS_OCMEM_CLIENT_ID,
612 sns_ctl.buf,
613 &sns_ctl.map_list);
614
615 if (err != 0) {
616 pr_err("ocmem_unmap failed %i\n", err);
617 } else {
618 err = sns_ocmem_wait(SNS_OCMEM_UNMAP_DONE |
619 SNS_OCMEM_UNMAP_FAIL, 0);
620
621 if (err == 0) {
622 if (sns_ocmem_is_status_set(SNS_OCMEM_UNMAP_DONE))
623 pr_debug("%s: OCMEM_UNMAP_DONE\n", __func__);
624 else if (sns_ocmem_is_status_set(
625 SNS_OCMEM_UNMAP_FAIL)) {
626 pr_err("%s: OCMEM_UNMAP_FAIL\n", __func__);
627 BUG_ON(true);
628 } else
629 pr_err("%s: status flag not set\n", __func__);
630 } else {
631 pr_err("%s: sns_ocmem_wait failed %i\n",
632 __func__, err);
633 }
634 }
635
636 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
637 sns_ctl.buf, OCMEM_OFF);
638}
639
Neeti Desaia07e87d2012-12-18 18:26:31 -0800640/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700641 * Waits for allocation to succeed. This may take considerable time if the device
642 * is presently in a high-power use case.
643 *
644 * @return 0 on success; < 0 upon error
645 */
646static int sns_ocmem_wait_for_alloc(void)
647{
648 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700649
650 err = sns_ocmem_wait(SNS_OCMEM_ALLOC_GROW |
651 DSPS_HAS_NO_CLIENT, 0);
652
653 if (err == 0) {
654 if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
655 pr_debug("%s: Lost client while waiting for GROW\n",
656 __func__);
657 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
658 sns_ctl.buf = NULL;
659 return -EPIPE;
660 }
661 } else {
662 pr_err("sns_ocmem_wait failed %i\n", err);
663 return -EFAULT;
664 }
665
666 return 0;
667}
668
Neeti Desaia07e87d2012-12-18 18:26:31 -0800669/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700670 * Kicks-off the mapping of memory from DDR to ocmem. Waits for the process
671 * to complete, then indicates so to the ADSP.
672 *
673 * @return 0: Success; < 0: Other error
674 */
675static int sns_ocmem_map(void)
676{
677 int err = 0;
678 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700679
680 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
681 sns_ctl.sns_ocmem_status &=
682 (~SNS_OCMEM_MAP_FAIL & ~SNS_OCMEM_MAP_DONE);
683 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
684
685 /* vote for ocmem bus bandwidth */
686 err = msm_bus_scale_client_update_request(
687 sns_ctl.sns_ocmem_bus_client,
688 0);
689 if (err)
690 pr_err("%s: failed to vote for bus bandwidth\n", __func__);
691
692 err = ocmem_map(SNS_OCMEM_CLIENT_ID,
693 sns_ctl.buf,
694 &sns_ctl.map_list);
695
696 if (err != 0) {
697 pr_debug("ocmem_map failed %i\n", err);
698 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
699 sns_ctl.buf, OCMEM_OFF);
700 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
701 sns_ctl.buf = NULL;
702 } else {
703 err = sns_ocmem_wait(SNS_OCMEM_ALLOC_SHRINK |
704 DSPS_HAS_NO_CLIENT |
705 SNS_OCMEM_MAP_DONE |
706 SNS_OCMEM_MAP_FAIL, 0);
707
708 if (err == 0) {
709 if (sns_ocmem_is_status_set(SNS_OCMEM_MAP_DONE))
710 pr_debug("%s: OCMEM mapping DONE\n", __func__);
711 else if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
712 pr_debug("%s: Lost client while waiting for MAP\n",
713 __func__);
714 sns_ocmem_unmap();
715 ocmem_free(SNS_OCMEM_CLIENT_ID,
716 sns_ctl.buf);
717 sns_ctl.buf = NULL;
718 err = -EPIPE;
719 } else if (sns_ocmem_is_status_set(
720 SNS_OCMEM_ALLOC_SHRINK)) {
721 pr_debug("%s: SHRINK while wait for MAP\n",
722 __func__);
723 sns_ocmem_unmap();
724 err = ocmem_shrink(SNS_OCMEM_CLIENT_ID,
725 sns_ctl.buf, 0);
726 BUG_ON(err != 0);
727 err = -EFAULT;
728 } else if (sns_ocmem_is_status_set(
729 SNS_OCMEM_MAP_FAIL)) {
730 pr_err("%s: OCMEM mapping fails\n", __func__);
731 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
732 sns_ctl.buf,
733 OCMEM_OFF);
734 ocmem_free(SNS_OCMEM_CLIENT_ID,
735 sns_ctl.buf);
736 sns_ctl.buf = NULL;
737 } else
738 pr_err("%s: status flag not set\n", __func__);
739 } else {
740 pr_err("sns_ocmem_wait failed %i\n", err);
741 }
742 }
743
744 return err;
745}
746
Neeti Desaia07e87d2012-12-18 18:26:31 -0800747/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700748 * Allocates memory in ocmem and maps to it from DDR.
749 *
750 * @return 0 upon success; <0 upon failure;
751 */
752static int sns_ocmem_alloc(void)
753{
754 int err = 0;
755 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700756
757 if (sns_ctl.buf == NULL) {
758 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
759 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_GROW &
760 ~SNS_OCMEM_ALLOC_SHRINK;
761 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
762 sns_ctl.buf = ocmem_allocate_nb(SNS_OCMEM_CLIENT_ID,
763 SNS_OCMEM_SIZE);
764
765 if (sns_ctl.buf == NULL) {
766 pr_err("ocmem_allocate_nb returned NULL\n");
767 sns_ctl.ocmem_enabled = false;
768 err = -EFAULT;
769 } else if (sns_ctl.buf->len != 0 &&
770 SNS_OCMEM_SIZE > sns_ctl.buf->len) {
771 pr_err("ocmem_allocate_nb: invalid len %li, Req: %i)\n",
772 sns_ctl.buf->len, SNS_OCMEM_SIZE);
773 sns_ctl.ocmem_enabled = false;
774 err = -EFAULT;
775 }
776 }
777
778 pr_debug("%s OCMEM buf=%lx, buffer len=%li\n", __func__,
779 sns_ctl.buf->addr, sns_ctl.buf->len);
780
781 while (sns_ctl.ocmem_enabled) {
782 if (sns_ctl.buf->len == 0) {
783 pr_debug("%s: Waiting for memory allocation\n",
784 __func__);
785 err = sns_ocmem_wait_for_alloc();
786 if (err == -EPIPE) {
787 pr_debug("%s:Lost client while wait for alloc\n",
788 __func__);
789 break;
790 } else if (err != 0) {
791 pr_err("sns_ocmem_wait_for_alloc failed %i\n",
792 err);
793 break;
794 }
795 }
796
797 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
798 sns_ctl.buf,
799 OCMEM_ON);
800
801 err = sns_ocmem_map();
802
803 if (err == -EPIPE) {
804 pr_debug("%s: Lost client while waiting for mapping\n",
805 __func__);
806 break;
807 } else if (err < 0) {
808 pr_debug("%s: Mapping failed, will try again\n",
809 __func__);
810 break;
811 } else if (err == 0) {
812 pr_debug("%s: Mapping finished\n", __func__);
813 break;
814 }
815 }
816
817 return err;
818}
819
Neeti Desaia07e87d2012-12-18 18:26:31 -0800820/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700821 * Indicate to the ADSP that unmapping has completed, and wait for the response
822 * that its bandwidth vote has been removed.
823 *
824 * @return 0 Upon success; < 0 upon error
825 */
826static int sns_ocmem_unmap_send(void)
827{
828 int err;
829 struct sns_ocmem_hdr_s msg_hdr;
830 struct sns_ocmem_bw_vote_req_msg_v01 msg;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700831
832 memset(&msg, 0, sizeof(struct sns_ocmem_bw_vote_req_msg_v01));
833
834 msg_hdr.msg_id = SNS_OCMEM_BW_VOTE_REQ_V01;
835 msg_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
836 msg_hdr.msg_size = sizeof(struct sns_ocmem_bw_vote_req_msg_v01);
837 msg.is_map = 0;
838 msg.vectors_valid = 0;
839 msg.vectors_len = 0;
840
841 pr_debug("%s: send bw_vote OFF\n", __func__);
842 err = sns_ocmem_send_msg(&msg_hdr, &msg);
843 if (err != 0) {
844 pr_err("%s: sns_ocmem_send_msg failed %i\n",
845 __func__, err);
846 } else {
847 err = sns_ocmem_wait(DSPS_BW_VOTE_OFF, 0);
848 if (err != 0)
849 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
850 }
851
852 return err;
853}
854
Neeti Desaia07e87d2012-12-18 18:26:31 -0800855/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700856 * Indicate to the ADSP that mapping has completed, and wait for the response
857 * that its bandwidth vote has been made.
858 *
859 * @return 0 Upon success; < 0 upon error
860 */
861static int sns_ocmem_map_send(void)
862{
863 int err;
864 struct sns_ocmem_hdr_s msg_hdr;
865 struct sns_ocmem_bw_vote_req_msg_v01 msg;
866 struct ocmem_vectors *vectors;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700867
868 memset(&msg, 0, sizeof(struct sns_ocmem_bw_vote_req_msg_v01));
869
870 msg_hdr.msg_id = SNS_OCMEM_BW_VOTE_REQ_V01;
871 msg_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
872 msg_hdr.msg_size = sizeof(struct sns_ocmem_bw_vote_req_msg_v01);
873 msg.is_map = 1;
874
875 vectors = ocmem_get_vectors(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
876 if ((vectors != NULL)) {
Satya Durga Srinivasu Prabhalaf38015e2013-10-29 13:50:42 -0700877 memcpy(&msg.vectors, vectors, sizeof(*vectors));
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700878 /* TODO: set vectors_len */
879 msg.vectors_valid = true;
880 msg.vectors_len = 0;
881 }
882
883 pr_debug("%s: send bw_vote ON\n", __func__);
884 err = sns_ocmem_send_msg(&msg_hdr, &msg);
885 if (err != 0) {
886 pr_err("%s: sns_ocmem_send_msg failed %i\n", __func__, err);
887 } else {
888 err = sns_ocmem_wait(DSPS_BW_VOTE_ON |
889 SNS_OCMEM_ALLOC_SHRINK, 0);
890 if (err != 0)
891 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
892 }
893
894 return err;
895}
896
Neeti Desaia07e87d2012-12-18 18:26:31 -0800897/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700898 * Perform the encessary operations to clean-up OCMEM after being notified that
899 * there is no longer a client; if sensors was evicted; or if some error
900 * has occurred.
901 *
902 * @param[i] do_free Whether the memory should be freed (true) or if shrink
903 * should be called instead (false).
904 */
905static void sns_ocmem_evicted(bool do_free)
906{
907 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700908
909 sns_ocmem_unmap();
910 if (do_free) {
911 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
912 sns_ctl.buf = NULL;
913 } else {
914 err = ocmem_shrink(SNS_OCMEM_CLIENT_ID, sns_ctl.buf, 0);
915 BUG_ON(err != 0);
916 }
917
918 err = sns_ocmem_unmap_send();
919 if (err != 0)
920 pr_err("sns_ocmem_unmap_send failed %i\n", err);
921}
922
Neeti Desaia07e87d2012-12-18 18:26:31 -0800923/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700924 * After mapping has completed and the ADSP has reacted appropriately, wait
925 * for a shrink command or word from the ADSP that it no longer has a client.
926 *
927 * @return 0 If no clients; < 0 upon error;
928 */
929static int sns_ocmem_map_done(void)
930{
931 int err = 0;
932 unsigned long flags;
933
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700934 err = sns_ocmem_map_send();
935 if (err != 0) {
936 pr_err("sns_ocmem_map_send failed %i\n", err);
937 sns_ocmem_evicted(true);
938 } else {
939 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
940 sns_ctl.buf, OCMEM_OFF);
941
942 pr_debug("%s: Waiting for shrink or 'no client' updates\n",
943 __func__);
944 err = sns_ocmem_wait(DSPS_HAS_NO_CLIENT |
945 SNS_OCMEM_ALLOC_SHRINK, 0);
946 if (err == 0) {
947 if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
948 pr_debug("%s: No longer have a client\n",
949 __func__);
950 sns_ocmem_evicted(true);
951 } else if (sns_ocmem_is_status_set(
952 SNS_OCMEM_ALLOC_SHRINK)) {
953 pr_debug("%s: Received SHRINK\n", __func__);
954 sns_ocmem_evicted(false);
955
956 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
957 sns_ctl.sns_ocmem_status &=
958 ~SNS_OCMEM_ALLOC_SHRINK;
959 spin_unlock_irqrestore(&sns_ctl.sns_lock,
960 flags);
961 err = -EFAULT;
962 }
963 } else {
964 pr_err("sns_ocmem_wait failed %i\n", err);
965 }
966 }
967
968 return err;
969}
970
Neeti Desaia07e87d2012-12-18 18:26:31 -0800971/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700972 * Main function.
973 * Initializes sensors ocmem feature, and waits for an ADSP client.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700974 */
975static void sns_ocmem_main(struct work_struct *work)
976{
977 int err = 0;
978 pr_debug("%s\n", __func__);
979
980 err = sns_ocmem_init();
981 if (err != 0) {
982 pr_err("%s: sns_ocmem_init failed %i\n", __func__, err);
983 return;
984 }
985
986 while (true) {
987 pr_debug("%s: Waiting for sensor client\n", __func__);
988 if (sns_ocmem_is_status_set(DSPS_HAS_CLIENT) ||
989 !sns_ocmem_wait(DSPS_HAS_CLIENT, 0)) {
990 pr_debug("%s: DSPS_HAS_CLIENT\n", __func__);
991
992 err = sns_ocmem_alloc();
993 if (err != 0) {
994 pr_err("sns_ocmem_alloc failed %i\n", err);
995 return;
996 } else {
997 err = sns_ocmem_map_done();
998 if (err != 0) {
999 pr_err("sns_ocmem_map_done failed %i",
1000 err);
1001 return;
1002 }
1003 }
1004 }
1005 }
1006
1007 ocmem_notifier_unregister(sns_ctl.ocmem_handle,
1008 &sns_ctl.ocmem_nb);
1009}
1010
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001011static int sensors_adsp_open(struct inode *ip, struct file *fp)
1012{
1013 int ret = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001014 return ret;
1015}
1016
1017static int sensors_adsp_release(struct inode *inode, struct file *file)
1018{
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001019 return 0;
1020}
1021
Neeti Desaia07e87d2012-12-18 18:26:31 -08001022/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001023 * Read QTimer clock ticks and scale down to 32KHz clock as used
1024 * in DSPS
1025 */
1026static u32 sns_read_qtimer(void)
1027{
1028 u64 val;
1029 val = arch_counter_get_cntpct();
1030 /*
1031 * To convert ticks from 19.2 Mhz clock to 32768 Hz clock:
1032 * x = (value * 32768) / 19200000
1033 * This is same as first left shift the value by 4 bits, i.e. mutiply
1034 * by 16, and then divide by 9375. The latter is preferable since
1035 * QTimer tick (value) is 56-bit, so (value * 32768) could overflow,
1036 * while (value * 16) will never do
1037 */
1038 val <<= 4;
1039 do_div(val, 9375);
1040
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001041 return (u32)val;
1042}
1043
Neeti Desaia07e87d2012-12-18 18:26:31 -08001044/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001045 * IO Control - handle commands from client.
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001046 */
1047static long sensors_adsp_ioctl(struct file *file,
1048 unsigned int cmd, unsigned long arg)
1049{
1050 int ret = 0;
1051 u32 val = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001052
1053 switch (cmd) {
1054 case DSPS_IOCTL_READ_SLOW_TIMER:
1055 val = sns_read_qtimer();
1056 ret = put_user(val, (u32 __user *) arg);
1057 break;
1058
1059 default:
1060 ret = -EINVAL;
1061 break;
1062 }
1063
1064 return ret;
1065}
1066
Neeti Desaia07e87d2012-12-18 18:26:31 -08001067/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001068 * platform driver
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001069 */
1070const struct file_operations sensors_adsp_fops = {
1071 .owner = THIS_MODULE,
1072 .open = sensors_adsp_open,
1073 .release = sensors_adsp_release,
1074 .unlocked_ioctl = sensors_adsp_ioctl,
1075};
1076
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001077static int sensors_adsp_probe(struct platform_device *pdev)
1078{
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001079 int ret = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001080 sns_ctl.dev_class = class_create(THIS_MODULE, DRV_NAME);
1081 if (sns_ctl.dev_class == NULL) {
1082 pr_err("%s: class_create fail.\n", __func__);
1083 goto res_err;
1084 }
1085
1086 ret = alloc_chrdev_region(&sns_ctl.dev_num, 0, 1, DRV_NAME);
1087 if (ret) {
1088 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
1089 goto alloc_chrdev_region_err;
1090 }
1091
1092 sns_ctl.dev = device_create(sns_ctl.dev_class, NULL,
1093 sns_ctl.dev_num,
1094 &sns_ctl, DRV_NAME);
1095 if (IS_ERR(sns_ctl.dev)) {
1096 pr_err("%s: device_create fail.\n", __func__);
1097 goto device_create_err;
1098 }
1099
1100 sns_ctl.cdev = cdev_alloc();
1101 if (sns_ctl.cdev == NULL) {
1102 pr_err("%s: cdev_alloc fail.\n", __func__);
1103 goto cdev_alloc_err;
1104 }
1105 cdev_init(sns_ctl.cdev, &sensors_adsp_fops);
1106 sns_ctl.cdev->owner = THIS_MODULE;
1107
1108 ret = cdev_add(sns_ctl.cdev, sns_ctl.dev_num, 1);
1109 if (ret) {
1110 pr_err("%s: cdev_add fail.\n", __func__);
1111 goto cdev_add_err;
1112 }
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001113
1114 sns_ctl.sns_workqueue =
1115 alloc_workqueue("sns_ocmem", WQ_NON_REENTRANT, 0);
1116 if (!sns_ctl.sns_workqueue) {
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001117 pr_err("%s: Failed to create work queue\n",
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001118 __func__);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001119 goto cdev_add_err;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001120 }
1121
Neeti Desaia07e87d2012-12-18 18:26:31 -08001122 sns_ctl.smd_wq =
1123 alloc_workqueue("smd_wq", WQ_NON_REENTRANT, 0);
1124 if (!sns_ctl.smd_wq) {
1125 pr_err("%s: Failed to create work queue\n",
1126 __func__);
1127 goto cdev_add_err;
1128 }
1129
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001130 init_waitqueue_head(&sns_ctl.sns_wait);
1131 spin_lock_init(&sns_ctl.sns_lock);
1132
1133 sns_ctl.ocmem_handle = NULL;
1134 sns_ctl.buf = NULL;
1135 sns_ctl.sns_ocmem_status = 0;
1136 sns_ctl.ocmem_enabled = true;
1137 sns_ctl.ocmem_nb.notifier_call = sns_ocmem_drv_cb;
1138 sns_ctl.smd_ch = NULL;
1139 sns_ctl.pdev = pdev;
1140
1141 INIT_WORK(&sns_ctl.sns_work, sns_ocmem_main);
Neeti Desaia07e87d2012-12-18 18:26:31 -08001142 INIT_WORK(&sns_ctl.smd_read_work, sns_ocmem_smd_read);
1143
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001144 queue_work(sns_ctl.sns_workqueue, &sns_ctl.sns_work);
1145
1146 return 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001147
1148cdev_add_err:
1149 kfree(sns_ctl.cdev);
1150cdev_alloc_err:
1151 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
1152device_create_err:
1153 unregister_chrdev_region(sns_ctl.dev_num, 1);
1154alloc_chrdev_region_err:
1155 class_destroy(sns_ctl.dev_class);
1156res_err:
1157 return -ENODEV;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001158}
1159
1160static int sensors_adsp_remove(struct platform_device *pdev)
1161{
1162 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001163
1164 sns_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
1165 dev_get_drvdata(&pdev->dev);
1166
1167 kfree(sns_ocmem_bus_scale_pdata->usecase->vectors);
1168 kfree(sns_ocmem_bus_scale_pdata->usecase);
1169 kfree(sns_ocmem_bus_scale_pdata);
1170
1171 ocmem_notifier_unregister(sns_ctl.ocmem_handle,
1172 &sns_ctl.ocmem_nb);
1173 destroy_workqueue(sns_ctl.sns_workqueue);
Neeti Desaia07e87d2012-12-18 18:26:31 -08001174 destroy_workqueue(sns_ctl.smd_wq);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001175
1176 cdev_del(sns_ctl.cdev);
1177 kfree(sns_ctl.cdev);
1178 sns_ctl.cdev = NULL;
1179 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
1180 unregister_chrdev_region(sns_ctl.dev_num, 1);
1181 class_destroy(sns_ctl.dev_class);
1182
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001183 return 0;
1184}
1185
1186static const struct of_device_id msm_adsp_sensors_dt_match[] = {
1187 {.compatible = "qcom,msm-adsp-sensors"},
1188 {}
1189};
1190MODULE_DEVICE_TABLE(of, msm_adsp_sensors_dt_match);
1191
1192
1193static struct platform_driver sensors_adsp_driver = {
1194 .driver = {
1195 .name = "sensors-adsp",
1196 .owner = THIS_MODULE,
1197 .of_match_table = msm_adsp_sensors_dt_match,
1198 },
1199 .probe = sensors_adsp_probe,
1200 .remove = sensors_adsp_remove,
1201};
1202
Neeti Desaia07e87d2012-12-18 18:26:31 -08001203/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001204 * Module Init.
1205 */
1206static int sensors_adsp_init(void)
1207{
1208 int rc;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001209 pr_debug("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
1210
1211 rc = platform_driver_register(&sensors_adsp_driver);
1212
1213 if (rc) {
1214 pr_err("%s: Failed to register sensors adsp driver\n",
1215 __func__);
1216 return rc;
1217 }
1218
1219 return 0;
1220}
1221
Neeti Desaia07e87d2012-12-18 18:26:31 -08001222/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001223 * Module Exit.
1224 */
1225static void sensors_adsp_exit(void)
1226{
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001227 platform_driver_unregister(&sensors_adsp_driver);
1228}
1229
1230module_init(sensors_adsp_init);
1231module_exit(sensors_adsp_exit);
1232
1233MODULE_LICENSE("GPL v2");
1234MODULE_DESCRIPTION("Sensors ADSP driver");