10013ca8cSMauro Carvalho Chehab /* 20013ca8cSMauro Carvalho Chehab * smssdio.c - Siano 1xxx SDIO interface driver 30013ca8cSMauro Carvalho Chehab * 40013ca8cSMauro Carvalho Chehab * Copyright 2008 Pierre Ossman 50013ca8cSMauro Carvalho Chehab * 60013ca8cSMauro Carvalho Chehab * Based on code by Siano Mobile Silicon, Inc., 70013ca8cSMauro Carvalho Chehab * Copyright (C) 2006-2008, Uri Shkolnik 80013ca8cSMauro Carvalho Chehab * 90013ca8cSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 100013ca8cSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 110013ca8cSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or (at 120013ca8cSMauro Carvalho Chehab * your option) any later version. 130013ca8cSMauro Carvalho Chehab * 140013ca8cSMauro Carvalho Chehab * 150013ca8cSMauro Carvalho Chehab * This hardware is a bit odd in that all transfers should be done 160013ca8cSMauro Carvalho Chehab * to/from the SMSSDIO_DATA register, yet the "increase address" bit 170013ca8cSMauro Carvalho Chehab * always needs to be set. 180013ca8cSMauro Carvalho Chehab * 190013ca8cSMauro Carvalho Chehab * Also, buffers from the card are always aligned to 128 byte 200013ca8cSMauro Carvalho Chehab * boundaries. 210013ca8cSMauro Carvalho Chehab */ 220013ca8cSMauro Carvalho Chehab 230013ca8cSMauro Carvalho Chehab /* 240013ca8cSMauro Carvalho Chehab * General cleanup notes: 250013ca8cSMauro Carvalho Chehab * 260013ca8cSMauro Carvalho Chehab * - only typedefs should be name *_t 270013ca8cSMauro Carvalho Chehab * 280013ca8cSMauro Carvalho Chehab * - use ERR_PTR and friends for smscore_register_device() 290013ca8cSMauro Carvalho Chehab * 300013ca8cSMauro Carvalho Chehab * - smscore_getbuffer should zero fields 310013ca8cSMauro Carvalho Chehab * 320013ca8cSMauro Carvalho Chehab * Fix stop command 330013ca8cSMauro Carvalho Chehab */ 340013ca8cSMauro Carvalho Chehab 350013ca8cSMauro Carvalho Chehab #include <linux/moduleparam.h> 360013ca8cSMauro Carvalho Chehab #include <linux/slab.h> 370013ca8cSMauro Carvalho Chehab #include <linux/firmware.h> 380013ca8cSMauro Carvalho Chehab #include <linux/delay.h> 390013ca8cSMauro Carvalho Chehab #include <linux/mmc/card.h> 400013ca8cSMauro Carvalho Chehab #include <linux/mmc/sdio_func.h> 410013ca8cSMauro Carvalho Chehab #include <linux/mmc/sdio_ids.h> 420013ca8cSMauro Carvalho Chehab #include <linux/module.h> 430013ca8cSMauro Carvalho Chehab 440013ca8cSMauro Carvalho Chehab #include "smscoreapi.h" 450013ca8cSMauro Carvalho Chehab #include "sms-cards.h" 4680ccb51aSMauro Carvalho Chehab #include "smsendian.h" 470013ca8cSMauro Carvalho Chehab 480013ca8cSMauro Carvalho Chehab /* Registers */ 490013ca8cSMauro Carvalho Chehab 500013ca8cSMauro Carvalho Chehab #define SMSSDIO_DATA 0x00 510013ca8cSMauro Carvalho Chehab #define SMSSDIO_INT 0x04 520013ca8cSMauro Carvalho Chehab #define SMSSDIO_BLOCK_SIZE 128 530013ca8cSMauro Carvalho Chehab 544c62e976SGreg Kroah-Hartman static const struct sdio_device_id smssdio_ids[] = { 550013ca8cSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), 560013ca8cSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, 570013ca8cSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), 580013ca8cSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, 590013ca8cSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), 600013ca8cSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, 610013ca8cSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), 620013ca8cSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, 630013ca8cSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), 640013ca8cSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, 65347d8f1fSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302), 66347d8f1fSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_MING}, 67347d8f1fSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500), 68347d8f1fSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_PELE}, 69347d8f1fSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600), 70347d8f1fSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_RIO}, 71347d8f1fSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700), 72347d8f1fSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160}, 73347d8f1fSMauro Carvalho Chehab {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800), 74347d8f1fSMauro Carvalho Chehab .driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530}, 750013ca8cSMauro Carvalho Chehab { /* end: all zeroes */ }, 760013ca8cSMauro Carvalho Chehab }; 770013ca8cSMauro Carvalho Chehab 780013ca8cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(sdio, smssdio_ids); 790013ca8cSMauro Carvalho Chehab 800013ca8cSMauro Carvalho Chehab struct smssdio_device { 810013ca8cSMauro Carvalho Chehab struct sdio_func *func; 820013ca8cSMauro Carvalho Chehab 830013ca8cSMauro Carvalho Chehab struct smscore_device_t *coredev; 840013ca8cSMauro Carvalho Chehab 850013ca8cSMauro Carvalho Chehab struct smscore_buffer_t *split_cb; 860013ca8cSMauro Carvalho Chehab }; 870013ca8cSMauro Carvalho Chehab 880013ca8cSMauro Carvalho Chehab /*******************************************************************/ 890013ca8cSMauro Carvalho Chehab /* Siano core callbacks */ 900013ca8cSMauro Carvalho Chehab /*******************************************************************/ 910013ca8cSMauro Carvalho Chehab 920013ca8cSMauro Carvalho Chehab static int smssdio_sendrequest(void *context, void *buffer, size_t size) 930013ca8cSMauro Carvalho Chehab { 940013ca8cSMauro Carvalho Chehab int ret = 0; 950013ca8cSMauro Carvalho Chehab struct smssdio_device *smsdev; 960013ca8cSMauro Carvalho Chehab 970013ca8cSMauro Carvalho Chehab smsdev = context; 980013ca8cSMauro Carvalho Chehab 990013ca8cSMauro Carvalho Chehab sdio_claim_host(smsdev->func); 1000013ca8cSMauro Carvalho Chehab 101dfef84fcSMauro Carvalho Chehab smsendian_handle_tx_message((struct sms_msg_data *) buffer); 1020013ca8cSMauro Carvalho Chehab while (size >= smsdev->func->cur_blksize) { 1030013ca8cSMauro Carvalho Chehab ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, 1040013ca8cSMauro Carvalho Chehab buffer, smsdev->func->cur_blksize); 1050013ca8cSMauro Carvalho Chehab if (ret) 1060013ca8cSMauro Carvalho Chehab goto out; 1070013ca8cSMauro Carvalho Chehab 1080013ca8cSMauro Carvalho Chehab buffer += smsdev->func->cur_blksize; 1090013ca8cSMauro Carvalho Chehab size -= smsdev->func->cur_blksize; 1100013ca8cSMauro Carvalho Chehab } 1110013ca8cSMauro Carvalho Chehab 1120013ca8cSMauro Carvalho Chehab if (size) { 1130013ca8cSMauro Carvalho Chehab ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, 1140013ca8cSMauro Carvalho Chehab buffer, size); 1150013ca8cSMauro Carvalho Chehab } 1160013ca8cSMauro Carvalho Chehab 1170013ca8cSMauro Carvalho Chehab out: 1180013ca8cSMauro Carvalho Chehab sdio_release_host(smsdev->func); 1190013ca8cSMauro Carvalho Chehab 1200013ca8cSMauro Carvalho Chehab return ret; 1210013ca8cSMauro Carvalho Chehab } 1220013ca8cSMauro Carvalho Chehab 1230013ca8cSMauro Carvalho Chehab /*******************************************************************/ 1240013ca8cSMauro Carvalho Chehab /* SDIO callbacks */ 1250013ca8cSMauro Carvalho Chehab /*******************************************************************/ 1260013ca8cSMauro Carvalho Chehab 1270013ca8cSMauro Carvalho Chehab static void smssdio_interrupt(struct sdio_func *func) 1280013ca8cSMauro Carvalho Chehab { 1290013ca8cSMauro Carvalho Chehab int ret; 1300013ca8cSMauro Carvalho Chehab 1310013ca8cSMauro Carvalho Chehab struct smssdio_device *smsdev; 1320013ca8cSMauro Carvalho Chehab struct smscore_buffer_t *cb; 133dfef84fcSMauro Carvalho Chehab struct sms_msg_hdr *hdr; 1340013ca8cSMauro Carvalho Chehab size_t size; 1350013ca8cSMauro Carvalho Chehab 1360013ca8cSMauro Carvalho Chehab smsdev = sdio_get_drvdata(func); 1370013ca8cSMauro Carvalho Chehab 1380013ca8cSMauro Carvalho Chehab /* 1390013ca8cSMauro Carvalho Chehab * The interrupt register has no defined meaning. It is just 1400013ca8cSMauro Carvalho Chehab * a way of turning of the level triggered interrupt. 1410013ca8cSMauro Carvalho Chehab */ 1420013ca8cSMauro Carvalho Chehab (void)sdio_readb(func, SMSSDIO_INT, &ret); 1430013ca8cSMauro Carvalho Chehab if (ret) { 1440013ca8cSMauro Carvalho Chehab sms_err("Unable to read interrupt register!\n"); 1450013ca8cSMauro Carvalho Chehab return; 1460013ca8cSMauro Carvalho Chehab } 1470013ca8cSMauro Carvalho Chehab 1480013ca8cSMauro Carvalho Chehab if (smsdev->split_cb == NULL) { 1490013ca8cSMauro Carvalho Chehab cb = smscore_getbuffer(smsdev->coredev); 1500013ca8cSMauro Carvalho Chehab if (!cb) { 1510013ca8cSMauro Carvalho Chehab sms_err("Unable to allocate data buffer!\n"); 1520013ca8cSMauro Carvalho Chehab return; 1530013ca8cSMauro Carvalho Chehab } 1540013ca8cSMauro Carvalho Chehab 1550013ca8cSMauro Carvalho Chehab ret = sdio_memcpy_fromio(smsdev->func, 1560013ca8cSMauro Carvalho Chehab cb->p, 1570013ca8cSMauro Carvalho Chehab SMSSDIO_DATA, 1580013ca8cSMauro Carvalho Chehab SMSSDIO_BLOCK_SIZE); 1590013ca8cSMauro Carvalho Chehab if (ret) { 1600013ca8cSMauro Carvalho Chehab sms_err("Error %d reading initial block!\n", ret); 1610013ca8cSMauro Carvalho Chehab return; 1620013ca8cSMauro Carvalho Chehab } 1630013ca8cSMauro Carvalho Chehab 1640013ca8cSMauro Carvalho Chehab hdr = cb->p; 1650013ca8cSMauro Carvalho Chehab 166dfef84fcSMauro Carvalho Chehab if (hdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG) { 1670013ca8cSMauro Carvalho Chehab smsdev->split_cb = cb; 1680013ca8cSMauro Carvalho Chehab return; 1690013ca8cSMauro Carvalho Chehab } 1700013ca8cSMauro Carvalho Chehab 171dfef84fcSMauro Carvalho Chehab if (hdr->msg_length > smsdev->func->cur_blksize) 172dfef84fcSMauro Carvalho Chehab size = hdr->msg_length - smsdev->func->cur_blksize; 1730013ca8cSMauro Carvalho Chehab else 1740013ca8cSMauro Carvalho Chehab size = 0; 1750013ca8cSMauro Carvalho Chehab } else { 1760013ca8cSMauro Carvalho Chehab cb = smsdev->split_cb; 1770013ca8cSMauro Carvalho Chehab hdr = cb->p; 1780013ca8cSMauro Carvalho Chehab 179dfef84fcSMauro Carvalho Chehab size = hdr->msg_length - sizeof(struct sms_msg_hdr); 1800013ca8cSMauro Carvalho Chehab 1810013ca8cSMauro Carvalho Chehab smsdev->split_cb = NULL; 1820013ca8cSMauro Carvalho Chehab } 1830013ca8cSMauro Carvalho Chehab 1840013ca8cSMauro Carvalho Chehab if (size) { 1850013ca8cSMauro Carvalho Chehab void *buffer; 1860013ca8cSMauro Carvalho Chehab 187dfef84fcSMauro Carvalho Chehab buffer = cb->p + (hdr->msg_length - size); 1880013ca8cSMauro Carvalho Chehab size = ALIGN(size, SMSSDIO_BLOCK_SIZE); 1890013ca8cSMauro Carvalho Chehab 1900013ca8cSMauro Carvalho Chehab BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); 1910013ca8cSMauro Carvalho Chehab 1920013ca8cSMauro Carvalho Chehab /* 1930013ca8cSMauro Carvalho Chehab * First attempt to transfer all of it in one go... 1940013ca8cSMauro Carvalho Chehab */ 1950013ca8cSMauro Carvalho Chehab ret = sdio_memcpy_fromio(smsdev->func, 1960013ca8cSMauro Carvalho Chehab buffer, 1970013ca8cSMauro Carvalho Chehab SMSSDIO_DATA, 1980013ca8cSMauro Carvalho Chehab size); 1990013ca8cSMauro Carvalho Chehab if (ret && ret != -EINVAL) { 2000013ca8cSMauro Carvalho Chehab smscore_putbuffer(smsdev->coredev, cb); 2010013ca8cSMauro Carvalho Chehab sms_err("Error %d reading data from card!\n", ret); 2020013ca8cSMauro Carvalho Chehab return; 2030013ca8cSMauro Carvalho Chehab } 2040013ca8cSMauro Carvalho Chehab 2050013ca8cSMauro Carvalho Chehab /* 2060013ca8cSMauro Carvalho Chehab * ..then fall back to one block at a time if that is 2070013ca8cSMauro Carvalho Chehab * not possible... 2080013ca8cSMauro Carvalho Chehab * 2090013ca8cSMauro Carvalho Chehab * (we have to do this manually because of the 2100013ca8cSMauro Carvalho Chehab * problem with the "increase address" bit) 2110013ca8cSMauro Carvalho Chehab */ 2120013ca8cSMauro Carvalho Chehab if (ret == -EINVAL) { 2130013ca8cSMauro Carvalho Chehab while (size) { 2140013ca8cSMauro Carvalho Chehab ret = sdio_memcpy_fromio(smsdev->func, 2150013ca8cSMauro Carvalho Chehab buffer, SMSSDIO_DATA, 2160013ca8cSMauro Carvalho Chehab smsdev->func->cur_blksize); 2170013ca8cSMauro Carvalho Chehab if (ret) { 2180013ca8cSMauro Carvalho Chehab smscore_putbuffer(smsdev->coredev, cb); 2190013ca8cSMauro Carvalho Chehab sms_err("Error %d reading " 2200013ca8cSMauro Carvalho Chehab "data from card!\n", ret); 2210013ca8cSMauro Carvalho Chehab return; 2220013ca8cSMauro Carvalho Chehab } 2230013ca8cSMauro Carvalho Chehab 2240013ca8cSMauro Carvalho Chehab buffer += smsdev->func->cur_blksize; 2250013ca8cSMauro Carvalho Chehab if (size > smsdev->func->cur_blksize) 2260013ca8cSMauro Carvalho Chehab size -= smsdev->func->cur_blksize; 2270013ca8cSMauro Carvalho Chehab else 2280013ca8cSMauro Carvalho Chehab size = 0; 2290013ca8cSMauro Carvalho Chehab } 2300013ca8cSMauro Carvalho Chehab } 2310013ca8cSMauro Carvalho Chehab } 2320013ca8cSMauro Carvalho Chehab 233dfef84fcSMauro Carvalho Chehab cb->size = hdr->msg_length; 2340013ca8cSMauro Carvalho Chehab cb->offset = 0; 2350013ca8cSMauro Carvalho Chehab 236dfef84fcSMauro Carvalho Chehab smsendian_handle_rx_message((struct sms_msg_data *) cb->p); 2370013ca8cSMauro Carvalho Chehab smscore_onresponse(smsdev->coredev, cb); 2380013ca8cSMauro Carvalho Chehab } 2390013ca8cSMauro Carvalho Chehab 2404c62e976SGreg Kroah-Hartman static int smssdio_probe(struct sdio_func *func, 2410013ca8cSMauro Carvalho Chehab const struct sdio_device_id *id) 2420013ca8cSMauro Carvalho Chehab { 2430013ca8cSMauro Carvalho Chehab int ret; 2440013ca8cSMauro Carvalho Chehab 2450013ca8cSMauro Carvalho Chehab int board_id; 2460013ca8cSMauro Carvalho Chehab struct smssdio_device *smsdev; 2470013ca8cSMauro Carvalho Chehab struct smsdevice_params_t params; 2480013ca8cSMauro Carvalho Chehab 2490013ca8cSMauro Carvalho Chehab board_id = id->driver_data; 2500013ca8cSMauro Carvalho Chehab 2510013ca8cSMauro Carvalho Chehab smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); 2520013ca8cSMauro Carvalho Chehab if (!smsdev) 2530013ca8cSMauro Carvalho Chehab return -ENOMEM; 2540013ca8cSMauro Carvalho Chehab 2550013ca8cSMauro Carvalho Chehab smsdev->func = func; 2560013ca8cSMauro Carvalho Chehab 2570013ca8cSMauro Carvalho Chehab memset(¶ms, 0, sizeof(struct smsdevice_params_t)); 2580013ca8cSMauro Carvalho Chehab 2590013ca8cSMauro Carvalho Chehab params.device = &func->dev; 2600013ca8cSMauro Carvalho Chehab params.buffer_size = 0x5000; /* ?? */ 2610013ca8cSMauro Carvalho Chehab params.num_buffers = 22; /* ?? */ 2620013ca8cSMauro Carvalho Chehab params.context = smsdev; 2630013ca8cSMauro Carvalho Chehab 2640013ca8cSMauro Carvalho Chehab snprintf(params.devpath, sizeof(params.devpath), 2650013ca8cSMauro Carvalho Chehab "sdio\\%s", sdio_func_id(func)); 2660013ca8cSMauro Carvalho Chehab 2670013ca8cSMauro Carvalho Chehab params.sendrequest_handler = smssdio_sendrequest; 2680013ca8cSMauro Carvalho Chehab 2690013ca8cSMauro Carvalho Chehab params.device_type = sms_get_board(board_id)->type; 2700013ca8cSMauro Carvalho Chehab 2710013ca8cSMauro Carvalho Chehab if (params.device_type != SMS_STELLAR) 2720013ca8cSMauro Carvalho Chehab params.flags |= SMS_DEVICE_FAMILY2; 2730013ca8cSMauro Carvalho Chehab else { 2740013ca8cSMauro Carvalho Chehab /* 2750013ca8cSMauro Carvalho Chehab * FIXME: Stellar needs special handling... 2760013ca8cSMauro Carvalho Chehab */ 2770013ca8cSMauro Carvalho Chehab ret = -ENODEV; 2780013ca8cSMauro Carvalho Chehab goto free; 2790013ca8cSMauro Carvalho Chehab } 2800013ca8cSMauro Carvalho Chehab 2810013ca8cSMauro Carvalho Chehab ret = smscore_register_device(¶ms, &smsdev->coredev); 2820013ca8cSMauro Carvalho Chehab if (ret < 0) 2830013ca8cSMauro Carvalho Chehab goto free; 2840013ca8cSMauro Carvalho Chehab 2850013ca8cSMauro Carvalho Chehab smscore_set_board_id(smsdev->coredev, board_id); 2860013ca8cSMauro Carvalho Chehab 2870013ca8cSMauro Carvalho Chehab sdio_claim_host(func); 2880013ca8cSMauro Carvalho Chehab 2890013ca8cSMauro Carvalho Chehab ret = sdio_enable_func(func); 2900013ca8cSMauro Carvalho Chehab if (ret) 2910013ca8cSMauro Carvalho Chehab goto release; 2920013ca8cSMauro Carvalho Chehab 2930013ca8cSMauro Carvalho Chehab ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); 2940013ca8cSMauro Carvalho Chehab if (ret) 2950013ca8cSMauro Carvalho Chehab goto disable; 2960013ca8cSMauro Carvalho Chehab 2970013ca8cSMauro Carvalho Chehab ret = sdio_claim_irq(func, smssdio_interrupt); 2980013ca8cSMauro Carvalho Chehab if (ret) 2990013ca8cSMauro Carvalho Chehab goto disable; 3000013ca8cSMauro Carvalho Chehab 3010013ca8cSMauro Carvalho Chehab sdio_set_drvdata(func, smsdev); 3020013ca8cSMauro Carvalho Chehab 3030013ca8cSMauro Carvalho Chehab sdio_release_host(func); 3040013ca8cSMauro Carvalho Chehab 3050013ca8cSMauro Carvalho Chehab ret = smscore_start_device(smsdev->coredev); 3060013ca8cSMauro Carvalho Chehab if (ret < 0) 3070013ca8cSMauro Carvalho Chehab goto reclaim; 3080013ca8cSMauro Carvalho Chehab 3090013ca8cSMauro Carvalho Chehab return 0; 3100013ca8cSMauro Carvalho Chehab 3110013ca8cSMauro Carvalho Chehab reclaim: 3120013ca8cSMauro Carvalho Chehab sdio_claim_host(func); 3130013ca8cSMauro Carvalho Chehab sdio_release_irq(func); 3140013ca8cSMauro Carvalho Chehab disable: 3150013ca8cSMauro Carvalho Chehab sdio_disable_func(func); 3160013ca8cSMauro Carvalho Chehab release: 3170013ca8cSMauro Carvalho Chehab sdio_release_host(func); 3180013ca8cSMauro Carvalho Chehab smscore_unregister_device(smsdev->coredev); 3190013ca8cSMauro Carvalho Chehab free: 3200013ca8cSMauro Carvalho Chehab kfree(smsdev); 3210013ca8cSMauro Carvalho Chehab 3220013ca8cSMauro Carvalho Chehab return ret; 3230013ca8cSMauro Carvalho Chehab } 3240013ca8cSMauro Carvalho Chehab 3250013ca8cSMauro Carvalho Chehab static void smssdio_remove(struct sdio_func *func) 3260013ca8cSMauro Carvalho Chehab { 3270013ca8cSMauro Carvalho Chehab struct smssdio_device *smsdev; 3280013ca8cSMauro Carvalho Chehab 3290013ca8cSMauro Carvalho Chehab smsdev = sdio_get_drvdata(func); 3300013ca8cSMauro Carvalho Chehab 3310013ca8cSMauro Carvalho Chehab /* FIXME: racy! */ 3320013ca8cSMauro Carvalho Chehab if (smsdev->split_cb) 3330013ca8cSMauro Carvalho Chehab smscore_putbuffer(smsdev->coredev, smsdev->split_cb); 3340013ca8cSMauro Carvalho Chehab 3350013ca8cSMauro Carvalho Chehab smscore_unregister_device(smsdev->coredev); 3360013ca8cSMauro Carvalho Chehab 3370013ca8cSMauro Carvalho Chehab sdio_claim_host(func); 3380013ca8cSMauro Carvalho Chehab sdio_release_irq(func); 3390013ca8cSMauro Carvalho Chehab sdio_disable_func(func); 3400013ca8cSMauro Carvalho Chehab sdio_release_host(func); 3410013ca8cSMauro Carvalho Chehab 3420013ca8cSMauro Carvalho Chehab kfree(smsdev); 3430013ca8cSMauro Carvalho Chehab } 3440013ca8cSMauro Carvalho Chehab 3450013ca8cSMauro Carvalho Chehab static struct sdio_driver smssdio_driver = { 3460013ca8cSMauro Carvalho Chehab .name = "smssdio", 3470013ca8cSMauro Carvalho Chehab .id_table = smssdio_ids, 3480013ca8cSMauro Carvalho Chehab .probe = smssdio_probe, 3490013ca8cSMauro Carvalho Chehab .remove = smssdio_remove, 3500013ca8cSMauro Carvalho Chehab }; 3510013ca8cSMauro Carvalho Chehab 3520013ca8cSMauro Carvalho Chehab /*******************************************************************/ 3530013ca8cSMauro Carvalho Chehab /* Module functions */ 3540013ca8cSMauro Carvalho Chehab /*******************************************************************/ 3550013ca8cSMauro Carvalho Chehab 3560013ca8cSMauro Carvalho Chehab static int __init smssdio_module_init(void) 3570013ca8cSMauro Carvalho Chehab { 3580013ca8cSMauro Carvalho Chehab int ret = 0; 3590013ca8cSMauro Carvalho Chehab 3600013ca8cSMauro Carvalho Chehab printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); 3610013ca8cSMauro Carvalho Chehab printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); 3620013ca8cSMauro Carvalho Chehab 3630013ca8cSMauro Carvalho Chehab ret = sdio_register_driver(&smssdio_driver); 3640013ca8cSMauro Carvalho Chehab 3650013ca8cSMauro Carvalho Chehab return ret; 3660013ca8cSMauro Carvalho Chehab } 3670013ca8cSMauro Carvalho Chehab 3680013ca8cSMauro Carvalho Chehab static void __exit smssdio_module_exit(void) 3690013ca8cSMauro Carvalho Chehab { 3700013ca8cSMauro Carvalho Chehab sdio_unregister_driver(&smssdio_driver); 3710013ca8cSMauro Carvalho Chehab } 3720013ca8cSMauro Carvalho Chehab 3730013ca8cSMauro Carvalho Chehab module_init(smssdio_module_init); 3740013ca8cSMauro Carvalho Chehab module_exit(smssdio_module_exit); 3750013ca8cSMauro Carvalho Chehab 3760013ca8cSMauro Carvalho Chehab MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); 3770013ca8cSMauro Carvalho Chehab MODULE_AUTHOR("Pierre Ossman"); 3780013ca8cSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 379