blob: bfba40142effe11001bfb0f62cd75f97aa9c58bf [file] [log] [blame]
Bhalchandra Gajarec77b19f2018-03-09 17:22:33 -08001/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/platform_device.h>
17#include <linux/cdev.h>
18#include <linux/proc_fs.h>
19#include <linux/poll.h>
20#include <linux/slab.h>
21#include <linux/notifier.h>
22#include <linux/wcd-spi-ac-params.h>
23#include <soc/wcd-spi-ac.h>
24#include <soc/qcom/msm_qmi_interface.h>
25
26#include "wcd_spi_ctl_v01.h"
27
28#define WCD_SPI_AC_PFS_ENTRY_MAX_LEN 16
29#define WCD_SPI_AC_WRITE_CMD_MIN_SIZE \
30 (sizeof(struct wcd_spi_ac_write_cmd))
31#define WCD_SPI_AC_WRITE_CMD_MAX_SIZE \
32 (WCD_SPI_AC_WRITE_CMD_MIN_SIZE + \
33 (WCD_SPI_AC_MAX_BUFFERS * \
34 sizeof(struct wcd_spi_ac_buf_data)))
35
36#define WCD_SPI_AC_MUTEX_LOCK(dev, lock) \
37{ \
38 dev_dbg(dev, "%s: mutex_lock(%s)\n", \
39 __func__, __stringify_1(lock)); \
40 mutex_lock(&lock); \
41}
42
43#define WCD_SPI_AC_MUTEX_UNLOCK(dev, lock) \
44{ \
45 dev_dbg(dev, "%s: mutex_unlock(%s)\n", \
46 __func__, __stringify_1(lock)); \
47 mutex_unlock(&lock); \
48}
49
50/*
51 * All bits of status should be cleared for SPI access
52 * to be released.
53 */
54#define WCD_SPI_AC_STATUS_RELEASE_ACCESS 0x00
55#define WCD_SPI_AC_LOCAL_ACCESS 0x00
56#define WCD_SPI_AC_REMOTE_ACCESS 0x01
57#define WCD_SPI_CTL_INS_ID 0
58#define WCD_SPI_AC_QMI_TIMEOUT_MS 100
59
60struct wcd_spi_ac_priv {
61
62 /* Pointer to device for this driver */
63 struct device *dev;
64
65 /* Pointer to parent's device */
66 struct device *parent;
67
68 /* char dev related */
69 struct class *cls;
70 struct device *chardev;
71 struct cdev cdev;
72 dev_t cdev_num;
73
74 /* proc entry related */
75 struct proc_dir_entry *pfs_root;
76 struct proc_dir_entry *pfs_status;
77
78 /* service status related */
79 u8 svc_offline;
80 u8 svc_offline_change;
81 wait_queue_head_t svc_poll_wait;
82 struct mutex status_lock;
83
84 /* state maintenence related */
85 u32 state;
86 struct mutex state_lock;
87 u8 current_access;
88
89 /* qmi related */
90 struct qmi_handle *qmi_hdl;
91 struct work_struct svc_arr_work;
92 struct work_struct svc_exit_work;
93 struct notifier_block nb;
94 struct mutex svc_lock;
95 struct workqueue_struct *qmi_wq;
96 struct work_struct recv_msg_work;
97};
98
99
100static void wcd_spi_ac_status_change(struct wcd_spi_ac_priv *ac,
101 u8 online)
102{
103 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
104 ac->svc_offline = !online;
105 /* Make sure the write is complete */
106 wmb();
107 xchg(&ac->svc_offline_change, 1);
108 wake_up_interruptible(&ac->svc_poll_wait);
109 dev_dbg(ac->dev,
110 "%s request %u offline %u off_change %u\n",
111 __func__, online, ac->svc_offline,
112 ac->svc_offline_change);
113 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
114}
115
116static int wcd_spi_ac_status_open(struct inode *inode,
117 struct file *file)
118{
119 struct wcd_spi_ac_priv *ac = PDE_DATA(inode);
120
121 file->private_data = ac;
122
123 return 0;
124}
125
126static ssize_t wcd_spi_ac_status_read(struct file *file,
127 char __user *buffer,
128 size_t count, loff_t *offset)
129{
130 struct wcd_spi_ac_priv *ac;
131 char buf[WCD_SPI_AC_PFS_ENTRY_MAX_LEN];
132 int len, ret;
133 u8 offline;
134
135 ac = (struct wcd_spi_ac_priv *) file->private_data;
136 if (!ac) {
137 pr_err("%s: Invalid private data for status\n",
138 __func__);
139 return -EINVAL;
140 }
141
142 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
143 offline = ac->svc_offline;
144 /* Make sure the read is complete */
145 rmb();
146 dev_dbg(ac->dev, "%s: offline = %sline\n",
147 __func__, offline ? "off" : "on");
148 len = snprintf(buf, sizeof(buf), "%s\n",
149 offline ? "OFFLINE" : "ONLINE");
150 ret = simple_read_from_buffer(buffer, count, offset, buf, len);
151 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
152
153 return ret;
154}
155
156static unsigned int wcd_spi_ac_status_poll(struct file *file,
157 poll_table *wait)
158{
159 struct wcd_spi_ac_priv *ac;
160 unsigned int ret = 0;
161
162 ac = (struct wcd_spi_ac_priv *) file->private_data;
163 if (!ac) {
164 pr_err("%s: Invalid private data for status\n",
165 __func__);
166 return -EINVAL;
167 }
168
169 dev_dbg(ac->dev, "%s: Poll wait, svc = %s\n",
170 __func__, ac->svc_offline ? "offline" : "online");
171 poll_wait(file, &ac->svc_poll_wait, wait);
172 dev_dbg(ac->dev, "%s: Woken up Poll wait, svc = %s\n",
173 __func__, ac->svc_offline ? "offline" : "online");
174
175 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
176 if (xchg(&ac->svc_offline_change, 0))
177 ret = POLLIN | POLLPRI | POLLRDNORM;
178 dev_dbg(ac->dev, "%s: ret (%d) from poll_wait\n",
179 __func__, ret);
180 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
181
182 return ret;
183}
184
185static const struct file_operations wcd_spi_ac_status_ops = {
186 .owner = THIS_MODULE,
187 .open = wcd_spi_ac_status_open,
188 .read = wcd_spi_ac_status_read,
189 .poll = wcd_spi_ac_status_poll,
190};
191
192static int wcd_spi_ac_procfs_init(struct wcd_spi_ac_priv *ac)
193{
194 int ret = 0;
195
196 ac->pfs_root = proc_mkdir(WCD_SPI_AC_PROCFS_DIR_NAME, NULL);
197 if (!ac->pfs_root) {
198 dev_err(ac->dev, "%s: proc_mkdir failed\n", __func__);
199 return -EINVAL;
200 }
201
202 ac->pfs_status = proc_create_data(WCD_SPI_AC_PROCFS_STATE_NAME,
203 0444, ac->pfs_root,
204 &wcd_spi_ac_status_ops,
205 ac);
206 if (!ac->pfs_status) {
207 dev_err(ac->dev, "%s: proc_create_data failed\n",
208 __func__);
209 ret = -EINVAL;
210 goto rmdir_root;
211 }
212
213 proc_set_size(ac->pfs_status, WCD_SPI_AC_PFS_ENTRY_MAX_LEN);
214
215 return 0;
216
217rmdir_root:
218 proc_remove(ac->pfs_root);
219 return ret;
220}
221
222static void wcd_spi_ac_procfs_deinit(struct wcd_spi_ac_priv *ac)
223{
224 proc_remove(ac->pfs_status);
225 proc_remove(ac->pfs_root);
226}
227
228static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac,
229 bool is_svc_locked)
230{
231 struct wcd_spi_req_access_msg_v01 req;
232 struct wcd_spi_req_access_resp_v01 rsp;
233 struct msg_desc req_desc, rsp_desc;
234 int ret = 0;
235
236 dev_dbg(ac->dev, "%s: is_svc_locked = %s\n",
237 __func__, is_svc_locked ? "true" : "false");
238
239 memset(&req, 0, sizeof(req));
240 memset(&rsp, 0, sizeof(rsp));
241
242 req.reason_valid = 1;
243 req.reason = ac->state & 0x03;
244
245 req_desc.max_msg_len = WCD_SPI_REQ_ACCESS_MSG_V01_MAX_MSG_LEN;
246 req_desc.msg_id = WCD_SPI_REQ_ACCESS_MSG_V01;
247 req_desc.ei_array = wcd_spi_req_access_msg_v01_ei;
248
249 rsp_desc.max_msg_len = WCD_SPI_REQ_ACCESS_RESP_V01_MAX_MSG_LEN;
250 rsp_desc.msg_id = WCD_SPI_REQ_ACCESS_RESP_V01;
251 rsp_desc.ei_array = wcd_spi_req_access_resp_v01_ei;
252
253 if (!is_svc_locked)
254 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
255
256 ret = qmi_send_req_wait(ac->qmi_hdl,
257 &req_desc, &req, sizeof(req),
258 &rsp_desc, &rsp, sizeof(rsp),
259 WCD_SPI_AC_QMI_TIMEOUT_MS);
260 if (ret) {
261 dev_err(ac->dev, "%s: msg send failed %d\n",
262 __func__, ret);
263 goto done;
264 }
265
266 if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) {
267 ret = -EIO;
268 dev_err(ac->dev, "%s: qmi resp error %d\n",
269 __func__, rsp.resp.result);
270 }
271done:
272 if (!is_svc_locked)
273 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
274
275 return ret;
276}
277
278static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac,
279 bool is_svc_locked)
280{
281 struct wcd_spi_rel_access_msg_v01 req;
282 struct wcd_spi_rel_access_resp_v01 rsp;
283 struct msg_desc req_desc, rsp_desc;
284 int ret = 0;
285
286 dev_dbg(ac->dev, "%s: is_svc_locked = %s\n",
287 __func__, is_svc_locked ? "true" : "false");
288
289 memset(&req, 0, sizeof(req));
290 memset(&rsp, 0, sizeof(rsp));
291
292 req_desc.max_msg_len = WCD_SPI_REL_ACCESS_MSG_V01_MAX_MSG_LEN;
293 req_desc.msg_id = WCD_SPI_REL_ACCESS_MSG_V01;
294 req_desc.ei_array = wcd_spi_rel_access_msg_v01_ei;
295
296 rsp_desc.max_msg_len = WCD_SPI_REL_ACCESS_RESP_V01_MAX_MSG_LEN;
297 rsp_desc.msg_id = WCD_SPI_REL_ACCESS_RESP_V01;
298 rsp_desc.ei_array = wcd_spi_rel_access_resp_v01_ei;
299
300 if (!is_svc_locked)
301 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
302
303 ret = qmi_send_req_wait(ac->qmi_hdl,
304 &req_desc, &req, sizeof(req),
305 &rsp_desc, &rsp, sizeof(rsp),
306 WCD_SPI_AC_QMI_TIMEOUT_MS);
307 if (ret) {
308 dev_err(ac->dev, "%s: msg send failed %d\n",
309 __func__, ret);
310 goto done;
311 }
312
313 if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) {
314 ret = -EIO;
315 dev_err(ac->dev, "%s: qmi resp error %d\n",
316 __func__, rsp.resp.result);
317 }
318done:
319 if (!is_svc_locked)
320 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
321 return ret;
322}
323
324static int wcd_spi_ac_buf_msg(
325 struct wcd_spi_ac_priv *ac,
326 u8 *data, int data_sz)
327{
328 struct wcd_spi_ac_buf_data *buf_data;
329 struct wcd_spi_buff_msg_v01 req;
330 struct wcd_spi_buff_resp_v01 rsp;
331 struct msg_desc req_desc, rsp_desc;
332 int ret = 0;
333
334 memset(&req, 0, sizeof(req));
335 memset(&rsp, 0, sizeof(rsp));
336
337 buf_data = (struct wcd_spi_ac_buf_data *) data;
338 memcpy(req.buff_addr_1, buf_data,
339 sizeof(*buf_data));
340
341 if (data_sz - sizeof(*buf_data) != 0) {
342 req.buff_addr_2_valid = 1;
343 buf_data++;
344 memcpy(req.buff_addr_2, buf_data,
345 sizeof(*buf_data));
346 }
347
348 req_desc.max_msg_len = WCD_SPI_BUFF_MSG_V01_MAX_MSG_LEN;
349 req_desc.msg_id = WCD_SPI_BUFF_MSG_V01;
350 req_desc.ei_array = wcd_spi_buff_msg_v01_ei;
351
352 rsp_desc.max_msg_len = WCD_SPI_BUFF_RESP_V01_MAX_MSG_LEN;
353 rsp_desc.msg_id = WCD_SPI_BUFF_RESP_V01;
354 rsp_desc.ei_array = wcd_spi_buff_resp_v01_ei;
355
356 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
357 ret = qmi_send_req_wait(ac->qmi_hdl,
358 &req_desc, &req, sizeof(req),
359 &rsp_desc, &rsp, sizeof(rsp),
360 WCD_SPI_AC_QMI_TIMEOUT_MS);
361
362 if (ret) {
363 dev_err(ac->dev, "%s: msg send failed %d\n",
364 __func__, ret);
365 goto done;
366 }
367
368 if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) {
369 ret = -EIO;
370 dev_err(ac->dev, "%s: qmi resp error %d\n",
371 __func__, rsp.resp.result);
372 }
373done:
374 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
375 return ret;
376
377}
378
379/*
380 * wcd_spi_ac_set_sync: Sets the current status of the SPI
381 * bus and requests access if not
382 * already accesible.
383 * @ac: pointer to the drivers private data
384 * @value: value to be set in the status mask
385 * @is_svc_locked: flag to indicate if svc_lock is acquired by caller
386 */
387static int wcd_spi_ac_set_sync(struct wcd_spi_ac_priv *ac,
388 u32 value, bool is_svc_locked)
389{
390 int ret = 0;
391
392 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->state_lock);
393 ac->state |= value;
394 /* any non-zero state indicates us to request SPI access */
395 wmb();
396 dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n",
397 __func__, ac->state, ac->current_access);
398 if (ac->current_access == WCD_SPI_AC_REMOTE_ACCESS) {
399 dev_dbg(ac->dev,
400 "%s: requesting access, state = 0x%x\n",
401 __func__, ac->state);
402 ret = wcd_spi_ac_request_access(ac, is_svc_locked);
403 if (!ret)
404 ac->current_access = WCD_SPI_AC_LOCAL_ACCESS;
405 }
406 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock);
407
408 return ret;
409}
410
411/*
412 * wcd_spi_ac_clear_sync: Clears the current status of the SPI
413 * bus and releases access if applicable
414 * @ac: pointer to the drivers private data
415 * @value: value to be cleared in the status mask
416 * @is_svc_locked: flag to indicate if svc_lock is acquired by caller
417 */
418static int wcd_spi_ac_clear_sync(struct wcd_spi_ac_priv *ac,
419 u32 value, bool is_svc_locked)
420{
421 int ret = 0;
422
423 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->state_lock);
424 ac->state &= ~(value);
425 /* make sure value is written before read */
426 wmb();
427 dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n",
428 __func__, ac->state, ac->current_access);
429 /* state should be zero to release SPI access */
430 if (!ac->state &&
431 ac->current_access == WCD_SPI_AC_LOCAL_ACCESS) {
432 dev_dbg(ac->dev,
433 "%s: releasing access, state = 0x%x\n",
434 __func__, ac->state);
435 ret = wcd_spi_ac_release_access(ac, is_svc_locked);
436 if (!ret)
437 ac->current_access = WCD_SPI_AC_REMOTE_ACCESS;
438 }
439 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock);
440
441 return ret;
442
443}
444
445/*
446 * wcd_spi_access_ctl: API to request/release the access
447 * to wcd-spi bus.
448 * @dev: handle to the wcd-spi-ac device
449 * @request: enum to indicate access request or access release
450 * @reason: reason for request/release. Must be one of the
451 * valid reasons.
452 * Returns success if the access handover was sucessful,
453 * negative error code otherwise.
454 */
455int wcd_spi_access_ctl(struct device *dev,
456 enum wcd_spi_acc_req request,
457 u32 reason)
458{
459 struct wcd_spi_ac_priv *ac;
460 int ret = 0;
461
462 if (!dev) {
463 pr_err("%s: invalid device\n", __func__);
464 return -EINVAL;
465 }
466
467 /* only data_transfer and remote_down are valid reasons */
468 if (reason != WCD_SPI_AC_DATA_TRANSFER &&
469 reason != WCD_SPI_AC_REMOTE_DOWN) {
470 pr_err("%s: Invalid reason 0x%x\n",
471 __func__, reason);
472 return -EINVAL;
473 }
474
475 ac = (struct wcd_spi_ac_priv *) dev_get_drvdata(dev);
476 if (!ac) {
477 dev_err(dev, "%s: invalid driver data\n", __func__);
478 return -EINVAL;
479 }
480
481 dev_dbg(dev, "%s: request = 0x%x, reason = 0x%x\n",
482 __func__, request, reason);
483
484 switch (request) {
485 case WCD_SPI_ACCESS_REQUEST:
486 ret = wcd_spi_ac_set_sync(ac, reason, false);
487 if (ret)
488 dev_err(dev, "%s: set_sync(0x%x) failed %d\n",
489 __func__, reason, ret);
490 break;
491 case WCD_SPI_ACCESS_RELEASE:
492 ret = wcd_spi_ac_clear_sync(ac, reason, false);
493 if (ret)
494 dev_err(dev, "%s: clear_sync(0x%x) failed %d\n",
495 __func__, reason, ret);
496 break;
497 default:
498 dev_err(dev, "%s: invalid request 0x%x\n",
499 __func__, request);
500 break;
501 }
502
503 return ret;
504}
505EXPORT_SYMBOL(wcd_spi_access_ctl);
506
507static int wcd_spi_ac_cdev_open(struct inode *inode,
508 struct file *file)
509{
510 struct wcd_spi_ac_priv *ac;
511 int ret = 0;
512
513 ac = container_of(inode->i_cdev, struct wcd_spi_ac_priv, cdev);
514 if (!ac) {
515 pr_err("%s: Invalid private data\n", __func__);
516 return -EINVAL;
517 }
518
519 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
520 if (ac->svc_offline) {
521 dev_err(ac->dev, "%s: SVC is not online, cannot open driver\n",
522 __func__);
523 ret = -ENODEV;
524 goto done;
525 }
526
527 file->private_data = ac;
528
529done:
530 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
531 return ret;
532}
533
534static ssize_t wcd_spi_ac_cdev_write(struct file *file,
535 const char __user *buf,
536 size_t count,
537 loff_t *ppos)
538{
539 struct wcd_spi_ac_priv *ac;
540 struct wcd_spi_ac_write_cmd *cmd_buf;
541 int ret = 0;
542 int data_sz;
543
544 ac = (struct wcd_spi_ac_priv *) file->private_data;
545 if (!ac) {
546 pr_err("%s: Invalid private data\n", __func__);
547 return -EINVAL;
548 }
549
550 if (count < WCD_SPI_AC_WRITE_CMD_MIN_SIZE ||
551 count > WCD_SPI_AC_WRITE_CMD_MAX_SIZE) {
552 dev_err(ac->dev, "%s: Invalid write count %zd\n",
553 __func__, count);
554 return -EINVAL;
555 }
556
557 cmd_buf = kzalloc(count, GFP_KERNEL);
558 if (!cmd_buf)
559 return -ENOMEM;
560
561 if (get_user(cmd_buf->cmd_type, buf)) {
562 dev_err(ac->dev, "%s: get_user failed\n", __func__);
563 ret = -EFAULT;
564 goto free_cmd_buf;
565 }
566
567 dev_dbg(ac->dev, "%s: write cmd type 0x%x\n",
568 __func__, cmd_buf->cmd_type);
569
570 switch (cmd_buf->cmd_type) {
571
572 case WCD_SPI_AC_CMD_CONC_BEGIN:
573 ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_CONCURRENCY, false);
574 if (ret) {
575 dev_err(ac->dev, "%s: set_sync(CONC) fail %d\n",
576 __func__, ret);
577 goto free_cmd_buf;
578 }
579
580 break;
581
582 case WCD_SPI_AC_CMD_CONC_END:
583 ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_CONCURRENCY, false);
584 if (ret) {
585 dev_err(ac->dev, "%s: clear_sync(CONC) fail %d\n",
586 __func__, ret);
587 goto free_cmd_buf;
588 }
589
590 break;
591
592 case WCD_SPI_AC_CMD_BUF_DATA:
593
594 /* Read the buffer details and send to service */
595 data_sz = count - sizeof(cmd_buf->cmd_type);
596
597 if (!data_sz ||
598 (data_sz % sizeof(struct wcd_spi_ac_buf_data))) {
599 dev_err(ac->dev, "%s: size %d not multiple of %ld\n",
600 __func__, data_sz,
601 sizeof(struct wcd_spi_ac_buf_data));
602 goto free_cmd_buf;
603 }
604
605 if (data_sz / sizeof(struct wcd_spi_ac_buf_data) >
606 WCD_SPI_AC_MAX_BUFFERS) {
607 dev_err(ac->dev, "%s: invalid size %d\n",
608 __func__, data_sz);
609 goto free_cmd_buf;
610 }
611
612 if (copy_from_user(cmd_buf->payload,
613 buf + sizeof(cmd_buf->cmd_type),
614 data_sz)) {
615 dev_err(ac->dev, "%s: copy_from_user failed\n",
616 __func__);
617 ret = -EFAULT;
618 goto free_cmd_buf;
619 }
620
621 ret = wcd_spi_ac_buf_msg(ac, cmd_buf->payload, data_sz);
622 if (ret) {
623 dev_err(ac->dev, "%s: _buf_msg failed %d\n",
624 __func__, ret);
625 goto free_cmd_buf;
626 }
627
628 ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_UNINITIALIZED,
629 false);
630 if (ret) {
631 dev_err(ac->dev, "%s: clear_sync 0x%lx failed %d\n",
632 __func__, WCD_SPI_AC_UNINITIALIZED, ret);
633 goto free_cmd_buf;
634 }
635 break;
636 default:
637 dev_err(ac->dev, "%s: Invalid cmd_type 0x%x\n",
638 __func__, cmd_buf->cmd_type);
639 ret = -EINVAL;
640 goto free_cmd_buf;
641 }
642
643free_cmd_buf:
644
645 kfree(cmd_buf);
646 if (!ret)
647 ret = count;
648
649 return ret;
650}
651
652static int wcd_spi_ac_cdev_release(struct inode *inode,
653 struct file *file)
654{
655 struct wcd_spi_ac_priv *ac;
656 int ret = 0;
657
658 ac = (struct wcd_spi_ac_priv *) file->private_data;
659 if (!ac) {
660 pr_err("%s: Invalid private data\n", __func__);
661 return -EINVAL;
662 }
663
664 ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_UNINITIALIZED, false);
665 if (ret)
666 dev_err(ac->dev, "%s: set_sync(UNINITIALIZED) failed %d\n",
667 __func__, ret);
668 return ret;
669}
670
671static const struct file_operations wcd_spi_ac_cdev_fops = {
672 .owner = THIS_MODULE,
673 .open = wcd_spi_ac_cdev_open,
674 .write = wcd_spi_ac_cdev_write,
675 .release = wcd_spi_ac_cdev_release,
676};
677
678static int wcd_spi_ac_reg_chardev(struct wcd_spi_ac_priv *ac)
679{
680 int ret;
681
682 ret = alloc_chrdev_region(&ac->cdev_num, 0, 1,
683 WCD_SPI_AC_CLIENT_CDEV_NAME);
684 if (ret) {
685 dev_err(ac->dev, "%s: alloc_chrdev_region failed %d\n",
686 __func__, ret);
687 return ret;
688 }
689
690 ac->cls = class_create(THIS_MODULE, WCD_SPI_AC_CLIENT_CDEV_NAME);
691 if (IS_ERR(ac->cls)) {
692 ret = PTR_ERR(ac->cls);
693 dev_err(ac->dev, "%s: class_create failed %d\n",
694 __func__, ret);
695 goto unregister_chrdev;
696 }
697
698 ac->chardev = device_create(ac->cls, NULL, ac->cdev_num,
699 NULL, WCD_SPI_AC_CLIENT_CDEV_NAME);
700 if (IS_ERR(ac->chardev)) {
701 ret = PTR_ERR(ac->chardev);
702 dev_err(ac->dev, "%s: device_create failed %d\n",
703 __func__, ret);
704 goto destroy_class;
705 }
706
707 cdev_init(&ac->cdev, &wcd_spi_ac_cdev_fops);
708 ret = cdev_add(&ac->cdev, ac->cdev_num, 1);
709 if (ret) {
710 dev_err(ac->dev, "%s: cdev_add failed %d\n",
711 __func__, ret);
712 goto destroy_device;
713 }
714
715 return 0;
716
717destroy_device:
718 device_destroy(ac->cls, ac->cdev_num);
719
720destroy_class:
721 class_destroy(ac->cls);
722
723unregister_chrdev:
724 unregister_chrdev_region(0, 1);
725 return ret;
726}
727
728static int wcd_spi_ac_unreg_chardev(struct wcd_spi_ac_priv *ac)
729{
730 cdev_del(&ac->cdev);
731 device_destroy(ac->cls, ac->cdev_num);
732 class_destroy(ac->cls);
733 unregister_chrdev_region(0, 1);
734
735 return 0;
736}
737
738static void wcd_spi_ac_recv_msg(struct work_struct *work)
739{
740 struct wcd_spi_ac_priv *ac;
741 int rc = 0;
742
743 ac = container_of(work, struct wcd_spi_ac_priv,
744 recv_msg_work);
745 if (!ac) {
746 pr_err("%s: Invalid private data\n", __func__);
747 return;
748 }
749
750 do {
751 dev_dbg(ac->dev, "%s: msg received, rc = %d\n",
752 __func__, rc);
753 } while ((rc = qmi_recv_msg(ac->qmi_hdl)) == 0);
754
755 if (rc != -ENOMSG)
756 dev_err(ac->dev, "%s: qmi_recv_msg failed %d\n",
757 __func__, rc);
758}
759
760static void wcd_spi_ac_clnt_notify(struct qmi_handle *hdl,
761 enum qmi_event_type event, void *priv_data)
762{
763 struct wcd_spi_ac_priv *ac;
764
765 if (!priv_data) {
766 pr_err("%s: Invalid private data\n", __func__);
767 return;
768 }
769
770 ac = (struct wcd_spi_ac_priv *) priv_data;
771
772 switch (event) {
773 case QMI_RECV_MSG:
774 queue_work(ac->qmi_wq, &ac->recv_msg_work);
775 break;
776 default:
777 break;
778 }
779}
780
781static void wcd_spi_ac_svc_arrive(struct work_struct *work)
782{
783 struct wcd_spi_ac_priv *ac;
784 int ret;
785
786 ac = container_of(work, struct wcd_spi_ac_priv,
787 svc_arr_work);
788 if (!ac) {
789 pr_err("%s: Invalid private data\n",
790 __func__);
791 return;
792 }
793
794 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
795 ac->qmi_hdl = qmi_handle_create(wcd_spi_ac_clnt_notify,
796 ac);
797 if (!ac->qmi_hdl) {
798 dev_err(ac->dev, "%s: qmi_handle_create failed\n",
799 __func__);
800 goto done;
801 }
802
803 ret = qmi_connect_to_service(ac->qmi_hdl,
804 WCD_SPI_CTL_SERVICE_ID_V01,
805 WCD_SPI_CTL_SERVICE_VERS_V01,
806 WCD_SPI_CTL_INS_ID);
807 if (ret) {
808 dev_err(ac->dev, "%s, cant connect to service, error %d\n",
809 __func__, ret);
810 qmi_handle_destroy(ac->qmi_hdl);
811 ac->qmi_hdl = NULL;
812 goto done;
813 }
814
815 /* Mark service as online */
816 wcd_spi_ac_status_change(ac, 1);
817
818 /*
819 * update the state and clear the WCD_SPI_AC_SVC_OFFLINE
820 * bit to indicate that the service is now online.
821 */
822 ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true);
823 if (ret)
824 dev_err(ac->dev, "%s: clear_sync(SVC_OFFLINE) failed %d\n",
825 __func__, ret);
826done:
827 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
828
829}
830
831static void wcd_spi_ac_svc_exit(struct work_struct *work)
832{
833 struct wcd_spi_ac_priv *ac;
834 int ret = 0;
835
836 ac = container_of(work, struct wcd_spi_ac_priv,
837 svc_exit_work);
838 if (!ac) {
839 pr_err("%s: Invalid private data\n",
840 __func__);
841 return;
842 }
843
844 WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
845 ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true);
846 if (ret)
847 dev_err(ac->dev, "%s: set_sync(SVC_OFFLINE) failed %d\n",
848 __func__, ret);
849 qmi_handle_destroy(ac->qmi_hdl);
850 ac->qmi_hdl = NULL;
851 wcd_spi_ac_status_change(ac, 0);
852 WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
853}
854
855static int wcd_spi_ac_svc_event(struct notifier_block *this,
856 unsigned long event,
857 void *data)
858{
859 struct wcd_spi_ac_priv *ac;
860
861 ac = container_of(this, struct wcd_spi_ac_priv, nb);
862 if (!ac) {
863 pr_err("%s: Invalid private data\n", __func__);
864 return -EINVAL;
865 }
866
867 dev_dbg(ac->dev, "%s: event = 0x%lx", __func__, event);
868
869 switch (event) {
870 case QMI_SERVER_ARRIVE:
871 schedule_work(&ac->svc_arr_work);
872 break;
873 case QMI_SERVER_EXIT:
874 schedule_work(&ac->svc_exit_work);
875 break;
876 default:
877 dev_err(ac->dev, "%s unhandled event %ld\n",
878 __func__, event);
879 break;
880 }
881
882 return 0;
883}
884
885static int wcd_spi_ac_probe(struct platform_device *pdev)
886{
887 struct wcd_spi_ac_priv *ac;
888 struct device *parent = pdev->dev.parent;
889 int ret = 0;
890
891 ac = devm_kzalloc(&pdev->dev, sizeof(*ac),
892 GFP_KERNEL);
893 if (!ac)
894 return -ENOMEM;
895
896 ac->dev = &pdev->dev;
897 ac->parent = parent;
898
899 ret = wcd_spi_ac_reg_chardev(ac);
900 if (ret)
901 return ret;
902
903 ret = wcd_spi_ac_procfs_init(ac);
904 if (ret)
905 goto unreg_chardev;
906
907 mutex_init(&ac->status_lock);
908 mutex_init(&ac->state_lock);
909 mutex_init(&ac->svc_lock);
910 init_waitqueue_head(&ac->svc_poll_wait);
911 ac->svc_offline = 1;
912 ac->state = (WCD_SPI_AC_SVC_OFFLINE |
913 WCD_SPI_AC_UNINITIALIZED);
914 ac->current_access = WCD_SPI_AC_LOCAL_ACCESS;
915
916 ac->nb.notifier_call = wcd_spi_ac_svc_event;
917 INIT_WORK(&ac->svc_arr_work, wcd_spi_ac_svc_arrive);
918 INIT_WORK(&ac->svc_exit_work, wcd_spi_ac_svc_exit);
919 INIT_WORK(&ac->recv_msg_work, wcd_spi_ac_recv_msg);
920
921 ac->qmi_wq = create_singlethread_workqueue("qmi_wq");
922 if (!ac->qmi_wq) {
923 dev_err(&pdev->dev,
924 "%s: create_singlethread_workqueue failed\n",
925 __func__);
926 goto deinit_procfs;
927 }
928
929 dev_set_drvdata(&pdev->dev, ac);
930
931 ret = qmi_svc_event_notifier_register(
932 WCD_SPI_CTL_SERVICE_ID_V01,
933 WCD_SPI_CTL_SERVICE_VERS_V01,
934 WCD_SPI_CTL_INS_ID,
935 &ac->nb);
936 if (ret) {
937 dev_err(&pdev->dev,
938 "%s: qmi_svc_event_notifier_register failed %d\n",
939 __func__, ret);
940 goto destroy_wq;
941 }
942
943 return 0;
944
945destroy_wq:
946 destroy_workqueue(ac->qmi_wq);
947 dev_set_drvdata(&pdev->dev, NULL);
948deinit_procfs:
949 wcd_spi_ac_procfs_deinit(ac);
950 mutex_destroy(&ac->status_lock);
951 mutex_destroy(&ac->state_lock);
952 mutex_destroy(&ac->svc_lock);
953unreg_chardev:
954 wcd_spi_ac_unreg_chardev(ac);
955 return ret;
956}
957
958static int wcd_spi_ac_remove(struct platform_device *pdev)
959{
960 struct wcd_spi_ac_priv *ac;
961
962 ac = dev_get_drvdata(&pdev->dev);
963 qmi_svc_event_notifier_unregister(
964 WCD_SPI_CTL_SERVICE_ID_V01,
965 WCD_SPI_CTL_SERVICE_VERS_V01,
966 WCD_SPI_CTL_INS_ID,
967 &ac->nb);
968 if (ac->qmi_wq)
969 destroy_workqueue(ac->qmi_wq);
970 wcd_spi_ac_unreg_chardev(ac);
971 wcd_spi_ac_procfs_deinit(ac);
972 mutex_destroy(&ac->status_lock);
973 mutex_destroy(&ac->state_lock);
974 mutex_destroy(&ac->svc_lock);
975
976 return 0;
977}
978
979static const struct of_device_id wcd_spi_ac_of_match[] = {
980 { .compatible = "qcom,wcd-spi-ac" },
981 { },
982};
983
984MODULE_DEVICE_TABLE(of, wcd_spi_ac_of_match);
985
986static struct platform_driver wcd_spi_ac_driver = {
987 .driver = {
988 .name = "qcom,wcd-spi-ac",
989 .of_match_table = wcd_spi_ac_of_match,
Xiaojun Sang53cd13a2018-06-29 15:14:37 +0800990 .suppress_bind_attrs = true,
Bhalchandra Gajarec77b19f2018-03-09 17:22:33 -0800991 },
992 .probe = wcd_spi_ac_probe,
993 .remove = wcd_spi_ac_remove,
994};
995
996module_platform_driver(wcd_spi_ac_driver);
997
998MODULE_DESCRIPTION("WCD SPI access control driver");
999MODULE_LICENSE("GPL v2");