Path: blob/master/drivers/media/dvb/siano/smssdio.c
15112 views
/*1* smssdio.c - Siano 1xxx SDIO interface driver2*3* Copyright 2008 Pierre Ossman4*5* Based on code by Siano Mobile Silicon, Inc.,6* Copyright (C) 2006-2008, Uri Shkolnik7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or (at11* your option) any later version.12*13*14* This hardware is a bit odd in that all transfers should be done15* to/from the SMSSDIO_DATA register, yet the "increase address" bit16* always needs to be set.17*18* Also, buffers from the card are always aligned to 128 byte19* boundaries.20*/2122/*23* General cleanup notes:24*25* - only typedefs should be name *_t26*27* - use ERR_PTR and friends for smscore_register_device()28*29* - smscore_getbuffer should zero fields30*31* Fix stop command32*/3334#include <linux/moduleparam.h>35#include <linux/slab.h>36#include <linux/firmware.h>37#include <linux/delay.h>38#include <linux/mmc/card.h>39#include <linux/mmc/sdio_func.h>40#include <linux/mmc/sdio_ids.h>4142#include "smscoreapi.h"43#include "sms-cards.h"4445/* Registers */4647#define SMSSDIO_DATA 0x0048#define SMSSDIO_INT 0x0449#define SMSSDIO_BLOCK_SIZE 1285051static const struct sdio_device_id smssdio_ids[] __devinitconst = {52{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),53.driver_data = SMS1XXX_BOARD_SIANO_STELLAR},54{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),55.driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},56{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),57.driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},58{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),59.driver_data = SMS1XXX_BOARD_SIANO_VEGA},60{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),61.driver_data = SMS1XXX_BOARD_SIANO_VEGA},62{ /* end: all zeroes */ },63};6465MODULE_DEVICE_TABLE(sdio, smssdio_ids);6667struct smssdio_device {68struct sdio_func *func;6970struct smscore_device_t *coredev;7172struct smscore_buffer_t *split_cb;73};7475/*******************************************************************/76/* Siano core callbacks */77/*******************************************************************/7879static int smssdio_sendrequest(void *context, void *buffer, size_t size)80{81int ret = 0;82struct smssdio_device *smsdev;8384smsdev = context;8586sdio_claim_host(smsdev->func);8788while (size >= smsdev->func->cur_blksize) {89ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,90buffer, smsdev->func->cur_blksize);91if (ret)92goto out;9394buffer += smsdev->func->cur_blksize;95size -= smsdev->func->cur_blksize;96}9798if (size) {99ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,100buffer, size);101}102103out:104sdio_release_host(smsdev->func);105106return ret;107}108109/*******************************************************************/110/* SDIO callbacks */111/*******************************************************************/112113static void smssdio_interrupt(struct sdio_func *func)114{115int ret, isr;116117struct smssdio_device *smsdev;118struct smscore_buffer_t *cb;119struct SmsMsgHdr_ST *hdr;120size_t size;121122smsdev = sdio_get_drvdata(func);123124/*125* The interrupt register has no defined meaning. It is just126* a way of turning of the level triggered interrupt.127*/128isr = sdio_readb(func, SMSSDIO_INT, &ret);129if (ret) {130sms_err("Unable to read interrupt register!\n");131return;132}133134if (smsdev->split_cb == NULL) {135cb = smscore_getbuffer(smsdev->coredev);136if (!cb) {137sms_err("Unable to allocate data buffer!\n");138return;139}140141ret = sdio_memcpy_fromio(smsdev->func,142cb->p,143SMSSDIO_DATA,144SMSSDIO_BLOCK_SIZE);145if (ret) {146sms_err("Error %d reading initial block!\n", ret);147return;148}149150hdr = cb->p;151152if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {153smsdev->split_cb = cb;154return;155}156157if (hdr->msgLength > smsdev->func->cur_blksize)158size = hdr->msgLength - smsdev->func->cur_blksize;159else160size = 0;161} else {162cb = smsdev->split_cb;163hdr = cb->p;164165size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);166167smsdev->split_cb = NULL;168}169170if (size) {171void *buffer;172173buffer = cb->p + (hdr->msgLength - size);174size = ALIGN(size, SMSSDIO_BLOCK_SIZE);175176BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE);177178/*179* First attempt to transfer all of it in one go...180*/181ret = sdio_memcpy_fromio(smsdev->func,182buffer,183SMSSDIO_DATA,184size);185if (ret && ret != -EINVAL) {186smscore_putbuffer(smsdev->coredev, cb);187sms_err("Error %d reading data from card!\n", ret);188return;189}190191/*192* ..then fall back to one block at a time if that is193* not possible...194*195* (we have to do this manually because of the196* problem with the "increase address" bit)197*/198if (ret == -EINVAL) {199while (size) {200ret = sdio_memcpy_fromio(smsdev->func,201buffer, SMSSDIO_DATA,202smsdev->func->cur_blksize);203if (ret) {204smscore_putbuffer(smsdev->coredev, cb);205sms_err("Error %d reading "206"data from card!\n", ret);207return;208}209210buffer += smsdev->func->cur_blksize;211if (size > smsdev->func->cur_blksize)212size -= smsdev->func->cur_blksize;213else214size = 0;215}216}217}218219cb->size = hdr->msgLength;220cb->offset = 0;221222smscore_onresponse(smsdev->coredev, cb);223}224225static int __devinit smssdio_probe(struct sdio_func *func,226const struct sdio_device_id *id)227{228int ret;229230int board_id;231struct smssdio_device *smsdev;232struct smsdevice_params_t params;233234board_id = id->driver_data;235236smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);237if (!smsdev)238return -ENOMEM;239240smsdev->func = func;241242memset(¶ms, 0, sizeof(struct smsdevice_params_t));243244params.device = &func->dev;245params.buffer_size = 0x5000; /* ?? */246params.num_buffers = 22; /* ?? */247params.context = smsdev;248249snprintf(params.devpath, sizeof(params.devpath),250"sdio\\%s", sdio_func_id(func));251252params.sendrequest_handler = smssdio_sendrequest;253254params.device_type = sms_get_board(board_id)->type;255256if (params.device_type != SMS_STELLAR)257params.flags |= SMS_DEVICE_FAMILY2;258else {259/*260* FIXME: Stellar needs special handling...261*/262ret = -ENODEV;263goto free;264}265266ret = smscore_register_device(¶ms, &smsdev->coredev);267if (ret < 0)268goto free;269270smscore_set_board_id(smsdev->coredev, board_id);271272sdio_claim_host(func);273274ret = sdio_enable_func(func);275if (ret)276goto release;277278ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE);279if (ret)280goto disable;281282ret = sdio_claim_irq(func, smssdio_interrupt);283if (ret)284goto disable;285286sdio_set_drvdata(func, smsdev);287288sdio_release_host(func);289290ret = smscore_start_device(smsdev->coredev);291if (ret < 0)292goto reclaim;293294return 0;295296reclaim:297sdio_claim_host(func);298sdio_release_irq(func);299disable:300sdio_disable_func(func);301release:302sdio_release_host(func);303smscore_unregister_device(smsdev->coredev);304free:305kfree(smsdev);306307return ret;308}309310static void smssdio_remove(struct sdio_func *func)311{312struct smssdio_device *smsdev;313314smsdev = sdio_get_drvdata(func);315316/* FIXME: racy! */317if (smsdev->split_cb)318smscore_putbuffer(smsdev->coredev, smsdev->split_cb);319320smscore_unregister_device(smsdev->coredev);321322sdio_claim_host(func);323sdio_release_irq(func);324sdio_disable_func(func);325sdio_release_host(func);326327kfree(smsdev);328}329330static struct sdio_driver smssdio_driver = {331.name = "smssdio",332.id_table = smssdio_ids,333.probe = smssdio_probe,334.remove = smssdio_remove,335};336337/*******************************************************************/338/* Module functions */339/*******************************************************************/340341static int __init smssdio_module_init(void)342{343int ret = 0;344345printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");346printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");347348ret = sdio_register_driver(&smssdio_driver);349350return ret;351}352353static void __exit smssdio_module_exit(void)354{355sdio_unregister_driver(&smssdio_driver);356}357358module_init(smssdio_module_init);359module_exit(smssdio_module_exit);360361MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");362MODULE_AUTHOR("Pierre Ossman");363MODULE_LICENSE("GPL");364365366