blob: adc360eb77af30c5dce2f14cb56d0719c0a33ba3 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/sched.h>
16#include <linux/wait.h>
17#include <linux/workqueue.h>
18#include <mach/sdio_al.h>
19#include <mach/sdio_smem.h>
20
21static void sdio_smem_read(struct work_struct *work);
22
23static struct sdio_channel *channel;
24static struct workqueue_struct *workq;
25static DECLARE_WORK(work_read, sdio_smem_read);
26static DECLARE_WAIT_QUEUE_HEAD(waitq);
27static int bytes_avail;
Maya Erez2bfba2c2011-09-19 11:41:55 +030028static int sdio_ch_opened;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029
30static void sdio_smem_release(struct device *dev)
31{
32 pr_debug("sdio smem released\n");
33}
34
35static struct sdio_smem_client client;
36
37static void sdio_smem_read(struct work_struct *work)
38{
39 int err;
40 int read_avail;
41 char *data = client.buf;
42
Maya Erez2bfba2c2011-09-19 11:41:55 +030043 if (!sdio_ch_opened)
44 return;
45
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046 read_avail = sdio_read_avail(channel);
47 if (read_avail > bytes_avail ||
48 read_avail < 0) {
49 pr_err("Error: read_avail=%d bytes_avail=%d\n",
50 read_avail, bytes_avail);
Maya Erez2bfba2c2011-09-19 11:41:55 +030051 goto read_err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052 }
53
Maya Erez2bfba2c2011-09-19 11:41:55 +030054 if (read_avail == 0)
55 return;
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057 err = sdio_read(channel,
58 &data[client.size - bytes_avail],
59 read_avail);
Maya Erez2bfba2c2011-09-19 11:41:55 +030060 if (err) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 pr_err("sdio_read error (%d)", err);
Maya Erez2bfba2c2011-09-19 11:41:55 +030062 goto read_err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063 }
64
65 bytes_avail -= read_avail;
66 pr_debug("read %d bytes (bytes_avail = %d)\n",
67 read_avail, bytes_avail);
68
69 if (!bytes_avail) {
70 bytes_avail = client.size;
71 err = client.cb_func(SDIO_SMEM_EVENT_READ_DONE);
72 }
73 if (err)
74 pr_err("error (%d) on callback\n", err);
Maya Erez2bfba2c2011-09-19 11:41:55 +030075
76 return;
77
78read_err:
79 if (sdio_ch_opened)
80 client.cb_func(SDIO_SMEM_EVENT_READ_ERR);
81 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082}
83
84static void sdio_smem_notify(void *priv, unsigned event)
85{
86 pr_debug("%d event received\n", event);
87
88 if (event == SDIO_EVENT_DATA_READ_AVAIL ||
89 event == SDIO_EVENT_DATA_WRITE_AVAIL)
90 queue_work(workq, &work_read);
91}
92
93int sdio_smem_register_client(void)
94{
95 int err = 0;
96
97 if (!client.buf || !client.size || !client.cb_func)
98 return -EINVAL;
99
100 pr_debug("buf = %p\n", client.buf);
101 pr_debug("size = 0x%x\n", client.size);
102
103 bytes_avail = client.size;
104 workq = create_singlethread_workqueue("sdio_smem");
105 if (!workq)
106 return -ENOMEM;
107
Maya Erez2bfba2c2011-09-19 11:41:55 +0300108 sdio_ch_opened = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 err = sdio_open("SDIO_SMEM", &channel, NULL, sdio_smem_notify);
Maya Erez2bfba2c2011-09-19 11:41:55 +0300110 if (err) {
111 sdio_ch_opened = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 pr_err("sdio_open error (%d)\n", err);
113 destroy_workqueue(workq);
114 return err;
115 }
116 pr_debug("SDIO SMEM channel opened\n");
117 return err;
118}
119
120int sdio_smem_unregister_client(void)
121{
Maya Erez2bfba2c2011-09-19 11:41:55 +0300122 int err = 0;
123
124 sdio_ch_opened = 0;
125 err = sdio_close(channel);
126 if (err) {
127 pr_err("sdio_close error (%d)\n", err);
128 return err;
129 }
130 pr_debug("SDIO SMEM channel closed\n");
131 flush_workqueue(workq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 destroy_workqueue(workq);
133 bytes_avail = 0;
134 client.buf = NULL;
135 client.cb_func = NULL;
136 client.size = 0;
Maya Erez2bfba2c2011-09-19 11:41:55 +0300137
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138 return 0;
139}
140
141static int sdio_smem_probe(struct platform_device *pdev)
142{
143 client.plat_dev.name = "SDIO_SMEM_CLIENT";
144 client.plat_dev.id = -1;
145 client.plat_dev.dev.release = sdio_smem_release;
146
147 return platform_device_register(&client.plat_dev);
148}
149
150static int sdio_smem_remove(struct platform_device *pdev)
151{
152 platform_device_unregister(&client.plat_dev);
153 memset(&client, 0, sizeof(client));
Maya Erez2bfba2c2011-09-19 11:41:55 +0300154 sdio_ch_opened = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155 return 0;
156}
157static struct platform_driver sdio_smem_drv = {
158 .probe = sdio_smem_probe,
159 .remove = sdio_smem_remove,
160 .driver = {
161 .name = "SDIO_SMEM",
162 .owner = THIS_MODULE,
163 },
164};
165
166static int __init sdio_smem_init(void)
167{
168 return platform_driver_register(&sdio_smem_drv);
169};
170
171module_init(sdio_smem_init);
172
173MODULE_DESCRIPTION("SDIO SMEM");
174MODULE_LICENSE("GPL v2");