| Duy Truong | e833aca | 2013-02-12 13:35:08 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 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> | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 18 | #include <linux/module.h> | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 19 | #include <mach/sdio_al.h> | 
|  | 20 | #include <mach/sdio_smem.h> | 
|  | 21 |  | 
|  | 22 | static void sdio_smem_read(struct work_struct *work); | 
|  | 23 |  | 
|  | 24 | static struct sdio_channel *channel; | 
|  | 25 | static struct workqueue_struct *workq; | 
|  | 26 | static DECLARE_WORK(work_read, sdio_smem_read); | 
|  | 27 | static DECLARE_WAIT_QUEUE_HEAD(waitq); | 
|  | 28 | static int bytes_avail; | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 29 | static int sdio_ch_opened; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 30 |  | 
|  | 31 | static void sdio_smem_release(struct device *dev) | 
|  | 32 | { | 
|  | 33 | pr_debug("sdio smem released\n"); | 
|  | 34 | } | 
|  | 35 |  | 
|  | 36 | static struct sdio_smem_client client; | 
|  | 37 |  | 
|  | 38 | static void sdio_smem_read(struct work_struct *work) | 
|  | 39 | { | 
|  | 40 | int err; | 
|  | 41 | int read_avail; | 
|  | 42 | char *data = client.buf; | 
|  | 43 |  | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 44 | if (!sdio_ch_opened) | 
|  | 45 | return; | 
|  | 46 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 47 | read_avail = sdio_read_avail(channel); | 
|  | 48 | if (read_avail > bytes_avail || | 
|  | 49 | read_avail < 0) { | 
|  | 50 | pr_err("Error: read_avail=%d bytes_avail=%d\n", | 
|  | 51 | read_avail, bytes_avail); | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 52 | goto read_err; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 53 | } | 
|  | 54 |  | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 55 | if (read_avail == 0) | 
|  | 56 | return; | 
|  | 57 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 58 | err = sdio_read(channel, | 
|  | 59 | &data[client.size - bytes_avail], | 
|  | 60 | read_avail); | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 61 | if (err) { | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 62 | pr_err("sdio_read error (%d)", err); | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 63 | goto read_err; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 64 | } | 
|  | 65 |  | 
|  | 66 | bytes_avail -= read_avail; | 
|  | 67 | pr_debug("read %d bytes (bytes_avail = %d)\n", | 
|  | 68 | read_avail, bytes_avail); | 
|  | 69 |  | 
|  | 70 | if (!bytes_avail) { | 
|  | 71 | bytes_avail = client.size; | 
|  | 72 | err = client.cb_func(SDIO_SMEM_EVENT_READ_DONE); | 
|  | 73 | } | 
|  | 74 | if (err) | 
|  | 75 | pr_err("error (%d) on callback\n", err); | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 76 |  | 
|  | 77 | return; | 
|  | 78 |  | 
|  | 79 | read_err: | 
|  | 80 | if (sdio_ch_opened) | 
|  | 81 | client.cb_func(SDIO_SMEM_EVENT_READ_ERR); | 
|  | 82 | return; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 83 | } | 
|  | 84 |  | 
|  | 85 | static void sdio_smem_notify(void *priv, unsigned event) | 
|  | 86 | { | 
|  | 87 | pr_debug("%d event received\n", event); | 
|  | 88 |  | 
|  | 89 | if (event == SDIO_EVENT_DATA_READ_AVAIL || | 
|  | 90 | event == SDIO_EVENT_DATA_WRITE_AVAIL) | 
|  | 91 | queue_work(workq, &work_read); | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | int sdio_smem_register_client(void) | 
|  | 95 | { | 
|  | 96 | int err = 0; | 
|  | 97 |  | 
|  | 98 | if (!client.buf || !client.size || !client.cb_func) | 
|  | 99 | return -EINVAL; | 
|  | 100 |  | 
|  | 101 | pr_debug("buf = %p\n", client.buf); | 
|  | 102 | pr_debug("size = 0x%x\n", client.size); | 
|  | 103 |  | 
|  | 104 | bytes_avail = client.size; | 
|  | 105 | workq = create_singlethread_workqueue("sdio_smem"); | 
|  | 106 | if (!workq) | 
|  | 107 | return -ENOMEM; | 
|  | 108 |  | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 109 | sdio_ch_opened = 1; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 110 | err = sdio_open("SDIO_SMEM", &channel, NULL, sdio_smem_notify); | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 111 | if (err) { | 
|  | 112 | sdio_ch_opened = 0; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 113 | pr_err("sdio_open error (%d)\n", err); | 
|  | 114 | destroy_workqueue(workq); | 
|  | 115 | return err; | 
|  | 116 | } | 
|  | 117 | pr_debug("SDIO SMEM channel opened\n"); | 
|  | 118 | return err; | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | int sdio_smem_unregister_client(void) | 
|  | 122 | { | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 123 | int err = 0; | 
|  | 124 |  | 
|  | 125 | sdio_ch_opened = 0; | 
|  | 126 | err = sdio_close(channel); | 
|  | 127 | if (err) { | 
|  | 128 | pr_err("sdio_close error (%d)\n", err); | 
|  | 129 | return err; | 
|  | 130 | } | 
|  | 131 | pr_debug("SDIO SMEM channel closed\n"); | 
|  | 132 | flush_workqueue(workq); | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 133 | destroy_workqueue(workq); | 
|  | 134 | bytes_avail = 0; | 
|  | 135 | client.buf = NULL; | 
|  | 136 | client.cb_func = NULL; | 
|  | 137 | client.size = 0; | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 138 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 139 | return 0; | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | static int sdio_smem_probe(struct platform_device *pdev) | 
|  | 143 | { | 
|  | 144 | client.plat_dev.name = "SDIO_SMEM_CLIENT"; | 
|  | 145 | client.plat_dev.id = -1; | 
|  | 146 | client.plat_dev.dev.release = sdio_smem_release; | 
|  | 147 |  | 
|  | 148 | return platform_device_register(&client.plat_dev); | 
|  | 149 | } | 
|  | 150 |  | 
|  | 151 | static int sdio_smem_remove(struct platform_device *pdev) | 
|  | 152 | { | 
|  | 153 | platform_device_unregister(&client.plat_dev); | 
|  | 154 | memset(&client, 0, sizeof(client)); | 
| Maya Erez | 2bfba2c | 2011-09-19 11:41:55 +0300 | [diff] [blame] | 155 | sdio_ch_opened = 0; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 156 | return 0; | 
|  | 157 | } | 
|  | 158 | static struct platform_driver sdio_smem_drv = { | 
|  | 159 | .probe		= sdio_smem_probe, | 
|  | 160 | .remove		= sdio_smem_remove, | 
|  | 161 | .driver		= { | 
|  | 162 | .name	= "SDIO_SMEM", | 
|  | 163 | .owner	= THIS_MODULE, | 
|  | 164 | }, | 
|  | 165 | }; | 
|  | 166 |  | 
|  | 167 | static int __init sdio_smem_init(void) | 
|  | 168 | { | 
|  | 169 | return platform_driver_register(&sdio_smem_drv); | 
|  | 170 | }; | 
|  | 171 |  | 
|  | 172 | module_init(sdio_smem_init); | 
|  | 173 |  | 
|  | 174 | MODULE_DESCRIPTION("SDIO SMEM"); | 
|  | 175 | MODULE_LICENSE("GPL v2"); |