linux/drivers/media/mmc/siano/smssdio.c
<<
>>
Prefs
   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, 0x302),
  62        .driver_data = SMS1XXX_BOARD_SIANO_MING},
  63        {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500),
  64        .driver_data = SMS1XXX_BOARD_SIANO_PELE},
  65        {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600),
  66        .driver_data = SMS1XXX_BOARD_SIANO_RIO},
  67        {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700),
  68        .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160},
  69        {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800),
  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(&params, 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(&params, &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");
 375