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