1 /* 2 * smssdio.c - Siano 1xxx SDIO interface driver 3 * 4 * Copyright 2008 Pierre Ossman 5 * 6 * Based on code by Siano Mobile Silicon, Inc., 7 * Copyright (C) 2006-2008, Uri Shkolnik 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * 15 * This hardware is a bit odd in that all transfers should be done 16 * to/from the SMSSDIO_DATA register, yet the "increase address" bit 17 * always needs to be set. 18 * 19 * Also, buffers from the card are always aligned to 128 byte 20 * boundaries. 21 */ 22 23 /* 24 * General cleanup notes: 25 * 26 * - only typedefs should be name *_t 27 * 28 * - use ERR_PTR and friends for smscore_register_device() 29 * 30 * - smscore_getbuffer should zero fields 31 * 32 * Fix stop command 33 */ 34 35 #include <linux/moduleparam.h> 36 #include <linux/slab.h> 37 #include <linux/firmware.h> 38 #include <linux/delay.h> 39 #include <linux/mmc/card.h> 40 #include <linux/mmc/sdio_func.h> 41 #include <linux/mmc/sdio_ids.h> 42 #include <linux/module.h> 43 44 #include "smscoreapi.h" 45 #include "sms-cards.h" 46 47 /* Registers */ 48 49 #define SMSSDIO_DATA 0x00 50 #define SMSSDIO_INT 0x04 51 #define SMSSDIO_BLOCK_SIZE 128 52 53 static const struct sdio_device_id smssdio_ids[] = { 54 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), 55 .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, 56 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), 57 .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, 58 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), 59 .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, 60 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), 61 .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, 62 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), 63 .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, 64 { /* end: all zeroes */ }, 65 }; 66 67 MODULE_DEVICE_TABLE(sdio, smssdio_ids); 68 69 struct smssdio_device { 70 struct sdio_func *func; 71 72 struct smscore_device_t *coredev; 73 74 struct smscore_buffer_t *split_cb; 75 }; 76 77 /*******************************************************************/ 78 /* Siano core callbacks */ 79 /*******************************************************************/ 80 81 static int smssdio_sendrequest(void *context, void *buffer, size_t size) 82 { 83 int ret = 0; 84 struct smssdio_device *smsdev; 85 86 smsdev = context; 87 88 sdio_claim_host(smsdev->func); 89 90 while (size >= smsdev->func->cur_blksize) { 91 ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, 92 buffer, smsdev->func->cur_blksize); 93 if (ret) 94 goto out; 95 96 buffer += smsdev->func->cur_blksize; 97 size -= smsdev->func->cur_blksize; 98 } 99 100 if (size) { 101 ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, 102 buffer, size); 103 } 104 105 out: 106 sdio_release_host(smsdev->func); 107 108 return ret; 109 } 110 111 /*******************************************************************/ 112 /* SDIO callbacks */ 113 /*******************************************************************/ 114 115 static void smssdio_interrupt(struct sdio_func *func) 116 { 117 int ret; 118 119 struct smssdio_device *smsdev; 120 struct smscore_buffer_t *cb; 121 struct SmsMsgHdr_ST *hdr; 122 size_t size; 123 124 smsdev = sdio_get_drvdata(func); 125 126 /* 127 * The interrupt register has no defined meaning. It is just 128 * a way of turning of the level triggered interrupt. 129 */ 130 (void)sdio_readb(func, SMSSDIO_INT, &ret); 131 if (ret) { 132 sms_err("Unable to read interrupt register!\n"); 133 return; 134 } 135 136 if (smsdev->split_cb == NULL) { 137 cb = smscore_getbuffer(smsdev->coredev); 138 if (!cb) { 139 sms_err("Unable to allocate data buffer!\n"); 140 return; 141 } 142 143 ret = sdio_memcpy_fromio(smsdev->func, 144 cb->p, 145 SMSSDIO_DATA, 146 SMSSDIO_BLOCK_SIZE); 147 if (ret) { 148 sms_err("Error %d reading initial block!\n", ret); 149 return; 150 } 151 152 hdr = cb->p; 153 154 if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { 155 smsdev->split_cb = cb; 156 return; 157 } 158 159 if (hdr->msgLength > smsdev->func->cur_blksize) 160 size = hdr->msgLength - smsdev->func->cur_blksize; 161 else 162 size = 0; 163 } else { 164 cb = smsdev->split_cb; 165 hdr = cb->p; 166 167 size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST); 168 169 smsdev->split_cb = NULL; 170 } 171 172 if (size) { 173 void *buffer; 174 175 buffer = cb->p + (hdr->msgLength - size); 176 size = ALIGN(size, SMSSDIO_BLOCK_SIZE); 177 178 BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); 179 180 /* 181 * First attempt to transfer all of it in one go... 182 */ 183 ret = sdio_memcpy_fromio(smsdev->func, 184 buffer, 185 SMSSDIO_DATA, 186 size); 187 if (ret && ret != -EINVAL) { 188 smscore_putbuffer(smsdev->coredev, cb); 189 sms_err("Error %d reading data from card!\n", ret); 190 return; 191 } 192 193 /* 194 * ..then fall back to one block at a time if that is 195 * not possible... 196 * 197 * (we have to do this manually because of the 198 * problem with the "increase address" bit) 199 */ 200 if (ret == -EINVAL) { 201 while (size) { 202 ret = sdio_memcpy_fromio(smsdev->func, 203 buffer, SMSSDIO_DATA, 204 smsdev->func->cur_blksize); 205 if (ret) { 206 smscore_putbuffer(smsdev->coredev, cb); 207 sms_err("Error %d reading " 208 "data from card!\n", ret); 209 return; 210 } 211 212 buffer += smsdev->func->cur_blksize; 213 if (size > smsdev->func->cur_blksize) 214 size -= smsdev->func->cur_blksize; 215 else 216 size = 0; 217 } 218 } 219 } 220 221 cb->size = hdr->msgLength; 222 cb->offset = 0; 223 224 smscore_onresponse(smsdev->coredev, cb); 225 } 226 227 static int smssdio_probe(struct sdio_func *func, 228 const struct sdio_device_id *id) 229 { 230 int ret; 231 232 int board_id; 233 struct smssdio_device *smsdev; 234 struct smsdevice_params_t params; 235 236 board_id = id->driver_data; 237 238 smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); 239 if (!smsdev) 240 return -ENOMEM; 241 242 smsdev->func = func; 243 244 memset(¶ms, 0, sizeof(struct smsdevice_params_t)); 245 246 params.device = &func->dev; 247 params.buffer_size = 0x5000; /* ?? */ 248 params.num_buffers = 22; /* ?? */ 249 params.context = smsdev; 250 251 snprintf(params.devpath, sizeof(params.devpath), 252 "sdio\\%s", sdio_func_id(func)); 253 254 params.sendrequest_handler = smssdio_sendrequest; 255 256 params.device_type = sms_get_board(board_id)->type; 257 258 if (params.device_type != SMS_STELLAR) 259 params.flags |= SMS_DEVICE_FAMILY2; 260 else { 261 /* 262 * FIXME: Stellar needs special handling... 263 */ 264 ret = -ENODEV; 265 goto free; 266 } 267 268 ret = smscore_register_device(¶ms, &smsdev->coredev); 269 if (ret < 0) 270 goto free; 271 272 smscore_set_board_id(smsdev->coredev, board_id); 273 274 sdio_claim_host(func); 275 276 ret = sdio_enable_func(func); 277 if (ret) 278 goto release; 279 280 ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); 281 if (ret) 282 goto disable; 283 284 ret = sdio_claim_irq(func, smssdio_interrupt); 285 if (ret) 286 goto disable; 287 288 sdio_set_drvdata(func, smsdev); 289 290 sdio_release_host(func); 291 292 ret = smscore_start_device(smsdev->coredev); 293 if (ret < 0) 294 goto reclaim; 295 296 return 0; 297 298 reclaim: 299 sdio_claim_host(func); 300 sdio_release_irq(func); 301 disable: 302 sdio_disable_func(func); 303 release: 304 sdio_release_host(func); 305 smscore_unregister_device(smsdev->coredev); 306 free: 307 kfree(smsdev); 308 309 return ret; 310 } 311 312 static void smssdio_remove(struct sdio_func *func) 313 { 314 struct smssdio_device *smsdev; 315 316 smsdev = sdio_get_drvdata(func); 317 318 /* FIXME: racy! */ 319 if (smsdev->split_cb) 320 smscore_putbuffer(smsdev->coredev, smsdev->split_cb); 321 322 smscore_unregister_device(smsdev->coredev); 323 324 sdio_claim_host(func); 325 sdio_release_irq(func); 326 sdio_disable_func(func); 327 sdio_release_host(func); 328 329 kfree(smsdev); 330 } 331 332 static struct sdio_driver smssdio_driver = { 333 .name = "smssdio", 334 .id_table = smssdio_ids, 335 .probe = smssdio_probe, 336 .remove = smssdio_remove, 337 }; 338 339 /*******************************************************************/ 340 /* Module functions */ 341 /*******************************************************************/ 342 343 static int __init smssdio_module_init(void) 344 { 345 int ret = 0; 346 347 printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); 348 printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); 349 350 ret = sdio_register_driver(&smssdio_driver); 351 352 return ret; 353 } 354 355 static void __exit smssdio_module_exit(void) 356 { 357 sdio_unregister_driver(&smssdio_driver); 358 } 359 360 module_init(smssdio_module_init); 361 module_exit(smssdio_module_exit); 362 363 MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); 364 MODULE_AUTHOR("Pierre Ossman"); 365 MODULE_LICENSE("GPL"); 366