blob: ad19e167076a2be916a12ea03ee7ccdea1f439e9 [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
Jie Chengab0a2132013-11-11 13:40:11 +080041#define CLASS_NAME "ssc"
Naveen Ramaraj93060a42012-11-15 23:23:55 -080042#define DRV_NAME "sensors"
Naveen Ramaraj88f23632012-10-10 12:23:29 -070043#define DRV_VERSION "1.00"
44
45#define SNS_OCMEM_SMD_CHANNEL "SENSOR"
46#define SNS_OCMEM_CLIENT_ID OCMEM_SENSORS
Neeti Desaia07e87d2012-12-18 18:26:31 -080047#define SNS_OCMEM_SIZE SZ_256K
48#define SMD_BUF_SIZE 1024
49#define SNS_TIMEOUT_MS 1000
Naveen Ramaraj88f23632012-10-10 12:23:29 -070050
51#define SNS_OCMEM_ALLOC_GROW 0x00000001
52#define SNS_OCMEM_ALLOC_SHRINK 0x00000002
53#define SNS_OCMEM_MAP_DONE 0x00000004
54#define SNS_OCMEM_MAP_FAIL 0x00000008
55#define SNS_OCMEM_UNMAP_DONE 0x00000010
56#define SNS_OCMEM_UNMAP_FAIL 0x00000020
57
58#define DSPS_HAS_CLIENT 0x00000100
59#define DSPS_HAS_NO_CLIENT 0x00000200
60#define DSPS_BW_VOTE_ON 0x00000400
61#define DSPS_BW_VOTE_OFF 0x00000800
62#define DSPS_PHYS_ADDR_SET 0x00001000
63
Neeti Desaia07e87d2012-12-18 18:26:31 -080064/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -070065 * Structure contains all state used by the sensors driver
66 */
67struct sns_adsp_control_s {
68 wait_queue_head_t sns_wait;
69 spinlock_t sns_lock;
70 struct workqueue_struct *sns_workqueue;
71 struct work_struct sns_work;
Neeti Desaia07e87d2012-12-18 18:26:31 -080072 struct workqueue_struct *smd_wq;
73 struct work_struct smd_read_work;
Naveen Ramaraj88f23632012-10-10 12:23:29 -070074 smd_channel_t *smd_ch;
75 uint32_t sns_ocmem_status;
76 uint32_t mem_segments_size;
77 struct sns_mem_segment_s_v01 mem_segments[SNS_OCMEM_MAX_NUM_SEG_V01];
78 struct ocmem_buf *buf;
79 struct ocmem_map_list map_list;
80 struct ocmem_notifier *ocmem_handle;
81 bool ocmem_enabled;
82 struct notifier_block ocmem_nb;
83 uint32_t sns_ocmem_bus_client;
84 struct platform_device *pdev;
85 void *pil;
Naveen Ramaraj93060a42012-11-15 23:23:55 -080086 struct class *dev_class;
87 dev_t dev_num;
88 struct device *dev;
89 struct cdev *cdev;
Naveen Ramaraj88f23632012-10-10 12:23:29 -070090};
91
92static struct sns_adsp_control_s sns_ctl;
93
Neeti Desaia07e87d2012-12-18 18:26:31 -080094/*
95 * All asynchronous responses from the OCMEM driver are received
96 * by this function
97 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -070098int sns_ocmem_drv_cb(struct notifier_block *self,
99 unsigned long action,
100 void *dev)
101{
102 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700103
104 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
105
106 pr_debug("%s: Received OCMEM callback: action=%li\n",
107 __func__, action);
108
109 switch (action) {
110 case OCMEM_MAP_DONE:
111 sns_ctl.sns_ocmem_status |= SNS_OCMEM_MAP_DONE;
112 sns_ctl.sns_ocmem_status &= (~OCMEM_MAP_FAIL &
113 ~SNS_OCMEM_UNMAP_DONE &
114 ~SNS_OCMEM_UNMAP_FAIL);
115 break;
116 case OCMEM_MAP_FAIL:
117 sns_ctl.sns_ocmem_status |= SNS_OCMEM_MAP_FAIL;
118 sns_ctl.sns_ocmem_status &= (~OCMEM_MAP_DONE &
119 ~SNS_OCMEM_UNMAP_DONE &
120 ~SNS_OCMEM_UNMAP_FAIL);
121 break;
122 case OCMEM_UNMAP_DONE:
123 sns_ctl.sns_ocmem_status |= SNS_OCMEM_UNMAP_DONE;
124 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_FAIL &
125 ~SNS_OCMEM_MAP_DONE &
126 ~OCMEM_MAP_FAIL);
127 break;
128 case OCMEM_UNMAP_FAIL:
129 sns_ctl.sns_ocmem_status |= SNS_OCMEM_UNMAP_FAIL;
130 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_DONE &
131 ~SNS_OCMEM_MAP_DONE &
132 ~OCMEM_MAP_FAIL);
133 break;
134 case OCMEM_ALLOC_GROW:
135 sns_ctl.sns_ocmem_status |= SNS_OCMEM_ALLOC_GROW;
136 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_SHRINK;
137 break;
138 case OCMEM_ALLOC_SHRINK:
139 sns_ctl.sns_ocmem_status |= SNS_OCMEM_ALLOC_SHRINK;
140 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_GROW;
141 break;
142 default:
143 pr_err("%s: Unknown action received in OCMEM callback %lu\n",
144 __func__, action);
145 break;
146 }
147
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700148 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700149 wake_up(&sns_ctl.sns_wait);
150
151 return 0;
152}
153
Neeti Desaia07e87d2012-12-18 18:26:31 -0800154/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700155 * Processes messages received through SMD from the ADSP
156 *
157 * @param hdr The message header
158 * @param msg Message pointer
159 *
Neeti Desaia07e87d2012-12-18 18:26:31 -0800160 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700161void sns_ocmem_smd_process(struct sns_ocmem_hdr_s *hdr, void *msg)
162{
163 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700164
165 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
166
167 pr_debug("%s: Received message from ADSP; id: %i type: %i (%08x)\n",
168 __func__, hdr->msg_id, hdr->msg_type,
169 sns_ctl.sns_ocmem_status);
170
171 if (hdr->msg_id == SNS_OCMEM_PHYS_ADDR_RESP_V01 &&
172 hdr->msg_type == SNS_OCMEM_MSG_TYPE_RESP) {
173 struct sns_ocmem_phys_addr_resp_msg_v01 *msg_ptr =
174 (struct sns_ocmem_phys_addr_resp_msg_v01 *)msg;
175 pr_debug("%s: Received SNS_OCMEM_PHYS_ADDR_RESP_V01\n",
176 __func__);
177 pr_debug("%s: segments_valid=%d, segments_len=%d\n", __func__,
178 msg_ptr->segments_valid, msg_ptr->segments_len);
179
180 if (msg_ptr->segments_valid) {
181 sns_ctl.mem_segments_size = msg_ptr->segments_len;
182 memcpy(sns_ctl.mem_segments, msg_ptr->segments,
183 sizeof(struct sns_mem_segment_s_v01) *
184 msg_ptr->segments_len);
185
186 sns_ctl.sns_ocmem_status |= DSPS_PHYS_ADDR_SET;
187 } else {
188 pr_err("%s: Received invalid segment list\n", __func__);
189 }
190 } else if (hdr->msg_id == SNS_OCMEM_HAS_CLIENT_IND_V01 &&
191 hdr->msg_type == SNS_OCMEM_MSG_TYPE_IND) {
192 struct sns_ocmem_has_client_ind_msg_v01 *msg_ptr =
193 (struct sns_ocmem_has_client_ind_msg_v01 *)msg;
194
195 pr_debug("%s: Received SNS_OCMEM_HAS_CLIENT_IND_V01\n",
196 __func__);
197 pr_debug("%s: ADSP has %i client(s)\n", __func__,
198 msg_ptr->num_clients);
199 if (msg_ptr->num_clients > 0) {
200 sns_ctl.sns_ocmem_status |= DSPS_HAS_CLIENT;
201 sns_ctl.sns_ocmem_status &= ~DSPS_HAS_NO_CLIENT;
202 } else {
203 sns_ctl.sns_ocmem_status |= DSPS_HAS_NO_CLIENT;
204 sns_ctl.sns_ocmem_status &= ~DSPS_HAS_CLIENT;
205 }
206 } else if (hdr->msg_id == SNS_OCMEM_BW_VOTE_RESP_V01 &&
207 hdr->msg_type == SNS_OCMEM_MSG_TYPE_RESP) {
208 /* no need to handle this response msg, just return */
209 pr_debug("%s: Received SNS_OCMEM_BW_VOTE_RESP_V01\n", __func__);
210 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
211 return;
212 } else if (hdr->msg_id == SNS_OCMEM_BW_VOTE_IND_V01 &&
213 hdr->msg_type == SNS_OCMEM_MSG_TYPE_IND) {
214 struct sns_ocmem_bw_vote_ind_msg_v01 *msg_ptr =
215 (struct sns_ocmem_bw_vote_ind_msg_v01 *)msg;
216 pr_debug("%s: Received BW_VOTE_IND_V01, is_vote_on=%d\n",
217 __func__, msg_ptr->is_vote_on);
218
219 if (msg_ptr->is_vote_on) {
220 sns_ctl.sns_ocmem_status |= DSPS_BW_VOTE_ON;
221 sns_ctl.sns_ocmem_status &= ~DSPS_BW_VOTE_OFF;
222 } else {
223 sns_ctl.sns_ocmem_status |= DSPS_BW_VOTE_OFF;
224 sns_ctl.sns_ocmem_status &= ~DSPS_BW_VOTE_ON;
225 }
226 } else {
227 pr_err("%s: Unknown message type received. id: %i; type: %i\n",
228 __func__, hdr->msg_id, hdr->msg_type);
229 }
230
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700231 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
232
233 wake_up(&sns_ctl.sns_wait);
234}
235
Neeti Desaia07e87d2012-12-18 18:26:31 -0800236static void sns_ocmem_smd_read(struct work_struct *ws)
237{
238 struct smd_channel *ch = sns_ctl.smd_ch;
239 unsigned char *buf = NULL;
240 int sz, len;
241
242 for (;;) {
243 sz = smd_cur_packet_size(ch);
244 BUG_ON(sz > SMD_BUF_SIZE);
245 len = smd_read_avail(ch);
246 pr_debug("%s: sz=%d, len=%d\n", __func__, sz, len);
247 if (len == 0 || len < sz)
248 break;
249 buf = kzalloc(SMD_BUF_SIZE, GFP_KERNEL);
250 if (buf == NULL) {
251 pr_err("%s: malloc failed", __func__);
252 break;
253 }
254
255 if (smd_read(ch, buf, sz) != sz) {
256 pr_err("%s: not enough data?!\n", __func__);
257 kfree(buf);
258 continue;
259 }
260
261 sns_ocmem_smd_process((struct sns_ocmem_hdr_s *)buf,
262 (void *)((char *)buf +
263 sizeof(struct sns_ocmem_hdr_s)));
264
265 kfree(buf);
266
267 }
268}
269
270/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700271 * All SMD notifications and messages from Sensors on ADSP are
272 * received by this function
273 *
Neeti Desaia07e87d2012-12-18 18:26:31 -0800274 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700275void sns_ocmem_smd_notify_data(void *data, unsigned int event)
276{
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700277 if (event == SMD_EVENT_DATA) {
Neeti Desaia07e87d2012-12-18 18:26:31 -0800278 int sz;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700279 pr_debug("%s: Received SMD event Data\n", __func__);
Neeti Desaia07e87d2012-12-18 18:26:31 -0800280 sz = smd_cur_packet_size(sns_ctl.smd_ch);
281 if ((sz > 0) && (sz <= smd_read_avail(sns_ctl.smd_ch)))
282 queue_work(sns_ctl.smd_wq, &sns_ctl.smd_read_work);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700283 } else if (event == SMD_EVENT_OPEN) {
284 pr_debug("%s: Received SMD event Open\n", __func__);
285 } else if (event == SMD_EVENT_CLOSE) {
286 pr_debug("%s: Received SMD event Close\n", __func__);
287 }
288}
289
290static bool sns_ocmem_is_status_set(uint32_t sns_ocmem_status)
291{
292 unsigned long flags;
293 bool is_set;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700294
295 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
296 is_set = sns_ctl.sns_ocmem_status & sns_ocmem_status;
297 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700298 return is_set;
299}
300
Neeti Desaia07e87d2012-12-18 18:26:31 -0800301/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700302 * Wait for a response from ADSP or OCMEM Driver, timeout if necessary
303 *
304 * @param sns_ocmem_status Status flags to wait for.
305 * @param timeout_sec Seconds to wait before timeout
306 * @param timeout_nsec Nanoseconds to wait. Total timeout = nsec + sec
307 *
308 * @return 0 If any status flag is set at any time prior to a timeout.
309 * 0 if success or timedout ; <0 for failures
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700310 */
311static int sns_ocmem_wait(uint32_t sns_ocmem_status,
312 uint32_t timeout_ms)
313{
314 int err;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700315 if (timeout_ms) {
316 err = wait_event_interruptible_timeout(sns_ctl.sns_wait,
317 sns_ocmem_is_status_set(sns_ocmem_status),
318 msecs_to_jiffies(timeout_ms));
319
320 if (err == 0)
321 pr_err("%s: interruptible_timeout timeout err=%i\n",
322 __func__, err);
323 else if (err < 0)
324 pr_err("%s: interruptible_timeout failed err=%i\n",
325 __func__, err);
326 } else { /* no timeout */
327 err = wait_event_interruptible(sns_ctl.sns_wait,
328 sns_ocmem_is_status_set(sns_ocmem_status));
329 if (err < 0)
330 pr_err("%s: wait_event_interruptible failed err=%i\n",
331 __func__, err);
332 }
333
334 return err;
335}
336
Neeti Desaia07e87d2012-12-18 18:26:31 -0800337/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700338 * Sends a message to the ADSP via SMD.
339 *
340 * @param hdr Specifies message type and other meta data
341 * @param msg_ptr Pointer to the message contents.
342 * Must be freed within this function if no error is returned.
343 *
344 * @return 0 upon success; < 0 upon error
345 */
346static int
347sns_ocmem_send_msg(struct sns_ocmem_hdr_s *hdr, void const *msg_ptr)
348{
349 int rv = 0;
350 int err = 0;
351 void *temp = NULL;
352 int size = sizeof(struct sns_ocmem_hdr_s) + hdr->msg_size;
353
354 temp = kzalloc(sizeof(struct sns_ocmem_hdr_s) + hdr->msg_size,
355 GFP_KERNEL);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700356 if (temp == NULL) {
357 pr_err("%s: allocation failure\n", __func__);
358 rv = -ENOMEM;
Satya Durga Srinivasu Prabhalaebcdc4b2013-10-29 14:01:30 -0700359 goto out;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700360 }
361
362 hdr->dst_module = SNS_OCMEM_MODULE_ADSP;
363 hdr->src_module = SNS_OCMEM_MODULE_KERNEL;
364
365 memcpy(temp, hdr, sizeof(struct sns_ocmem_hdr_s));
366 memcpy((char *)temp + sizeof(struct sns_ocmem_hdr_s),
367 msg_ptr, hdr->msg_size);
368 pr_debug("%s: send msg type: %i size: %i id: %i dst: %i src: %i\n",
369 __func__, hdr->msg_type, hdr->msg_size,
370 hdr->msg_id, hdr->dst_module, hdr->src_module);
371
372 if (hdr == NULL) {
373 pr_err("%s: NULL message header\n", __func__);
374 rv = -EINVAL;
375 } else {
376 if (sns_ctl.smd_ch == NULL) {
377 pr_err("%s: null smd_ch\n", __func__);
378 rv = -EINVAL;
379 }
380 err = smd_write(sns_ctl.smd_ch, temp, size);
381 if (err < 0) {
382 pr_err("%s: smd_write failed %i\n", __func__, err);
383 rv = -ECOMM;
384 } else {
385 pr_debug("%s smd_write successful ret=%d\n",
386 __func__, err);
387 }
388 }
389
390 kfree(temp);
391
Satya Durga Srinivasu Prabhalaebcdc4b2013-10-29 14:01:30 -0700392out:
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700393 return rv;
394}
395
Neeti Desaia07e87d2012-12-18 18:26:31 -0800396/*
397 * Load ADSP Firmware.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700398 */
399
400static int sns_load_adsp(void)
401{
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700402 sns_ctl.pil = subsystem_get("adsp");
403 if (IS_ERR(sns_ctl.pil)) {
404 pr_err("%s: fail to load ADSP firmware\n", __func__);
405 return -ENODEV;
406 }
407
408 pr_debug("%s: Q6/ADSP image is loaded\n", __func__);
409
410 return 0;
411}
412
413static int sns_ocmem_platform_data_populate(struct platform_device *pdev)
414{
415 int ret;
416 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
417 struct msm_bus_vectors *sns_ocmem_bus_vectors = NULL;
418 struct msm_bus_paths *ocmem_sns_bus_paths = NULL;
419 u32 val;
420
421 if (!pdev->dev.of_node) {
422 pr_err("%s: device tree information missing\n", __func__);
423 return -ENODEV;
424 }
425
426 sns_ocmem_bus_vectors = kzalloc(sizeof(struct msm_bus_vectors),
427 GFP_KERNEL);
428 if (!sns_ocmem_bus_vectors) {
429 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
430 return -ENOMEM;
431 }
432
433 ret = of_property_read_u32(pdev->dev.of_node,
434 "qcom,src-id", &val);
435 if (ret) {
436 dev_err(&pdev->dev, "%s: qcom,src-id missing in DT node\n",
437 __func__);
438 goto fail1;
439 }
440 sns_ocmem_bus_vectors->src = val;
441 ret = of_property_read_u32(pdev->dev.of_node,
442 "qcom,dst-id", &val);
443 if (ret) {
444 dev_err(&pdev->dev, "%s: qcom,dst-id missing in DT node\n",
445 __func__);
446 goto fail1;
447 }
448 sns_ocmem_bus_vectors->dst = val;
449 ret = of_property_read_u32(pdev->dev.of_node,
450 "qcom,ab", &val);
451 if (ret) {
452 dev_err(&pdev->dev, "%s: qcom,ab missing in DT node\n",
453 __func__);
454 goto fail1;
455 }
456 sns_ocmem_bus_vectors->ab = val;
457 ret = of_property_read_u32(pdev->dev.of_node,
458 "qcom,ib", &val);
459 if (ret) {
460 dev_err(&pdev->dev, "%s: qcom,ib missing in DT node\n",
461 __func__);
462 goto fail1;
463 }
464 sns_ocmem_bus_vectors->ib = val;
465 ocmem_sns_bus_paths = kzalloc(sizeof(struct msm_bus_paths),
466 GFP_KERNEL);
467
468 if (!ocmem_sns_bus_paths) {
469 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
470 goto fail1;
471 }
472 ocmem_sns_bus_paths->num_paths = 1;
473 ocmem_sns_bus_paths->vectors = sns_ocmem_bus_vectors;
474
475 sns_ocmem_bus_scale_pdata =
476 kzalloc(sizeof(struct msm_bus_scale_pdata), GFP_KERNEL);
477 if (!sns_ocmem_bus_scale_pdata) {
478 dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
479 goto fail2;
480 }
481
482 sns_ocmem_bus_scale_pdata->usecase = ocmem_sns_bus_paths;
483 sns_ocmem_bus_scale_pdata->num_usecases = 1;
484 sns_ocmem_bus_scale_pdata->name = "sensors-ocmem";
485
486 dev_set_drvdata(&pdev->dev, sns_ocmem_bus_scale_pdata);
487 return ret;
488
489fail2:
490 kfree(ocmem_sns_bus_paths);
491fail1:
492 kfree(sns_ocmem_bus_vectors);
493 return ret;
494}
495
496
Neeti Desaia07e87d2012-12-18 18:26:31 -0800497/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700498 * Initialize all sensors ocmem driver data fields and register with the
499 * ocmem driver.
500 *
501 * @return 0 upon success; < 0 upon error
502 */
503static int sns_ocmem_init(void)
504{
505 int i, err, ret;
506 struct sns_ocmem_hdr_s addr_req_hdr;
507 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
508
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700509 /* register from OCMEM callack */
510 sns_ctl.ocmem_handle =
511 ocmem_notifier_register(SNS_OCMEM_CLIENT_ID,
512 &sns_ctl.ocmem_nb);
513 if (sns_ctl.ocmem_handle == NULL) {
514 pr_err("OCMEM notifier registration failed\n");
515 return -EFAULT;
516 }
517
518 /* populate platform data */
519 ret = sns_ocmem_platform_data_populate(sns_ctl.pdev);
520 if (ret) {
521 dev_err(&sns_ctl.pdev->dev,
522 "%s: failed to populate platform data, rc = %d\n",
523 __func__, ret);
524 return -ENODEV;
525 }
526 sns_ocmem_bus_scale_pdata = dev_get_drvdata(&sns_ctl.pdev->dev);
527
528 sns_ctl.sns_ocmem_bus_client =
529 msm_bus_scale_register_client(sns_ocmem_bus_scale_pdata);
530
531 if (!sns_ctl.sns_ocmem_bus_client) {
532 pr_err("%s: msm_bus_scale_register_client() failed\n",
533 __func__);
534 return -EFAULT;
535 }
536
537 /* load ADSP first */
538 if (sns_load_adsp() != 0) {
539 pr_err("%s: sns_load_adsp failed\n", __func__);
540 return -EFAULT;
541 }
542
Neeti Desaia07e87d2012-12-18 18:26:31 -0800543 /*
544 * wait before open SMD channel from kernel to ensure
545 * channel has been openned already from ADSP side
546 */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700547 msleep(1000);
548
549 err = smd_named_open_on_edge(SNS_OCMEM_SMD_CHANNEL,
550 SMD_APPS_QDSP,
551 &sns_ctl.smd_ch,
552 NULL,
553 sns_ocmem_smd_notify_data);
554 if (err != 0) {
555 pr_err("%s: smd_named_open_on_edge failed %i\n", __func__, err);
556 return -EFAULT;
557 }
558
559 pr_debug("%s: SMD channel openned successfuly!\n", __func__);
560 /* wait for the channel ready before writing data */
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700561 msleep(1000);
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700562 addr_req_hdr.msg_id = SNS_OCMEM_PHYS_ADDR_REQ_V01;
563 addr_req_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
564 addr_req_hdr.msg_size = 0;
565
566 err = sns_ocmem_send_msg(&addr_req_hdr, NULL);
567 if (err != 0) {
568 pr_err("%s: sns_ocmem_send_msg failed %i\n", __func__, err);
569 return -ECOMM;
570 }
571
572 err = sns_ocmem_wait(DSPS_PHYS_ADDR_SET, 0);
573 if (err != 0) {
574 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
575 return -EFAULT;
576 }
577
578 sns_ctl.map_list.num_chunks = sns_ctl.mem_segments_size;
579 for (i = 0; i < sns_ctl.mem_segments_size; i++) {
580 sns_ctl.map_list.chunks[i].ro =
581 sns_ctl.mem_segments[i].type == 1 ? true : false;
582 sns_ctl.map_list.chunks[i].ddr_paddr =
583 sns_ctl.mem_segments[i].start_address;
584 sns_ctl.map_list.chunks[i].size =
585 sns_ctl.mem_segments[i].size;
586
587 pr_debug("%s: chunks[%d]: ro=%d, ddr_paddr=0x%lx, size=%li",
588 __func__, i,
589 sns_ctl.map_list.chunks[i].ro,
590 sns_ctl.map_list.chunks[i].ddr_paddr,
591 sns_ctl.map_list.chunks[i].size);
592 }
593
594 return 0;
595}
596
Neeti Desaia07e87d2012-12-18 18:26:31 -0800597/*
598 * Unmaps memory in ocmem back to DDR, indicates to the ADSP its completion,
599 * and waits for it to finish removing its bandwidth vote.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700600 */
601static void sns_ocmem_unmap(void)
602{
603 unsigned long flags;
604 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700605
606 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
607 sns_ctl.buf, OCMEM_ON);
608
609 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
610 sns_ctl.sns_ocmem_status &= (~SNS_OCMEM_UNMAP_FAIL &
611 ~SNS_OCMEM_UNMAP_DONE);
612 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
613
614 err = ocmem_unmap(SNS_OCMEM_CLIENT_ID,
615 sns_ctl.buf,
616 &sns_ctl.map_list);
617
618 if (err != 0) {
619 pr_err("ocmem_unmap failed %i\n", err);
620 } else {
621 err = sns_ocmem_wait(SNS_OCMEM_UNMAP_DONE |
622 SNS_OCMEM_UNMAP_FAIL, 0);
623
624 if (err == 0) {
625 if (sns_ocmem_is_status_set(SNS_OCMEM_UNMAP_DONE))
626 pr_debug("%s: OCMEM_UNMAP_DONE\n", __func__);
627 else if (sns_ocmem_is_status_set(
628 SNS_OCMEM_UNMAP_FAIL)) {
629 pr_err("%s: OCMEM_UNMAP_FAIL\n", __func__);
630 BUG_ON(true);
631 } else
632 pr_err("%s: status flag not set\n", __func__);
633 } else {
634 pr_err("%s: sns_ocmem_wait failed %i\n",
635 __func__, err);
636 }
637 }
638
639 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
640 sns_ctl.buf, OCMEM_OFF);
641}
642
Neeti Desaia07e87d2012-12-18 18:26:31 -0800643/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700644 * Waits for allocation to succeed. This may take considerable time if the device
645 * is presently in a high-power use case.
646 *
647 * @return 0 on success; < 0 upon error
648 */
649static int sns_ocmem_wait_for_alloc(void)
650{
651 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700652
653 err = sns_ocmem_wait(SNS_OCMEM_ALLOC_GROW |
654 DSPS_HAS_NO_CLIENT, 0);
655
656 if (err == 0) {
657 if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
658 pr_debug("%s: Lost client while waiting for GROW\n",
659 __func__);
660 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
661 sns_ctl.buf = NULL;
662 return -EPIPE;
663 }
664 } else {
665 pr_err("sns_ocmem_wait failed %i\n", err);
666 return -EFAULT;
667 }
668
669 return 0;
670}
671
Neeti Desaia07e87d2012-12-18 18:26:31 -0800672/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700673 * Kicks-off the mapping of memory from DDR to ocmem. Waits for the process
674 * to complete, then indicates so to the ADSP.
675 *
676 * @return 0: Success; < 0: Other error
677 */
678static int sns_ocmem_map(void)
679{
680 int err = 0;
681 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700682
683 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
684 sns_ctl.sns_ocmem_status &=
685 (~SNS_OCMEM_MAP_FAIL & ~SNS_OCMEM_MAP_DONE);
686 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
687
688 /* vote for ocmem bus bandwidth */
689 err = msm_bus_scale_client_update_request(
690 sns_ctl.sns_ocmem_bus_client,
691 0);
692 if (err)
693 pr_err("%s: failed to vote for bus bandwidth\n", __func__);
694
695 err = ocmem_map(SNS_OCMEM_CLIENT_ID,
696 sns_ctl.buf,
697 &sns_ctl.map_list);
698
699 if (err != 0) {
700 pr_debug("ocmem_map failed %i\n", err);
701 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
702 sns_ctl.buf, OCMEM_OFF);
703 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
704 sns_ctl.buf = NULL;
705 } else {
706 err = sns_ocmem_wait(SNS_OCMEM_ALLOC_SHRINK |
707 DSPS_HAS_NO_CLIENT |
708 SNS_OCMEM_MAP_DONE |
709 SNS_OCMEM_MAP_FAIL, 0);
710
711 if (err == 0) {
712 if (sns_ocmem_is_status_set(SNS_OCMEM_MAP_DONE))
713 pr_debug("%s: OCMEM mapping DONE\n", __func__);
714 else if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
715 pr_debug("%s: Lost client while waiting for MAP\n",
716 __func__);
717 sns_ocmem_unmap();
718 ocmem_free(SNS_OCMEM_CLIENT_ID,
719 sns_ctl.buf);
720 sns_ctl.buf = NULL;
721 err = -EPIPE;
722 } else if (sns_ocmem_is_status_set(
723 SNS_OCMEM_ALLOC_SHRINK)) {
724 pr_debug("%s: SHRINK while wait for MAP\n",
725 __func__);
726 sns_ocmem_unmap();
727 err = ocmem_shrink(SNS_OCMEM_CLIENT_ID,
728 sns_ctl.buf, 0);
729 BUG_ON(err != 0);
730 err = -EFAULT;
731 } else if (sns_ocmem_is_status_set(
732 SNS_OCMEM_MAP_FAIL)) {
733 pr_err("%s: OCMEM mapping fails\n", __func__);
734 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
735 sns_ctl.buf,
736 OCMEM_OFF);
737 ocmem_free(SNS_OCMEM_CLIENT_ID,
738 sns_ctl.buf);
739 sns_ctl.buf = NULL;
740 } else
741 pr_err("%s: status flag not set\n", __func__);
742 } else {
743 pr_err("sns_ocmem_wait failed %i\n", err);
744 }
745 }
746
747 return err;
748}
749
Neeti Desaia07e87d2012-12-18 18:26:31 -0800750/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700751 * Allocates memory in ocmem and maps to it from DDR.
752 *
753 * @return 0 upon success; <0 upon failure;
754 */
755static int sns_ocmem_alloc(void)
756{
757 int err = 0;
758 unsigned long flags;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700759
760 if (sns_ctl.buf == NULL) {
761 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
762 sns_ctl.sns_ocmem_status &= ~SNS_OCMEM_ALLOC_GROW &
763 ~SNS_OCMEM_ALLOC_SHRINK;
764 spin_unlock_irqrestore(&sns_ctl.sns_lock, flags);
765 sns_ctl.buf = ocmem_allocate_nb(SNS_OCMEM_CLIENT_ID,
766 SNS_OCMEM_SIZE);
767
768 if (sns_ctl.buf == NULL) {
769 pr_err("ocmem_allocate_nb returned NULL\n");
770 sns_ctl.ocmem_enabled = false;
771 err = -EFAULT;
772 } else if (sns_ctl.buf->len != 0 &&
773 SNS_OCMEM_SIZE > sns_ctl.buf->len) {
774 pr_err("ocmem_allocate_nb: invalid len %li, Req: %i)\n",
775 sns_ctl.buf->len, SNS_OCMEM_SIZE);
776 sns_ctl.ocmem_enabled = false;
777 err = -EFAULT;
778 }
779 }
780
781 pr_debug("%s OCMEM buf=%lx, buffer len=%li\n", __func__,
782 sns_ctl.buf->addr, sns_ctl.buf->len);
783
784 while (sns_ctl.ocmem_enabled) {
785 if (sns_ctl.buf->len == 0) {
786 pr_debug("%s: Waiting for memory allocation\n",
787 __func__);
788 err = sns_ocmem_wait_for_alloc();
789 if (err == -EPIPE) {
790 pr_debug("%s:Lost client while wait for alloc\n",
791 __func__);
792 break;
793 } else if (err != 0) {
794 pr_err("sns_ocmem_wait_for_alloc failed %i\n",
795 err);
796 break;
797 }
798 }
799
800 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
801 sns_ctl.buf,
802 OCMEM_ON);
803
804 err = sns_ocmem_map();
805
806 if (err == -EPIPE) {
807 pr_debug("%s: Lost client while waiting for mapping\n",
808 __func__);
809 break;
810 } else if (err < 0) {
811 pr_debug("%s: Mapping failed, will try again\n",
812 __func__);
813 break;
814 } else if (err == 0) {
815 pr_debug("%s: Mapping finished\n", __func__);
816 break;
817 }
818 }
819
820 return err;
821}
822
Neeti Desaia07e87d2012-12-18 18:26:31 -0800823/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700824 * Indicate to the ADSP that unmapping has completed, and wait for the response
825 * that its bandwidth vote has been removed.
826 *
827 * @return 0 Upon success; < 0 upon error
828 */
829static int sns_ocmem_unmap_send(void)
830{
831 int err;
832 struct sns_ocmem_hdr_s msg_hdr;
833 struct sns_ocmem_bw_vote_req_msg_v01 msg;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700834
835 memset(&msg, 0, sizeof(struct sns_ocmem_bw_vote_req_msg_v01));
836
837 msg_hdr.msg_id = SNS_OCMEM_BW_VOTE_REQ_V01;
838 msg_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
839 msg_hdr.msg_size = sizeof(struct sns_ocmem_bw_vote_req_msg_v01);
840 msg.is_map = 0;
841 msg.vectors_valid = 0;
842 msg.vectors_len = 0;
843
844 pr_debug("%s: send bw_vote OFF\n", __func__);
845 err = sns_ocmem_send_msg(&msg_hdr, &msg);
846 if (err != 0) {
847 pr_err("%s: sns_ocmem_send_msg failed %i\n",
848 __func__, err);
849 } else {
850 err = sns_ocmem_wait(DSPS_BW_VOTE_OFF, 0);
851 if (err != 0)
852 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
853 }
854
855 return err;
856}
857
Neeti Desaia07e87d2012-12-18 18:26:31 -0800858/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700859 * Indicate to the ADSP that mapping has completed, and wait for the response
860 * that its bandwidth vote has been made.
861 *
862 * @return 0 Upon success; < 0 upon error
863 */
864static int sns_ocmem_map_send(void)
865{
866 int err;
867 struct sns_ocmem_hdr_s msg_hdr;
868 struct sns_ocmem_bw_vote_req_msg_v01 msg;
869 struct ocmem_vectors *vectors;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700870
871 memset(&msg, 0, sizeof(struct sns_ocmem_bw_vote_req_msg_v01));
872
873 msg_hdr.msg_id = SNS_OCMEM_BW_VOTE_REQ_V01;
874 msg_hdr.msg_type = SNS_OCMEM_MSG_TYPE_REQ;
875 msg_hdr.msg_size = sizeof(struct sns_ocmem_bw_vote_req_msg_v01);
876 msg.is_map = 1;
877
878 vectors = ocmem_get_vectors(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
879 if ((vectors != NULL)) {
Satya Durga Srinivasu Prabhalaf38015e2013-10-29 13:50:42 -0700880 memcpy(&msg.vectors, vectors, sizeof(*vectors));
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700881 /* TODO: set vectors_len */
882 msg.vectors_valid = true;
883 msg.vectors_len = 0;
884 }
885
886 pr_debug("%s: send bw_vote ON\n", __func__);
887 err = sns_ocmem_send_msg(&msg_hdr, &msg);
888 if (err != 0) {
889 pr_err("%s: sns_ocmem_send_msg failed %i\n", __func__, err);
890 } else {
891 err = sns_ocmem_wait(DSPS_BW_VOTE_ON |
892 SNS_OCMEM_ALLOC_SHRINK, 0);
893 if (err != 0)
894 pr_err("%s: sns_ocmem_wait failed %i\n", __func__, err);
895 }
896
897 return err;
898}
899
Neeti Desaia07e87d2012-12-18 18:26:31 -0800900/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700901 * Perform the encessary operations to clean-up OCMEM after being notified that
902 * there is no longer a client; if sensors was evicted; or if some error
903 * has occurred.
904 *
905 * @param[i] do_free Whether the memory should be freed (true) or if shrink
906 * should be called instead (false).
907 */
908static void sns_ocmem_evicted(bool do_free)
909{
910 int err = 0;
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700911
912 sns_ocmem_unmap();
913 if (do_free) {
914 ocmem_free(SNS_OCMEM_CLIENT_ID, sns_ctl.buf);
915 sns_ctl.buf = NULL;
916 } else {
917 err = ocmem_shrink(SNS_OCMEM_CLIENT_ID, sns_ctl.buf, 0);
918 BUG_ON(err != 0);
919 }
920
921 err = sns_ocmem_unmap_send();
922 if (err != 0)
923 pr_err("sns_ocmem_unmap_send failed %i\n", err);
924}
925
Neeti Desaia07e87d2012-12-18 18:26:31 -0800926/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700927 * After mapping has completed and the ADSP has reacted appropriately, wait
928 * for a shrink command or word from the ADSP that it no longer has a client.
929 *
930 * @return 0 If no clients; < 0 upon error;
931 */
932static int sns_ocmem_map_done(void)
933{
934 int err = 0;
935 unsigned long flags;
936
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700937 err = sns_ocmem_map_send();
938 if (err != 0) {
939 pr_err("sns_ocmem_map_send failed %i\n", err);
940 sns_ocmem_evicted(true);
941 } else {
942 ocmem_set_power_state(SNS_OCMEM_CLIENT_ID,
943 sns_ctl.buf, OCMEM_OFF);
944
945 pr_debug("%s: Waiting for shrink or 'no client' updates\n",
946 __func__);
947 err = sns_ocmem_wait(DSPS_HAS_NO_CLIENT |
948 SNS_OCMEM_ALLOC_SHRINK, 0);
949 if (err == 0) {
950 if (sns_ocmem_is_status_set(DSPS_HAS_NO_CLIENT)) {
951 pr_debug("%s: No longer have a client\n",
952 __func__);
953 sns_ocmem_evicted(true);
954 } else if (sns_ocmem_is_status_set(
955 SNS_OCMEM_ALLOC_SHRINK)) {
956 pr_debug("%s: Received SHRINK\n", __func__);
957 sns_ocmem_evicted(false);
958
959 spin_lock_irqsave(&sns_ctl.sns_lock, flags);
960 sns_ctl.sns_ocmem_status &=
961 ~SNS_OCMEM_ALLOC_SHRINK;
962 spin_unlock_irqrestore(&sns_ctl.sns_lock,
963 flags);
964 err = -EFAULT;
965 }
966 } else {
967 pr_err("sns_ocmem_wait failed %i\n", err);
968 }
969 }
970
971 return err;
972}
973
Neeti Desaia07e87d2012-12-18 18:26:31 -0800974/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700975 * Main function.
976 * Initializes sensors ocmem feature, and waits for an ADSP client.
Naveen Ramaraj88f23632012-10-10 12:23:29 -0700977 */
978static void sns_ocmem_main(struct work_struct *work)
979{
980 int err = 0;
981 pr_debug("%s\n", __func__);
982
983 err = sns_ocmem_init();
984 if (err != 0) {
985 pr_err("%s: sns_ocmem_init failed %i\n", __func__, err);
986 return;
987 }
988
989 while (true) {
990 pr_debug("%s: Waiting for sensor client\n", __func__);
991 if (sns_ocmem_is_status_set(DSPS_HAS_CLIENT) ||
992 !sns_ocmem_wait(DSPS_HAS_CLIENT, 0)) {
993 pr_debug("%s: DSPS_HAS_CLIENT\n", __func__);
994
995 err = sns_ocmem_alloc();
996 if (err != 0) {
997 pr_err("sns_ocmem_alloc failed %i\n", err);
998 return;
999 } else {
1000 err = sns_ocmem_map_done();
1001 if (err != 0) {
1002 pr_err("sns_ocmem_map_done failed %i",
1003 err);
1004 return;
1005 }
1006 }
1007 }
1008 }
1009
1010 ocmem_notifier_unregister(sns_ctl.ocmem_handle,
1011 &sns_ctl.ocmem_nb);
1012}
1013
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001014static int sensors_adsp_open(struct inode *ip, struct file *fp)
1015{
1016 int ret = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001017 return ret;
1018}
1019
1020static int sensors_adsp_release(struct inode *inode, struct file *file)
1021{
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001022 return 0;
1023}
1024
Neeti Desaia07e87d2012-12-18 18:26:31 -08001025/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001026 * Read QTimer clock ticks and scale down to 32KHz clock as used
1027 * in DSPS
1028 */
1029static u32 sns_read_qtimer(void)
1030{
1031 u64 val;
1032 val = arch_counter_get_cntpct();
1033 /*
1034 * To convert ticks from 19.2 Mhz clock to 32768 Hz clock:
1035 * x = (value * 32768) / 19200000
1036 * This is same as first left shift the value by 4 bits, i.e. mutiply
1037 * by 16, and then divide by 9375. The latter is preferable since
1038 * QTimer tick (value) is 56-bit, so (value * 32768) could overflow,
1039 * while (value * 16) will never do
1040 */
1041 val <<= 4;
1042 do_div(val, 9375);
1043
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001044 return (u32)val;
1045}
1046
Neeti Desaia07e87d2012-12-18 18:26:31 -08001047/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001048 * IO Control - handle commands from client.
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001049 */
1050static long sensors_adsp_ioctl(struct file *file,
1051 unsigned int cmd, unsigned long arg)
1052{
1053 int ret = 0;
1054 u32 val = 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001055
1056 switch (cmd) {
1057 case DSPS_IOCTL_READ_SLOW_TIMER:
1058 val = sns_read_qtimer();
1059 ret = put_user(val, (u32 __user *) arg);
1060 break;
1061
1062 default:
1063 ret = -EINVAL;
1064 break;
1065 }
1066
1067 return ret;
1068}
1069
Neeti Desaia07e87d2012-12-18 18:26:31 -08001070/*
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001071 * platform driver
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001072 */
1073const struct file_operations sensors_adsp_fops = {
1074 .owner = THIS_MODULE,
1075 .open = sensors_adsp_open,
1076 .release = sensors_adsp_release,
1077 .unlocked_ioctl = sensors_adsp_ioctl,
1078};
1079
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001080static int sensors_adsp_probe(struct platform_device *pdev)
1081{
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001082 int ret = 0;
Jie Chengab0a2132013-11-11 13:40:11 +08001083 sns_ctl.dev_class = class_create(THIS_MODULE, CLASS_NAME);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001084 if (sns_ctl.dev_class == NULL) {
1085 pr_err("%s: class_create fail.\n", __func__);
1086 goto res_err;
1087 }
1088
1089 ret = alloc_chrdev_region(&sns_ctl.dev_num, 0, 1, DRV_NAME);
1090 if (ret) {
1091 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
1092 goto alloc_chrdev_region_err;
1093 }
1094
1095 sns_ctl.dev = device_create(sns_ctl.dev_class, NULL,
1096 sns_ctl.dev_num,
1097 &sns_ctl, DRV_NAME);
1098 if (IS_ERR(sns_ctl.dev)) {
1099 pr_err("%s: device_create fail.\n", __func__);
1100 goto device_create_err;
1101 }
1102
1103 sns_ctl.cdev = cdev_alloc();
1104 if (sns_ctl.cdev == NULL) {
1105 pr_err("%s: cdev_alloc fail.\n", __func__);
1106 goto cdev_alloc_err;
1107 }
1108 cdev_init(sns_ctl.cdev, &sensors_adsp_fops);
1109 sns_ctl.cdev->owner = THIS_MODULE;
1110
1111 ret = cdev_add(sns_ctl.cdev, sns_ctl.dev_num, 1);
1112 if (ret) {
1113 pr_err("%s: cdev_add fail.\n", __func__);
1114 goto cdev_add_err;
1115 }
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001116
1117 sns_ctl.sns_workqueue =
1118 alloc_workqueue("sns_ocmem", WQ_NON_REENTRANT, 0);
1119 if (!sns_ctl.sns_workqueue) {
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001120 pr_err("%s: Failed to create work queue\n",
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001121 __func__);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001122 goto cdev_add_err;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001123 }
1124
Neeti Desaia07e87d2012-12-18 18:26:31 -08001125 sns_ctl.smd_wq =
1126 alloc_workqueue("smd_wq", WQ_NON_REENTRANT, 0);
1127 if (!sns_ctl.smd_wq) {
1128 pr_err("%s: Failed to create work queue\n",
1129 __func__);
1130 goto cdev_add_err;
1131 }
1132
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001133 init_waitqueue_head(&sns_ctl.sns_wait);
1134 spin_lock_init(&sns_ctl.sns_lock);
1135
1136 sns_ctl.ocmem_handle = NULL;
1137 sns_ctl.buf = NULL;
1138 sns_ctl.sns_ocmem_status = 0;
1139 sns_ctl.ocmem_enabled = true;
1140 sns_ctl.ocmem_nb.notifier_call = sns_ocmem_drv_cb;
1141 sns_ctl.smd_ch = NULL;
1142 sns_ctl.pdev = pdev;
1143
1144 INIT_WORK(&sns_ctl.sns_work, sns_ocmem_main);
Neeti Desaia07e87d2012-12-18 18:26:31 -08001145 INIT_WORK(&sns_ctl.smd_read_work, sns_ocmem_smd_read);
1146
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001147 queue_work(sns_ctl.sns_workqueue, &sns_ctl.sns_work);
1148
1149 return 0;
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001150
1151cdev_add_err:
1152 kfree(sns_ctl.cdev);
1153cdev_alloc_err:
1154 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
1155device_create_err:
1156 unregister_chrdev_region(sns_ctl.dev_num, 1);
1157alloc_chrdev_region_err:
1158 class_destroy(sns_ctl.dev_class);
1159res_err:
1160 return -ENODEV;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001161}
1162
1163static int sensors_adsp_remove(struct platform_device *pdev)
1164{
1165 struct msm_bus_scale_pdata *sns_ocmem_bus_scale_pdata = NULL;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001166
1167 sns_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
1168 dev_get_drvdata(&pdev->dev);
1169
1170 kfree(sns_ocmem_bus_scale_pdata->usecase->vectors);
1171 kfree(sns_ocmem_bus_scale_pdata->usecase);
1172 kfree(sns_ocmem_bus_scale_pdata);
1173
1174 ocmem_notifier_unregister(sns_ctl.ocmem_handle,
1175 &sns_ctl.ocmem_nb);
1176 destroy_workqueue(sns_ctl.sns_workqueue);
Neeti Desaia07e87d2012-12-18 18:26:31 -08001177 destroy_workqueue(sns_ctl.smd_wq);
Naveen Ramaraj93060a42012-11-15 23:23:55 -08001178
1179 cdev_del(sns_ctl.cdev);
1180 kfree(sns_ctl.cdev);
1181 sns_ctl.cdev = NULL;
1182 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
1183 unregister_chrdev_region(sns_ctl.dev_num, 1);
1184 class_destroy(sns_ctl.dev_class);
1185
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001186 return 0;
1187}
1188
1189static const struct of_device_id msm_adsp_sensors_dt_match[] = {
1190 {.compatible = "qcom,msm-adsp-sensors"},
1191 {}
1192};
1193MODULE_DEVICE_TABLE(of, msm_adsp_sensors_dt_match);
1194
1195
1196static struct platform_driver sensors_adsp_driver = {
1197 .driver = {
1198 .name = "sensors-adsp",
1199 .owner = THIS_MODULE,
1200 .of_match_table = msm_adsp_sensors_dt_match,
1201 },
1202 .probe = sensors_adsp_probe,
1203 .remove = sensors_adsp_remove,
1204};
1205
Neeti Desaia07e87d2012-12-18 18:26:31 -08001206/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001207 * Module Init.
1208 */
1209static int sensors_adsp_init(void)
1210{
1211 int rc;
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001212 pr_debug("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
1213
1214 rc = platform_driver_register(&sensors_adsp_driver);
1215
1216 if (rc) {
1217 pr_err("%s: Failed to register sensors adsp driver\n",
1218 __func__);
1219 return rc;
1220 }
1221
1222 return 0;
1223}
1224
Neeti Desaia07e87d2012-12-18 18:26:31 -08001225/*
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001226 * Module Exit.
1227 */
1228static void sensors_adsp_exit(void)
1229{
Naveen Ramaraj88f23632012-10-10 12:23:29 -07001230 platform_driver_unregister(&sensors_adsp_driver);
1231}
1232
1233module_init(sensors_adsp_init);
1234module_exit(sensors_adsp_exit);
1235
1236MODULE_LICENSE("GPL v2");
1237MODULE_DESCRIPTION("Sensors ADSP driver");