blob: 4da81996c2ff1e0038f702c401606baeea1997b4 [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;
28
29static void sdio_smem_release(struct device *dev)
30{
31 pr_debug("sdio smem released\n");
32}
33
34static struct sdio_smem_client client;
35
36static void sdio_smem_read(struct work_struct *work)
37{
38 int err;
39 int read_avail;
40 char *data = client.buf;
41
42 read_avail = sdio_read_avail(channel);
43 if (read_avail > bytes_avail ||
44 read_avail < 0) {
45 pr_err("Error: read_avail=%d bytes_avail=%d\n",
46 read_avail, bytes_avail);
47 client.cb_func(SDIO_SMEM_EVENT_READ_ERR);
48 return;
49 }
50
51 err = sdio_read(channel,
52 &data[client.size - bytes_avail],
53 read_avail);
54 if (err < 0) {
55 pr_err("sdio_read error (%d)", err);
56 client.cb_func(SDIO_SMEM_EVENT_READ_ERR);
57 return;
58 }
59
60 bytes_avail -= read_avail;
61 pr_debug("read %d bytes (bytes_avail = %d)\n",
62 read_avail, bytes_avail);
63
64 if (!bytes_avail) {
65 bytes_avail = client.size;
66 err = client.cb_func(SDIO_SMEM_EVENT_READ_DONE);
67 }
68 if (err)
69 pr_err("error (%d) on callback\n", err);
70}
71
72static void sdio_smem_notify(void *priv, unsigned event)
73{
74 pr_debug("%d event received\n", event);
75
76 if (event == SDIO_EVENT_DATA_READ_AVAIL ||
77 event == SDIO_EVENT_DATA_WRITE_AVAIL)
78 queue_work(workq, &work_read);
79}
80
81int sdio_smem_register_client(void)
82{
83 int err = 0;
84
85 if (!client.buf || !client.size || !client.cb_func)
86 return -EINVAL;
87
88 pr_debug("buf = %p\n", client.buf);
89 pr_debug("size = 0x%x\n", client.size);
90
91 bytes_avail = client.size;
92 workq = create_singlethread_workqueue("sdio_smem");
93 if (!workq)
94 return -ENOMEM;
95
96 err = sdio_open("SDIO_SMEM", &channel, NULL, sdio_smem_notify);
97 if (err < 0) {
98 pr_err("sdio_open error (%d)\n", err);
99 destroy_workqueue(workq);
100 return err;
101 }
102 pr_debug("SDIO SMEM channel opened\n");
103 return err;
104}
105
106int sdio_smem_unregister_client(void)
107{
108 destroy_workqueue(workq);
109 bytes_avail = 0;
110 client.buf = NULL;
111 client.cb_func = NULL;
112 client.size = 0;
113 pr_debug("SDIO SMEM channel closed\n");
114 return 0;
115}
116
117static int sdio_smem_probe(struct platform_device *pdev)
118{
119 client.plat_dev.name = "SDIO_SMEM_CLIENT";
120 client.plat_dev.id = -1;
121 client.plat_dev.dev.release = sdio_smem_release;
122
123 return platform_device_register(&client.plat_dev);
124}
125
126static int sdio_smem_remove(struct platform_device *pdev)
127{
128 platform_device_unregister(&client.plat_dev);
129 memset(&client, 0, sizeof(client));
130 return 0;
131}
132static struct platform_driver sdio_smem_drv = {
133 .probe = sdio_smem_probe,
134 .remove = sdio_smem_remove,
135 .driver = {
136 .name = "SDIO_SMEM",
137 .owner = THIS_MODULE,
138 },
139};
140
141static int __init sdio_smem_init(void)
142{
143 return platform_driver_register(&sdio_smem_drv);
144};
145
146module_init(sdio_smem_init);
147
148MODULE_DESCRIPTION("SDIO SMEM");
149MODULE_LICENSE("GPL v2");