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 #include "smsendian.h" 47 48 /* Registers */ 49 50 #define SMSSDIO_DATA 0x00 51 #define SMSSDIO_INT 0x04 52 #define SMSSDIO_BLOCK_SIZE 128 53 54 static const struct sdio_device_id smssdio_ids[] = { 55 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), 56 .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, 57 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), 58 .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, 59 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), 60 .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, 61 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), 62 .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, 63 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), 64 .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, 65 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302), 66 .driver_data = SMS1XXX_BOARD_SIANO_MING}, 67 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500), 68 .driver_data = SMS1XXX_BOARD_SIANO_PELE}, 69 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600), 70 .driver_data = SMS1XXX_BOARD_SIANO_RIO}, 71 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700), 72 .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160}, 73 {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800), 74 .driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530}, 75 { /* end: all zeroes */ }, 76 }; 77 78 MODULE_DEVICE_TABLE(sdio, smssdio_ids); 79 80 struct smssdio_device { 81 struct sdio_func *func; 82 83 struct smscore_device_t *coredev; 84 85 struct smscore_buffer_t *split_cb; 86 }; 87 88 /*******************************************************************/ 89 /* Siano core callbacks */ 90 /*******************************************************************/ 91 92 static int smssdio_sendrequest(void *context, void *buffer, size_t size) 93 { 94 int ret = 0; 95 struct smssdio_device *smsdev; 96 97 smsdev = context; 98 99 sdio_claim_host(smsdev->func); 100 101 smsendian_handle_tx_message((struct sms_msg_data *) buffer); 102 while (size >= smsdev->func->cur_blksize) { 103 ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, 104 buffer, smsdev->func->cur_blksize); 105 if (ret) 106 goto out; 107 108 buffer += smsdev->func->cur_blksize; 109 size -= smsdev->func->cur_blksize; 110 } 111 112 if (size) { 113 ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, 114 buffer, size); 115 } 116 117 out: 118 sdio_release_host(smsdev->func); 119 120 return ret; 121 } 122 123 /*******************************************************************/ 124 /* SDIO callbacks */ 125 /*******************************************************************/ 126 127 static void smssdio_interrupt(struct sdio_func *func) 128 { 129 int ret; 130 131 struct smssdio_device *smsdev; 132 struct smscore_buffer_t *cb; 133 struct sms_msg_hdr *hdr; 134 size_t size; 135 136 smsdev = sdio_get_drvdata(func); 137 138 /* 139 * The interrupt register has no defined meaning. It is just 140 * a way of turning of the level triggered interrupt. 141 */ 142 (void)sdio_readb(func, SMSSDIO_INT, &ret); 143 if (ret) { 144 sms_err("Unable to read interrupt register!\n"); 145 return; 146 } 147 148 if (smsdev->split_cb == NULL) { 149 cb = smscore_getbuffer(smsdev->coredev); 150 if (!cb) { 151 sms_err("Unable to allocate data buffer!\n"); 152 return; 153 } 154 155 ret = sdio_memcpy_fromio(smsdev->func, 156 cb->p, 157 SMSSDIO_DATA, 158 SMSSDIO_BLOCK_SIZE); 159 if (ret) { 160 sms_err("Error %d reading initial block!\n", ret); 161 return; 162 } 163 164 hdr = cb->p; 165 166 if (hdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG) { 167 smsdev->split_cb = cb; 168 return; 169 } 170 171 if (hdr->msg_length > smsdev->func->cur_blksize) 172 size = hdr->msg_length - smsdev->func->cur_blksize; 173 else 174 size = 0; 175 } else { 176 cb = smsdev->split_cb; 177 hdr = cb->p; 178 179 size = hdr->msg_length - sizeof(struct sms_msg_hdr); 180 181 smsdev->split_cb = NULL; 182 } 183 184 if (size) { 185 void *buffer; 186 187 buffer = cb->p + (hdr->msg_length - size); 188 size = ALIGN(size, SMSSDIO_BLOCK_SIZE); 189 190 BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); 191 192 /* 193 * First attempt to transfer all of it in one go... 194 */ 195 ret = sdio_memcpy_fromio(smsdev->func, 196 buffer, 197 SMSSDIO_DATA, 198 size); 199 if (ret && ret != -EINVAL) { 200 smscore_putbuffer(smsdev->coredev, cb); 201 sms_err("Error %d reading data from card!\n", ret); 202 return; 203 } 204 205 /* 206 * ..then fall back to one block at a time if that is 207 * not possible... 208 * 209 * (we have to do this manually because of the 210 * problem with the "increase address" bit) 211 */ 212 if (ret == -EINVAL) { 213 while (size) { 214 ret = sdio_memcpy_fromio(smsdev->func, 215 buffer, SMSSDIO_DATA, 216 smsdev->func->cur_blksize); 217 if (ret) { 218 smscore_putbuffer(smsdev->coredev, cb); 219 sms_err("Error %d reading " 220 "data from card!\n", ret); 221 return; 222 } 223 224 buffer += smsdev->func->cur_blksize; 225 if (size > smsdev->func->cur_blksize) 226 size -= smsdev->func->cur_blksize; 227 else 228 size = 0; 229 } 230 } 231 } 232 233 cb->size = hdr->msg_length; 234 cb->offset = 0; 235 236 smsendian_handle_rx_message((struct sms_msg_data *) cb->p); 237 smscore_onresponse(smsdev->coredev, cb); 238 } 239 240 static int smssdio_probe(struct sdio_func *func, 241 const struct sdio_device_id *id) 242 { 243 int ret; 244 245 int board_id; 246 struct smssdio_device *smsdev; 247 struct smsdevice_params_t params; 248 249 board_id = id->driver_data; 250 251 smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); 252 if (!smsdev) 253 return -ENOMEM; 254 255 smsdev->func = func; 256 257 memset(¶ms, 0, sizeof(struct smsdevice_params_t)); 258 259 params.device = &func->dev; 260 params.buffer_size = 0x5000; /* ?? */ 261 params.num_buffers = 22; /* ?? */ 262 params.context = smsdev; 263 264 snprintf(params.devpath, sizeof(params.devpath), 265 "sdio\\%s", sdio_func_id(func)); 266 267 params.sendrequest_handler = smssdio_sendrequest; 268 269 params.device_type = sms_get_board(board_id)->type; 270 271 if (params.device_type != SMS_STELLAR) 272 params.flags |= SMS_DEVICE_FAMILY2; 273 else { 274 /* 275 * FIXME: Stellar needs special handling... 276 */ 277 ret = -ENODEV; 278 goto free; 279 } 280 281 ret = smscore_register_device(¶ms, &smsdev->coredev); 282 if (ret < 0) 283 goto free; 284 285 smscore_set_board_id(smsdev->coredev, board_id); 286 287 sdio_claim_host(func); 288 289 ret = sdio_enable_func(func); 290 if (ret) 291 goto release; 292 293 ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); 294 if (ret) 295 goto disable; 296 297 ret = sdio_claim_irq(func, smssdio_interrupt); 298 if (ret) 299 goto disable; 300 301 sdio_set_drvdata(func, smsdev); 302 303 sdio_release_host(func); 304 305 ret = smscore_start_device(smsdev->coredev); 306 if (ret < 0) 307 goto reclaim; 308 309 return 0; 310 311 reclaim: 312 sdio_claim_host(func); 313 sdio_release_irq(func); 314 disable: 315 sdio_disable_func(func); 316 release: 317 sdio_release_host(func); 318 smscore_unregister_device(smsdev->coredev); 319 free: 320 kfree(smsdev); 321 322 return ret; 323 } 324 325 static void smssdio_remove(struct sdio_func *func) 326 { 327 struct smssdio_device *smsdev; 328 329 smsdev = sdio_get_drvdata(func); 330 331 /* FIXME: racy! */ 332 if (smsdev->split_cb) 333 smscore_putbuffer(smsdev->coredev, smsdev->split_cb); 334 335 smscore_unregister_device(smsdev->coredev); 336 337 sdio_claim_host(func); 338 sdio_release_irq(func); 339 sdio_disable_func(func); 340 sdio_release_host(func); 341 342 kfree(smsdev); 343 } 344 345 static struct sdio_driver smssdio_driver = { 346 .name = "smssdio", 347 .id_table = smssdio_ids, 348 .probe = smssdio_probe, 349 .remove = smssdio_remove, 350 }; 351 352 /*******************************************************************/ 353 /* Module functions */ 354 /*******************************************************************/ 355 356 static int __init smssdio_module_init(void) 357 { 358 int ret = 0; 359 360 printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); 361 printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); 362 363 ret = sdio_register_driver(&smssdio_driver); 364 365 return ret; 366 } 367 368 static void __exit smssdio_module_exit(void) 369 { 370 sdio_unregister_driver(&smssdio_driver); 371 } 372 373 module_init(smssdio_module_init); 374 module_exit(smssdio_module_exit); 375 376 MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); 377 MODULE_AUTHOR("Pierre Ossman"); 378 MODULE_LICENSE("GPL"); 379