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