# HG changeset patch
# User Uri Shkolnik <[email protected]>
# Date 1231770205 -7200
# Node ID 2a546240ebe57391622c731646cf7523d20830dd
# Parent  036cad8c8b513658cdfb714d0ed3274447d4f748
Add SDIO driver interface for SMS based devices

From: Uri Shkolnik <[email protected]>

This patch provides SDIO interface driver for SMS chip-set based devices.
The patch includes SMS high level SDIO driver and requires patching the kernel 
SDIO stack.

I would like to thank Pierre Ossman, MMC maintainer, who wrote this driver.

Priority: normal

Signed-off-by: Pierre Ossman <[email protected]>
Signed-off-by: Uri Shkolnik <[email protected]>

diff -r 036cad8c8b51 -r 2a546240ebe5 linux/drivers/media/dvb/siano/smssdio.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/siano/smssdio.c   Mon Jan 12 16:23:25 2009 +0200
@@ -0,0 +1,356 @@
+/*
+ *  smssdio.c - Siano 1xxx SDIO interface driver
+ *
+ *  Copyright 2008 Pierre Ossman
+ *
+ * Based on code by Siano Mobile Silicon, Inc.,
+ * Copyright (C) 2006-2008, Uri Shkolnik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ *
+ * This hardware is a bit odd in that all transfers should be done
+ * to/from the SMSSDIO_DATA register, yet the "increase address" bit
+ * always needs to be set.
+ *
+ * Also, buffers from the card are always aligned to 128 byte
+ * boundaries.
+ */
+
+/*
+ * General cleanup notes:
+ *
+ * - only typedefs should be name *_t
+ *
+ * - use ERR_PTR and friends for smscore_register_device()
+ *
+ * - smscore_getbuffer should zero fields
+ *
+ * Fix stop command
+ */
+
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include "smscoreapi.h"
+#include "sms-cards.h"
+
+/* Registers */
+
+#define SMSSDIO_DATA           0x00
+#define SMSSDIO_INT            0x04
+
+static const struct sdio_device_id smssdio_ids[] = {
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
+        .driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
+        .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
+        .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
+        .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
+        .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
+       { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, smssdio_ids);
+
+struct smssdio_device {
+       struct sdio_func *func;
+
+       struct smscore_device_t *coredev;
+
+       struct smscore_buffer_t *split_cb;
+};
+
+/*******************************************************************/
+/* Siano core callbacks                                            */
+/*******************************************************************/
+
+static int smssdio_sendrequest(void *context, void *buffer, size_t size)
+{
+       int ret;
+       struct smssdio_device *smsdev;
+
+       smsdev = context;
+
+       sdio_claim_host(smsdev->func);
+
+       while (size >= smsdev->func->cur_blksize) {
+               ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
+               if (ret)
+                       goto out;
+
+               buffer += smsdev->func->cur_blksize;
+               size -= smsdev->func->cur_blksize;
+       }
+
+       if (size) {
+               ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
+                                      buffer, size);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       sdio_release_host(smsdev->func);
+
+       return ret;
+}
+
+/*******************************************************************/
+/* SDIO callbacks                                                  */
+/*******************************************************************/
+
+static void smssdio_interrupt(struct sdio_func *func)
+{
+       int ret, isr;
+
+       struct smssdio_device *smsdev;
+       struct smscore_buffer_t *cb;
+       struct SmsMsgHdr_ST *hdr;
+       size_t size;
+
+       smsdev = sdio_get_drvdata(func);
+
+       /*
+        * The interrupt register has no defined meaning. It is just
+        * a way of turning of the level triggered interrupt.
+        */
+       isr = sdio_readb(func, SMSSDIO_INT, &ret);
+       if (ret) {
+               dev_err(&smsdev->func->dev,
+                       "Unable to read interrupt register!\n");
+               return;
+       }
+
+       if (smsdev->split_cb == NULL) {
+               cb = smscore_getbuffer(smsdev->coredev);
+               if (!cb) {
+                       dev_err(&smsdev->func->dev,
+                               "Unable to allocate data buffer!\n");
+                       return;
+               }
+
+               ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
+               if (ret) {
+                       dev_err(&smsdev->func->dev,
+                               "Error %d reading initial block!\n", ret);
+                       return;
+               }
+
+               hdr = cb->p;
+
+               if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {
+                       smsdev->split_cb = cb;
+                       return;
+               }
+
+               size = hdr->msgLength - smsdev->func->cur_blksize;
+       } else {
+               cb = smsdev->split_cb;
+               hdr = cb->p;
+
+               size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);
+
+               smsdev->split_cb = NULL;
+       }
+
+       if (hdr->msgLength > smsdev->func->cur_blksize) {
+               void *buffer;
+
+               size = ALIGN(size, 128);
+               buffer = cb->p + hdr->msgLength;
+
+               BUG_ON(smsdev->func->cur_blksize != 128);
+
+               /*
+                * First attempt to transfer all of it in one go...
+                */
+               ret = sdio_read_blocks(smsdev->func, buffer,
+                                      SMSSDIO_DATA, size / 128);
+               if (ret && ret != -EINVAL) {
+                       smscore_putbuffer(smsdev->coredev, cb);
+                       dev_err(&smsdev->func->dev,
+                               "Error %d reading data from card!\n", ret);
+                       return;
+               }
+
+               /*
+                * ..then fall back to one block at a time if that is
+                * not possible...
+                *
+                * (we have to do this manually because of the
+                * problem with the "increase address" bit)
+                */
+               if (ret == -EINVAL) {
+                       while (size) {
+                               ret = sdio_read_blocks(smsdev->func,
+                                                      buffer, SMSSDIO_DATA, 1);
+                               if (ret) {
+                                       smscore_putbuffer(smsdev->coredev, cb);
+                                       dev_err(&smsdev->func->dev,
+                                               "Error %d reading "
+                                               "data from card!\n", ret);
+                                       return;
+                               }
+
+                               buffer += smsdev->func->cur_blksize;
+                               if (size > smsdev->func->cur_blksize)
+                                       size -= smsdev->func->cur_blksize;
+                               else
+                                       size = 0;
+                       }
+               }
+       }
+
+       cb->size = hdr->msgLength;
+       cb->offset = 0;
+
+       smscore_onresponse(smsdev->coredev, cb);
+}
+
+static int smssdio_probe(struct sdio_func *func,
+                        const struct sdio_device_id *id)
+{
+       int ret;
+
+       int board_id;
+       struct smssdio_device *smsdev;
+       struct smsdevice_params_t params;
+
+       board_id = id->driver_data;
+
+       smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
+       if (!smsdev)
+               return -ENOMEM;
+
+       smsdev->func = func;
+
+       memset(&params, 0, sizeof(struct smsdevice_params_t));
+
+       params.device = &func->dev;
+       params.buffer_size = 0x5000;    /* ?? */
+       params.num_buffers = 22;        /* ?? */
+       params.context = smsdev;
+
+       snprintf(params.devpath, sizeof(params.devpath),
+                "sdio\\%s", sdio_func_id(func));
+
+       params.sendrequest_handler = smssdio_sendrequest;
+
+       params.device_type = sms_get_board(board_id)->type;
+
+       if (params.device_type != SMS_STELLAR)
+               params.flags |= SMS_DEVICE_FAMILY2;
+       else {
+               /*
+                * FIXME: Stellar needs special handling...
+                */
+               ret = -ENODEV;
+               goto free;
+       }
+
+       ret = smscore_register_device(&params, &smsdev->coredev);
+       if (ret < 0)
+               goto free;
+
+       smscore_set_board_id(smsdev->coredev, board_id);
+
+       sdio_claim_host(func);
+
+       ret = sdio_enable_func(func);
+       if (ret)
+               goto release;
+
+       ret = sdio_set_block_size(func, 128);
+       if (ret)
+               goto disable;
+
+       ret = sdio_claim_irq(func, smssdio_interrupt);
+       if (ret)
+               goto disable;
+
+       sdio_set_drvdata(func, smsdev);
+
+       sdio_release_host(func);
+
+       ret = smscore_start_device(smsdev->coredev);
+       if (ret < 0)
+               goto reclaim;
+
+       return 0;
+
+reclaim:
+       sdio_claim_host(func);
+       sdio_release_irq(func);
+disable:
+       sdio_disable_func(func);
+release:
+       sdio_release_host(func);
+       smscore_unregister_device(smsdev->coredev);
+free:
+       kfree(smsdev);
+
+       return ret;
+}
+
+static void smssdio_remove(struct sdio_func *func)
+{
+       struct smssdio_device *smsdev;
+
+       smsdev = sdio_get_drvdata(func);
+
+       /* FIXME: racy! */
+       if (smsdev->split_cb)
+               smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
+
+       smscore_unregister_device(smsdev->coredev);
+
+       sdio_claim_host(func);
+       sdio_release_irq(func);
+       sdio_disable_func(func);
+       sdio_release_host(func);
+
+       kfree(smsdev);
+}
+
+static struct sdio_driver smssdio_driver = {
+       .name = "smssdio",
+       .id_table = smssdio_ids,
+       .probe = smssdio_probe,
+       .remove = smssdio_remove,
+};
+
+/*******************************************************************/
+/* Module functions                                                */
+/*******************************************************************/
+
+int smssdio_register(void)
+{
+       int ret = 0;
+
+       printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
+       printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
+
+       ret = sdio_register_driver(&smssdio_driver);
+
+       return ret;
+}
+
+void smssdio_unregister(void)
+{
+       sdio_unregister_driver(&smssdio_driver);
+}
+
+MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
+MODULE_AUTHOR("Pierre Ossman");
+MODULE_LICENSE("GPL");



      
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to