11c6a0718SPierre Ossman /* 270f10482SPierre Ossman * linux/drivers/mmc/host/wbsd.c - Winbond W83L51xD SD/MMC driver 31c6a0718SPierre Ossman * 41c6a0718SPierre Ossman * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. 51c6a0718SPierre Ossman * 61c6a0718SPierre Ossman * This program is free software; you can redistribute it and/or modify 71c6a0718SPierre Ossman * it under the terms of the GNU General Public License as published by 81c6a0718SPierre Ossman * the Free Software Foundation; either version 2 of the License, or (at 91c6a0718SPierre Ossman * your option) any later version. 101c6a0718SPierre Ossman * 111c6a0718SPierre Ossman * 121c6a0718SPierre Ossman * Warning! 131c6a0718SPierre Ossman * 141c6a0718SPierre Ossman * Changes to the FIFO system should be done with extreme care since 151c6a0718SPierre Ossman * the hardware is full of bugs related to the FIFO. Known issues are: 161c6a0718SPierre Ossman * 171c6a0718SPierre Ossman * - FIFO size field in FSR is always zero. 181c6a0718SPierre Ossman * 191c6a0718SPierre Ossman * - FIFO interrupts tend not to work as they should. Interrupts are 201c6a0718SPierre Ossman * triggered only for full/empty events, not for threshold values. 211c6a0718SPierre Ossman * 221c6a0718SPierre Ossman * - On APIC systems the FIFO empty interrupt is sometimes lost. 231c6a0718SPierre Ossman */ 241c6a0718SPierre Ossman 251c6a0718SPierre Ossman #include <linux/module.h> 261c6a0718SPierre Ossman #include <linux/moduleparam.h> 271c6a0718SPierre Ossman #include <linux/init.h> 281c6a0718SPierre Ossman #include <linux/ioport.h> 291c6a0718SPierre Ossman #include <linux/platform_device.h> 301c6a0718SPierre Ossman #include <linux/interrupt.h> 311c6a0718SPierre Ossman #include <linux/dma-mapping.h> 321c6a0718SPierre Ossman #include <linux/delay.h> 331c6a0718SPierre Ossman #include <linux/pnp.h> 341c6a0718SPierre Ossman #include <linux/highmem.h> 351c6a0718SPierre Ossman #include <linux/mmc/host.h> 36bd6dee6fSJens Axboe #include <linux/scatterlist.h> 375a0e3ad6STejun Heo #include <linux/slab.h> 381c6a0718SPierre Ossman 391c6a0718SPierre Ossman #include <asm/io.h> 401c6a0718SPierre Ossman #include <asm/dma.h> 411c6a0718SPierre Ossman 421c6a0718SPierre Ossman #include "wbsd.h" 431c6a0718SPierre Ossman 441c6a0718SPierre Ossman #define DRIVER_NAME "wbsd" 451c6a0718SPierre Ossman 461c6a0718SPierre Ossman #define DBG(x...) \ 471c6a0718SPierre Ossman pr_debug(DRIVER_NAME ": " x) 481c6a0718SPierre Ossman #define DBGF(f, x...) \ 491c6a0718SPierre Ossman pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x) 501c6a0718SPierre Ossman 511c6a0718SPierre Ossman /* 521c6a0718SPierre Ossman * Device resources 531c6a0718SPierre Ossman */ 541c6a0718SPierre Ossman 551c6a0718SPierre Ossman #ifdef CONFIG_PNP 561c6a0718SPierre Ossman 571c6a0718SPierre Ossman static const struct pnp_device_id pnp_dev_table[] = { 581c6a0718SPierre Ossman { "WEC0517", 0 }, 591c6a0718SPierre Ossman { "WEC0518", 0 }, 601c6a0718SPierre Ossman { "", 0 }, 611c6a0718SPierre Ossman }; 621c6a0718SPierre Ossman 631c6a0718SPierre Ossman MODULE_DEVICE_TABLE(pnp, pnp_dev_table); 641c6a0718SPierre Ossman 651c6a0718SPierre Ossman #endif /* CONFIG_PNP */ 661c6a0718SPierre Ossman 671c6a0718SPierre Ossman static const int config_ports[] = { 0x2E, 0x4E }; 681c6a0718SPierre Ossman static const int unlock_codes[] = { 0x83, 0x87 }; 691c6a0718SPierre Ossman 701c6a0718SPierre Ossman static const int valid_ids[] = { 711c6a0718SPierre Ossman 0x7112, 721c6a0718SPierre Ossman }; 731c6a0718SPierre Ossman 741c6a0718SPierre Ossman #ifdef CONFIG_PNP 759eeebd22STomas Winkler static unsigned int param_nopnp = 0; 761c6a0718SPierre Ossman #else 779eeebd22STomas Winkler static const unsigned int param_nopnp = 1; 781c6a0718SPierre Ossman #endif 799eeebd22STomas Winkler static unsigned int param_io = 0x248; 809eeebd22STomas Winkler static unsigned int param_irq = 6; 819eeebd22STomas Winkler static int param_dma = 2; 821c6a0718SPierre Ossman 831c6a0718SPierre Ossman /* 841c6a0718SPierre Ossman * Basic functions 851c6a0718SPierre Ossman */ 861c6a0718SPierre Ossman 871c6a0718SPierre Ossman static inline void wbsd_unlock_config(struct wbsd_host *host) 881c6a0718SPierre Ossman { 891c6a0718SPierre Ossman BUG_ON(host->config == 0); 901c6a0718SPierre Ossman 911c6a0718SPierre Ossman outb(host->unlock_code, host->config); 921c6a0718SPierre Ossman outb(host->unlock_code, host->config); 931c6a0718SPierre Ossman } 941c6a0718SPierre Ossman 951c6a0718SPierre Ossman static inline void wbsd_lock_config(struct wbsd_host *host) 961c6a0718SPierre Ossman { 971c6a0718SPierre Ossman BUG_ON(host->config == 0); 981c6a0718SPierre Ossman 991c6a0718SPierre Ossman outb(LOCK_CODE, host->config); 1001c6a0718SPierre Ossman } 1011c6a0718SPierre Ossman 1021c6a0718SPierre Ossman static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value) 1031c6a0718SPierre Ossman { 1041c6a0718SPierre Ossman BUG_ON(host->config == 0); 1051c6a0718SPierre Ossman 1061c6a0718SPierre Ossman outb(reg, host->config); 1071c6a0718SPierre Ossman outb(value, host->config + 1); 1081c6a0718SPierre Ossman } 1091c6a0718SPierre Ossman 1101c6a0718SPierre Ossman static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg) 1111c6a0718SPierre Ossman { 1121c6a0718SPierre Ossman BUG_ON(host->config == 0); 1131c6a0718SPierre Ossman 1141c6a0718SPierre Ossman outb(reg, host->config); 1151c6a0718SPierre Ossman return inb(host->config + 1); 1161c6a0718SPierre Ossman } 1171c6a0718SPierre Ossman 1181c6a0718SPierre Ossman static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value) 1191c6a0718SPierre Ossman { 1201c6a0718SPierre Ossman outb(index, host->base + WBSD_IDXR); 1211c6a0718SPierre Ossman outb(value, host->base + WBSD_DATAR); 1221c6a0718SPierre Ossman } 1231c6a0718SPierre Ossman 1241c6a0718SPierre Ossman static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index) 1251c6a0718SPierre Ossman { 1261c6a0718SPierre Ossman outb(index, host->base + WBSD_IDXR); 1271c6a0718SPierre Ossman return inb(host->base + WBSD_DATAR); 1281c6a0718SPierre Ossman } 1291c6a0718SPierre Ossman 1301c6a0718SPierre Ossman /* 1311c6a0718SPierre Ossman * Common routines 1321c6a0718SPierre Ossman */ 1331c6a0718SPierre Ossman 1341c6a0718SPierre Ossman static void wbsd_init_device(struct wbsd_host *host) 1351c6a0718SPierre Ossman { 1361c6a0718SPierre Ossman u8 setup, ier; 1371c6a0718SPierre Ossman 1381c6a0718SPierre Ossman /* 1391c6a0718SPierre Ossman * Reset chip (SD/MMC part) and fifo. 1401c6a0718SPierre Ossman */ 1411c6a0718SPierre Ossman setup = wbsd_read_index(host, WBSD_IDX_SETUP); 1421c6a0718SPierre Ossman setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; 1431c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_SETUP, setup); 1441c6a0718SPierre Ossman 1451c6a0718SPierre Ossman /* 1461c6a0718SPierre Ossman * Set DAT3 to input 1471c6a0718SPierre Ossman */ 1481c6a0718SPierre Ossman setup &= ~WBSD_DAT3_H; 1491c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_SETUP, setup); 1501c6a0718SPierre Ossman host->flags &= ~WBSD_FIGNORE_DETECT; 1511c6a0718SPierre Ossman 1521c6a0718SPierre Ossman /* 1531c6a0718SPierre Ossman * Read back default clock. 1541c6a0718SPierre Ossman */ 1551c6a0718SPierre Ossman host->clk = wbsd_read_index(host, WBSD_IDX_CLK); 1561c6a0718SPierre Ossman 1571c6a0718SPierre Ossman /* 1581c6a0718SPierre Ossman * Power down port. 1591c6a0718SPierre Ossman */ 1601c6a0718SPierre Ossman outb(WBSD_POWER_N, host->base + WBSD_CSR); 1611c6a0718SPierre Ossman 1621c6a0718SPierre Ossman /* 1631c6a0718SPierre Ossman * Set maximum timeout. 1641c6a0718SPierre Ossman */ 1651c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); 1661c6a0718SPierre Ossman 1671c6a0718SPierre Ossman /* 1681c6a0718SPierre Ossman * Test for card presence 1691c6a0718SPierre Ossman */ 1701c6a0718SPierre Ossman if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) 1711c6a0718SPierre Ossman host->flags |= WBSD_FCARD_PRESENT; 1721c6a0718SPierre Ossman else 1731c6a0718SPierre Ossman host->flags &= ~WBSD_FCARD_PRESENT; 1741c6a0718SPierre Ossman 1751c6a0718SPierre Ossman /* 1761c6a0718SPierre Ossman * Enable interesting interrupts. 1771c6a0718SPierre Ossman */ 1781c6a0718SPierre Ossman ier = 0; 1791c6a0718SPierre Ossman ier |= WBSD_EINT_CARD; 1801c6a0718SPierre Ossman ier |= WBSD_EINT_FIFO_THRE; 1811c6a0718SPierre Ossman ier |= WBSD_EINT_CRC; 1821c6a0718SPierre Ossman ier |= WBSD_EINT_TIMEOUT; 1831c6a0718SPierre Ossman ier |= WBSD_EINT_TC; 1841c6a0718SPierre Ossman 1851c6a0718SPierre Ossman outb(ier, host->base + WBSD_EIR); 1861c6a0718SPierre Ossman 1871c6a0718SPierre Ossman /* 1881c6a0718SPierre Ossman * Clear interrupts. 1891c6a0718SPierre Ossman */ 1901c6a0718SPierre Ossman inb(host->base + WBSD_ISR); 1911c6a0718SPierre Ossman } 1921c6a0718SPierre Ossman 1931c6a0718SPierre Ossman static void wbsd_reset(struct wbsd_host *host) 1941c6a0718SPierre Ossman { 1951c6a0718SPierre Ossman u8 setup; 1961c6a0718SPierre Ossman 197a3c76eb9SGirish K S pr_err("%s: Resetting chip\n", mmc_hostname(host->mmc)); 1981c6a0718SPierre Ossman 1991c6a0718SPierre Ossman /* 2001c6a0718SPierre Ossman * Soft reset of chip (SD/MMC part). 2011c6a0718SPierre Ossman */ 2021c6a0718SPierre Ossman setup = wbsd_read_index(host, WBSD_IDX_SETUP); 2031c6a0718SPierre Ossman setup |= WBSD_SOFT_RESET; 2041c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_SETUP, setup); 2051c6a0718SPierre Ossman } 2061c6a0718SPierre Ossman 2071c6a0718SPierre Ossman static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq) 2081c6a0718SPierre Ossman { 2091c6a0718SPierre Ossman unsigned long dmaflags; 2101c6a0718SPierre Ossman 2111c6a0718SPierre Ossman if (host->dma >= 0) { 2121c6a0718SPierre Ossman /* 2131c6a0718SPierre Ossman * Release ISA DMA controller. 2141c6a0718SPierre Ossman */ 2151c6a0718SPierre Ossman dmaflags = claim_dma_lock(); 2161c6a0718SPierre Ossman disable_dma(host->dma); 2171c6a0718SPierre Ossman clear_dma_ff(host->dma); 2181c6a0718SPierre Ossman release_dma_lock(dmaflags); 2191c6a0718SPierre Ossman 2201c6a0718SPierre Ossman /* 2211c6a0718SPierre Ossman * Disable DMA on host. 2221c6a0718SPierre Ossman */ 2231c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_DMA, 0); 2241c6a0718SPierre Ossman } 2251c6a0718SPierre Ossman 2261c6a0718SPierre Ossman host->mrq = NULL; 2271c6a0718SPierre Ossman 2281c6a0718SPierre Ossman /* 2291c6a0718SPierre Ossman * MMC layer might call back into the driver so first unlock. 2301c6a0718SPierre Ossman */ 2311c6a0718SPierre Ossman spin_unlock(&host->lock); 2321c6a0718SPierre Ossman mmc_request_done(host->mmc, mrq); 2331c6a0718SPierre Ossman spin_lock(&host->lock); 2341c6a0718SPierre Ossman } 2351c6a0718SPierre Ossman 2361c6a0718SPierre Ossman /* 2371c6a0718SPierre Ossman * Scatter/gather functions 2381c6a0718SPierre Ossman */ 2391c6a0718SPierre Ossman 2401c6a0718SPierre Ossman static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data) 2411c6a0718SPierre Ossman { 2421c6a0718SPierre Ossman /* 2431c6a0718SPierre Ossman * Get info. about SG list from data structure. 2441c6a0718SPierre Ossman */ 2451c6a0718SPierre Ossman host->cur_sg = data->sg; 2461c6a0718SPierre Ossman host->num_sg = data->sg_len; 2471c6a0718SPierre Ossman 2481c6a0718SPierre Ossman host->offset = 0; 2491c6a0718SPierre Ossman host->remain = host->cur_sg->length; 2501c6a0718SPierre Ossman } 2511c6a0718SPierre Ossman 2521c6a0718SPierre Ossman static inline int wbsd_next_sg(struct wbsd_host *host) 2531c6a0718SPierre Ossman { 2541c6a0718SPierre Ossman /* 2551c6a0718SPierre Ossman * Skip to next SG entry. 2561c6a0718SPierre Ossman */ 2571c6a0718SPierre Ossman host->cur_sg++; 2581c6a0718SPierre Ossman host->num_sg--; 2591c6a0718SPierre Ossman 2601c6a0718SPierre Ossman /* 2611c6a0718SPierre Ossman * Any entries left? 2621c6a0718SPierre Ossman */ 2631c6a0718SPierre Ossman if (host->num_sg > 0) { 2641c6a0718SPierre Ossman host->offset = 0; 2651c6a0718SPierre Ossman host->remain = host->cur_sg->length; 2661c6a0718SPierre Ossman } 2671c6a0718SPierre Ossman 2681c6a0718SPierre Ossman return host->num_sg; 2691c6a0718SPierre Ossman } 2701c6a0718SPierre Ossman 2711c6a0718SPierre Ossman static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) 2721c6a0718SPierre Ossman { 27345711f1aSJens Axboe return sg_virt(host->cur_sg); 2741c6a0718SPierre Ossman } 2751c6a0718SPierre Ossman 2761c6a0718SPierre Ossman static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) 2771c6a0718SPierre Ossman { 2781c6a0718SPierre Ossman unsigned int len, i; 2791c6a0718SPierre Ossman struct scatterlist *sg; 2801c6a0718SPierre Ossman char *dmabuf = host->dma_buffer; 2811c6a0718SPierre Ossman char *sgbuf; 2821c6a0718SPierre Ossman 2831c6a0718SPierre Ossman sg = data->sg; 2841c6a0718SPierre Ossman len = data->sg_len; 2851c6a0718SPierre Ossman 2861c6a0718SPierre Ossman for (i = 0; i < len; i++) { 28745711f1aSJens Axboe sgbuf = sg_virt(&sg[i]); 2881c6a0718SPierre Ossman memcpy(dmabuf, sgbuf, sg[i].length); 2891c6a0718SPierre Ossman dmabuf += sg[i].length; 2901c6a0718SPierre Ossman } 2911c6a0718SPierre Ossman } 2921c6a0718SPierre Ossman 2931c6a0718SPierre Ossman static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) 2941c6a0718SPierre Ossman { 2951c6a0718SPierre Ossman unsigned int len, i; 2961c6a0718SPierre Ossman struct scatterlist *sg; 2971c6a0718SPierre Ossman char *dmabuf = host->dma_buffer; 2981c6a0718SPierre Ossman char *sgbuf; 2991c6a0718SPierre Ossman 3001c6a0718SPierre Ossman sg = data->sg; 3011c6a0718SPierre Ossman len = data->sg_len; 3021c6a0718SPierre Ossman 3031c6a0718SPierre Ossman for (i = 0; i < len; i++) { 30445711f1aSJens Axboe sgbuf = sg_virt(&sg[i]); 3051c6a0718SPierre Ossman memcpy(sgbuf, dmabuf, sg[i].length); 3061c6a0718SPierre Ossman dmabuf += sg[i].length; 3071c6a0718SPierre Ossman } 3081c6a0718SPierre Ossman } 3091c6a0718SPierre Ossman 3101c6a0718SPierre Ossman /* 3111c6a0718SPierre Ossman * Command handling 3121c6a0718SPierre Ossman */ 3131c6a0718SPierre Ossman 3141c6a0718SPierre Ossman static inline void wbsd_get_short_reply(struct wbsd_host *host, 3151c6a0718SPierre Ossman struct mmc_command *cmd) 3161c6a0718SPierre Ossman { 3171c6a0718SPierre Ossman /* 3181c6a0718SPierre Ossman * Correct response type? 3191c6a0718SPierre Ossman */ 3201c6a0718SPierre Ossman if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { 32117b0429dSPierre Ossman cmd->error = -EILSEQ; 3221c6a0718SPierre Ossman return; 3231c6a0718SPierre Ossman } 3241c6a0718SPierre Ossman 3251c6a0718SPierre Ossman cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; 3261c6a0718SPierre Ossman cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16; 3271c6a0718SPierre Ossman cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8; 3281c6a0718SPierre Ossman cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0; 3291c6a0718SPierre Ossman cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24; 3301c6a0718SPierre Ossman } 3311c6a0718SPierre Ossman 3321c6a0718SPierre Ossman static inline void wbsd_get_long_reply(struct wbsd_host *host, 3331c6a0718SPierre Ossman struct mmc_command *cmd) 3341c6a0718SPierre Ossman { 3351c6a0718SPierre Ossman int i; 3361c6a0718SPierre Ossman 3371c6a0718SPierre Ossman /* 3381c6a0718SPierre Ossman * Correct response type? 3391c6a0718SPierre Ossman */ 3401c6a0718SPierre Ossman if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { 34117b0429dSPierre Ossman cmd->error = -EILSEQ; 3421c6a0718SPierre Ossman return; 3431c6a0718SPierre Ossman } 3441c6a0718SPierre Ossman 3451c6a0718SPierre Ossman for (i = 0; i < 4; i++) { 3461c6a0718SPierre Ossman cmd->resp[i] = 3471c6a0718SPierre Ossman wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; 3481c6a0718SPierre Ossman cmd->resp[i] |= 3491c6a0718SPierre Ossman wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; 3501c6a0718SPierre Ossman cmd->resp[i] |= 3511c6a0718SPierre Ossman wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; 3521c6a0718SPierre Ossman cmd->resp[i] |= 3531c6a0718SPierre Ossman wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; 3541c6a0718SPierre Ossman } 3551c6a0718SPierre Ossman } 3561c6a0718SPierre Ossman 3571c6a0718SPierre Ossman static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd) 3581c6a0718SPierre Ossman { 3591c6a0718SPierre Ossman int i; 3601c6a0718SPierre Ossman u8 status, isr; 3611c6a0718SPierre Ossman 3621c6a0718SPierre Ossman /* 3631c6a0718SPierre Ossman * Clear accumulated ISR. The interrupt routine 3641c6a0718SPierre Ossman * will fill this one with events that occur during 3651c6a0718SPierre Ossman * transfer. 3661c6a0718SPierre Ossman */ 3671c6a0718SPierre Ossman host->isr = 0; 3681c6a0718SPierre Ossman 3691c6a0718SPierre Ossman /* 3701c6a0718SPierre Ossman * Send the command (CRC calculated by host). 3711c6a0718SPierre Ossman */ 3721c6a0718SPierre Ossman outb(cmd->opcode, host->base + WBSD_CMDR); 3731c6a0718SPierre Ossman for (i = 3; i >= 0; i--) 3741c6a0718SPierre Ossman outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); 3751c6a0718SPierre Ossman 37617b0429dSPierre Ossman cmd->error = 0; 3771c6a0718SPierre Ossman 3781c6a0718SPierre Ossman /* 3791c6a0718SPierre Ossman * Wait for the request to complete. 3801c6a0718SPierre Ossman */ 3811c6a0718SPierre Ossman do { 3821c6a0718SPierre Ossman status = wbsd_read_index(host, WBSD_IDX_STATUS); 3831c6a0718SPierre Ossman } while (status & WBSD_CARDTRAFFIC); 3841c6a0718SPierre Ossman 3851c6a0718SPierre Ossman /* 3861c6a0718SPierre Ossman * Do we expect a reply? 3871c6a0718SPierre Ossman */ 3881c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_PRESENT) { 3891c6a0718SPierre Ossman /* 3901c6a0718SPierre Ossman * Read back status. 3911c6a0718SPierre Ossman */ 3921c6a0718SPierre Ossman isr = host->isr; 3931c6a0718SPierre Ossman 3941c6a0718SPierre Ossman /* Card removed? */ 3951c6a0718SPierre Ossman if (isr & WBSD_INT_CARD) 39617b0429dSPierre Ossman cmd->error = -ENOMEDIUM; 3971c6a0718SPierre Ossman /* Timeout? */ 3981c6a0718SPierre Ossman else if (isr & WBSD_INT_TIMEOUT) 39917b0429dSPierre Ossman cmd->error = -ETIMEDOUT; 4001c6a0718SPierre Ossman /* CRC? */ 4011c6a0718SPierre Ossman else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) 40217b0429dSPierre Ossman cmd->error = -EILSEQ; 4031c6a0718SPierre Ossman /* All ok */ 4041c6a0718SPierre Ossman else { 4051c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_136) 4061c6a0718SPierre Ossman wbsd_get_long_reply(host, cmd); 4071c6a0718SPierre Ossman else 4081c6a0718SPierre Ossman wbsd_get_short_reply(host, cmd); 4091c6a0718SPierre Ossman } 4101c6a0718SPierre Ossman } 4111c6a0718SPierre Ossman } 4121c6a0718SPierre Ossman 4131c6a0718SPierre Ossman /* 4141c6a0718SPierre Ossman * Data functions 4151c6a0718SPierre Ossman */ 4161c6a0718SPierre Ossman 4171c6a0718SPierre Ossman static void wbsd_empty_fifo(struct wbsd_host *host) 4181c6a0718SPierre Ossman { 4191c6a0718SPierre Ossman struct mmc_data *data = host->mrq->cmd->data; 4201c6a0718SPierre Ossman char *buffer; 4211c6a0718SPierre Ossman int i, fsr, fifo; 4221c6a0718SPierre Ossman 4231c6a0718SPierre Ossman /* 4241c6a0718SPierre Ossman * Handle excessive data. 4251c6a0718SPierre Ossman */ 4261c6a0718SPierre Ossman if (host->num_sg == 0) 4271c6a0718SPierre Ossman return; 4281c6a0718SPierre Ossman 4291c6a0718SPierre Ossman buffer = wbsd_sg_to_buffer(host) + host->offset; 4301c6a0718SPierre Ossman 4311c6a0718SPierre Ossman /* 4321c6a0718SPierre Ossman * Drain the fifo. This has a tendency to loop longer 4331c6a0718SPierre Ossman * than the FIFO length (usually one block). 4341c6a0718SPierre Ossman */ 4351c6a0718SPierre Ossman while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) { 4361c6a0718SPierre Ossman /* 4371c6a0718SPierre Ossman * The size field in the FSR is broken so we have to 4381c6a0718SPierre Ossman * do some guessing. 4391c6a0718SPierre Ossman */ 4401c6a0718SPierre Ossman if (fsr & WBSD_FIFO_FULL) 4411c6a0718SPierre Ossman fifo = 16; 4421c6a0718SPierre Ossman else if (fsr & WBSD_FIFO_FUTHRE) 4431c6a0718SPierre Ossman fifo = 8; 4441c6a0718SPierre Ossman else 4451c6a0718SPierre Ossman fifo = 1; 4461c6a0718SPierre Ossman 4471c6a0718SPierre Ossman for (i = 0; i < fifo; i++) { 4481c6a0718SPierre Ossman *buffer = inb(host->base + WBSD_DFR); 4491c6a0718SPierre Ossman buffer++; 4501c6a0718SPierre Ossman host->offset++; 4511c6a0718SPierre Ossman host->remain--; 4521c6a0718SPierre Ossman 4531c6a0718SPierre Ossman data->bytes_xfered++; 4541c6a0718SPierre Ossman 4551c6a0718SPierre Ossman /* 4561c6a0718SPierre Ossman * End of scatter list entry? 4571c6a0718SPierre Ossman */ 4581c6a0718SPierre Ossman if (host->remain == 0) { 4591c6a0718SPierre Ossman /* 4601c6a0718SPierre Ossman * Get next entry. Check if last. 4611c6a0718SPierre Ossman */ 4621c6a0718SPierre Ossman if (!wbsd_next_sg(host)) 4631c6a0718SPierre Ossman return; 4641c6a0718SPierre Ossman 4651c6a0718SPierre Ossman buffer = wbsd_sg_to_buffer(host); 4661c6a0718SPierre Ossman } 4671c6a0718SPierre Ossman } 4681c6a0718SPierre Ossman } 4691c6a0718SPierre Ossman 4701c6a0718SPierre Ossman /* 4711c6a0718SPierre Ossman * This is a very dirty hack to solve a 4721c6a0718SPierre Ossman * hardware problem. The chip doesn't trigger 4731c6a0718SPierre Ossman * FIFO threshold interrupts properly. 4741c6a0718SPierre Ossman */ 4751c6a0718SPierre Ossman if ((data->blocks * data->blksz - data->bytes_xfered) < 16) 4761c6a0718SPierre Ossman tasklet_schedule(&host->fifo_tasklet); 4771c6a0718SPierre Ossman } 4781c6a0718SPierre Ossman 4791c6a0718SPierre Ossman static void wbsd_fill_fifo(struct wbsd_host *host) 4801c6a0718SPierre Ossman { 4811c6a0718SPierre Ossman struct mmc_data *data = host->mrq->cmd->data; 4821c6a0718SPierre Ossman char *buffer; 4831c6a0718SPierre Ossman int i, fsr, fifo; 4841c6a0718SPierre Ossman 4851c6a0718SPierre Ossman /* 4861c6a0718SPierre Ossman * Check that we aren't being called after the 48725985edcSLucas De Marchi * entire buffer has been transferred. 4881c6a0718SPierre Ossman */ 4891c6a0718SPierre Ossman if (host->num_sg == 0) 4901c6a0718SPierre Ossman return; 4911c6a0718SPierre Ossman 4921c6a0718SPierre Ossman buffer = wbsd_sg_to_buffer(host) + host->offset; 4931c6a0718SPierre Ossman 4941c6a0718SPierre Ossman /* 4951c6a0718SPierre Ossman * Fill the fifo. This has a tendency to loop longer 4961c6a0718SPierre Ossman * than the FIFO length (usually one block). 4971c6a0718SPierre Ossman */ 4981c6a0718SPierre Ossman while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) { 4991c6a0718SPierre Ossman /* 5001c6a0718SPierre Ossman * The size field in the FSR is broken so we have to 5011c6a0718SPierre Ossman * do some guessing. 5021c6a0718SPierre Ossman */ 5031c6a0718SPierre Ossman if (fsr & WBSD_FIFO_EMPTY) 5041c6a0718SPierre Ossman fifo = 0; 5051c6a0718SPierre Ossman else if (fsr & WBSD_FIFO_EMTHRE) 5061c6a0718SPierre Ossman fifo = 8; 5071c6a0718SPierre Ossman else 5081c6a0718SPierre Ossman fifo = 15; 5091c6a0718SPierre Ossman 5101c6a0718SPierre Ossman for (i = 16; i > fifo; i--) { 5111c6a0718SPierre Ossman outb(*buffer, host->base + WBSD_DFR); 5121c6a0718SPierre Ossman buffer++; 5131c6a0718SPierre Ossman host->offset++; 5141c6a0718SPierre Ossman host->remain--; 5151c6a0718SPierre Ossman 5161c6a0718SPierre Ossman data->bytes_xfered++; 5171c6a0718SPierre Ossman 5181c6a0718SPierre Ossman /* 5191c6a0718SPierre Ossman * End of scatter list entry? 5201c6a0718SPierre Ossman */ 5211c6a0718SPierre Ossman if (host->remain == 0) { 5221c6a0718SPierre Ossman /* 5231c6a0718SPierre Ossman * Get next entry. Check if last. 5241c6a0718SPierre Ossman */ 5251c6a0718SPierre Ossman if (!wbsd_next_sg(host)) 5261c6a0718SPierre Ossman return; 5271c6a0718SPierre Ossman 5281c6a0718SPierre Ossman buffer = wbsd_sg_to_buffer(host); 5291c6a0718SPierre Ossman } 5301c6a0718SPierre Ossman } 5311c6a0718SPierre Ossman } 5321c6a0718SPierre Ossman 5331c6a0718SPierre Ossman /* 5341c6a0718SPierre Ossman * The controller stops sending interrupts for 5351c6a0718SPierre Ossman * 'FIFO empty' under certain conditions. So we 5361c6a0718SPierre Ossman * need to be a bit more pro-active. 5371c6a0718SPierre Ossman */ 5381c6a0718SPierre Ossman tasklet_schedule(&host->fifo_tasklet); 5391c6a0718SPierre Ossman } 5401c6a0718SPierre Ossman 5411c6a0718SPierre Ossman static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) 5421c6a0718SPierre Ossman { 5431c6a0718SPierre Ossman u16 blksize; 5441c6a0718SPierre Ossman u8 setup; 5451c6a0718SPierre Ossman unsigned long dmaflags; 5461c6a0718SPierre Ossman unsigned int size; 5471c6a0718SPierre Ossman 5481c6a0718SPierre Ossman /* 5491c6a0718SPierre Ossman * Calculate size. 5501c6a0718SPierre Ossman */ 5511c6a0718SPierre Ossman size = data->blocks * data->blksz; 5521c6a0718SPierre Ossman 5531c6a0718SPierre Ossman /* 5541c6a0718SPierre Ossman * Check timeout values for overflow. 5551c6a0718SPierre Ossman * (Yes, some cards cause this value to overflow). 5561c6a0718SPierre Ossman */ 5571c6a0718SPierre Ossman if (data->timeout_ns > 127000000) 5581c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_TAAC, 127); 5591c6a0718SPierre Ossman else { 5601c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_TAAC, 5611c6a0718SPierre Ossman data->timeout_ns / 1000000); 5621c6a0718SPierre Ossman } 5631c6a0718SPierre Ossman 5641c6a0718SPierre Ossman if (data->timeout_clks > 255) 5651c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_NSAC, 255); 5661c6a0718SPierre Ossman else 5671c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); 5681c6a0718SPierre Ossman 5691c6a0718SPierre Ossman /* 5701c6a0718SPierre Ossman * Inform the chip of how large blocks will be 5711c6a0718SPierre Ossman * sent. It needs this to determine when to 5721c6a0718SPierre Ossman * calculate CRC. 5731c6a0718SPierre Ossman * 5741c6a0718SPierre Ossman * Space for CRC must be included in the size. 5751c6a0718SPierre Ossman * Two bytes are needed for each data line. 5761c6a0718SPierre Ossman */ 5771c6a0718SPierre Ossman if (host->bus_width == MMC_BUS_WIDTH_1) { 5781c6a0718SPierre Ossman blksize = data->blksz + 2; 5791c6a0718SPierre Ossman 5801c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); 5811c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); 5821c6a0718SPierre Ossman } else if (host->bus_width == MMC_BUS_WIDTH_4) { 5831c6a0718SPierre Ossman blksize = data->blksz + 2 * 4; 5841c6a0718SPierre Ossman 5851c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_PBSMSB, 5861c6a0718SPierre Ossman ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); 5871c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); 5881c6a0718SPierre Ossman } else { 58917b0429dSPierre Ossman data->error = -EINVAL; 5901c6a0718SPierre Ossman return; 5911c6a0718SPierre Ossman } 5921c6a0718SPierre Ossman 5931c6a0718SPierre Ossman /* 5941c6a0718SPierre Ossman * Clear the FIFO. This is needed even for DMA 5951c6a0718SPierre Ossman * transfers since the chip still uses the FIFO 5961c6a0718SPierre Ossman * internally. 5971c6a0718SPierre Ossman */ 5981c6a0718SPierre Ossman setup = wbsd_read_index(host, WBSD_IDX_SETUP); 5991c6a0718SPierre Ossman setup |= WBSD_FIFO_RESET; 6001c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_SETUP, setup); 6011c6a0718SPierre Ossman 6021c6a0718SPierre Ossman /* 6031c6a0718SPierre Ossman * DMA transfer? 6041c6a0718SPierre Ossman */ 6051c6a0718SPierre Ossman if (host->dma >= 0) { 6061c6a0718SPierre Ossman /* 6071c6a0718SPierre Ossman * The buffer for DMA is only 64 kB. 6081c6a0718SPierre Ossman */ 6091c6a0718SPierre Ossman BUG_ON(size > 0x10000); 6101c6a0718SPierre Ossman if (size > 0x10000) { 61117b0429dSPierre Ossman data->error = -EINVAL; 6121c6a0718SPierre Ossman return; 6131c6a0718SPierre Ossman } 6141c6a0718SPierre Ossman 6151c6a0718SPierre Ossman /* 6161c6a0718SPierre Ossman * Transfer data from the SG list to 6171c6a0718SPierre Ossman * the DMA buffer. 6181c6a0718SPierre Ossman */ 6191c6a0718SPierre Ossman if (data->flags & MMC_DATA_WRITE) 6201c6a0718SPierre Ossman wbsd_sg_to_dma(host, data); 6211c6a0718SPierre Ossman 6221c6a0718SPierre Ossman /* 6231c6a0718SPierre Ossman * Initialise the ISA DMA controller. 6241c6a0718SPierre Ossman */ 6251c6a0718SPierre Ossman dmaflags = claim_dma_lock(); 6261c6a0718SPierre Ossman disable_dma(host->dma); 6271c6a0718SPierre Ossman clear_dma_ff(host->dma); 6281c6a0718SPierre Ossman if (data->flags & MMC_DATA_READ) 6291c6a0718SPierre Ossman set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); 6301c6a0718SPierre Ossman else 6311c6a0718SPierre Ossman set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); 6321c6a0718SPierre Ossman set_dma_addr(host->dma, host->dma_addr); 6331c6a0718SPierre Ossman set_dma_count(host->dma, size); 6341c6a0718SPierre Ossman 6351c6a0718SPierre Ossman enable_dma(host->dma); 6361c6a0718SPierre Ossman release_dma_lock(dmaflags); 6371c6a0718SPierre Ossman 6381c6a0718SPierre Ossman /* 6391c6a0718SPierre Ossman * Enable DMA on the host. 6401c6a0718SPierre Ossman */ 6411c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE); 6421c6a0718SPierre Ossman } else { 6431c6a0718SPierre Ossman /* 6441c6a0718SPierre Ossman * This flag is used to keep printk 6451c6a0718SPierre Ossman * output to a minimum. 6461c6a0718SPierre Ossman */ 6471c6a0718SPierre Ossman host->firsterr = 1; 6481c6a0718SPierre Ossman 6491c6a0718SPierre Ossman /* 6501c6a0718SPierre Ossman * Initialise the SG list. 6511c6a0718SPierre Ossman */ 6521c6a0718SPierre Ossman wbsd_init_sg(host, data); 6531c6a0718SPierre Ossman 6541c6a0718SPierre Ossman /* 6551c6a0718SPierre Ossman * Turn off DMA. 6561c6a0718SPierre Ossman */ 6571c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_DMA, 0); 6581c6a0718SPierre Ossman 6591c6a0718SPierre Ossman /* 6601c6a0718SPierre Ossman * Set up FIFO threshold levels (and fill 6611c6a0718SPierre Ossman * buffer if doing a write). 6621c6a0718SPierre Ossman */ 6631c6a0718SPierre Ossman if (data->flags & MMC_DATA_READ) { 6641c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_FIFOEN, 6651c6a0718SPierre Ossman WBSD_FIFOEN_FULL | 8); 6661c6a0718SPierre Ossman } else { 6671c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_FIFOEN, 6681c6a0718SPierre Ossman WBSD_FIFOEN_EMPTY | 8); 6691c6a0718SPierre Ossman wbsd_fill_fifo(host); 6701c6a0718SPierre Ossman } 6711c6a0718SPierre Ossman } 6721c6a0718SPierre Ossman 67317b0429dSPierre Ossman data->error = 0; 6741c6a0718SPierre Ossman } 6751c6a0718SPierre Ossman 6761c6a0718SPierre Ossman static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) 6771c6a0718SPierre Ossman { 6781c6a0718SPierre Ossman unsigned long dmaflags; 6791c6a0718SPierre Ossman int count; 6801c6a0718SPierre Ossman u8 status; 6811c6a0718SPierre Ossman 6821c6a0718SPierre Ossman WARN_ON(host->mrq == NULL); 6831c6a0718SPierre Ossman 6841c6a0718SPierre Ossman /* 6851c6a0718SPierre Ossman * Send a stop command if needed. 6861c6a0718SPierre Ossman */ 6871c6a0718SPierre Ossman if (data->stop) 6881c6a0718SPierre Ossman wbsd_send_command(host, data->stop); 6891c6a0718SPierre Ossman 6901c6a0718SPierre Ossman /* 6911c6a0718SPierre Ossman * Wait for the controller to leave data 6921c6a0718SPierre Ossman * transfer state. 6931c6a0718SPierre Ossman */ 6941c6a0718SPierre Ossman do { 6951c6a0718SPierre Ossman status = wbsd_read_index(host, WBSD_IDX_STATUS); 6961c6a0718SPierre Ossman } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); 6971c6a0718SPierre Ossman 6981c6a0718SPierre Ossman /* 6991c6a0718SPierre Ossman * DMA transfer? 7001c6a0718SPierre Ossman */ 7011c6a0718SPierre Ossman if (host->dma >= 0) { 7021c6a0718SPierre Ossman /* 7031c6a0718SPierre Ossman * Disable DMA on the host. 7041c6a0718SPierre Ossman */ 7051c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_DMA, 0); 7061c6a0718SPierre Ossman 7071c6a0718SPierre Ossman /* 7081c6a0718SPierre Ossman * Turn of ISA DMA controller. 7091c6a0718SPierre Ossman */ 7101c6a0718SPierre Ossman dmaflags = claim_dma_lock(); 7111c6a0718SPierre Ossman disable_dma(host->dma); 7121c6a0718SPierre Ossman clear_dma_ff(host->dma); 7131c6a0718SPierre Ossman count = get_dma_residue(host->dma); 7141c6a0718SPierre Ossman release_dma_lock(dmaflags); 7151c6a0718SPierre Ossman 7161c6a0718SPierre Ossman data->bytes_xfered = host->mrq->data->blocks * 7171c6a0718SPierre Ossman host->mrq->data->blksz - count; 7181c6a0718SPierre Ossman data->bytes_xfered -= data->bytes_xfered % data->blksz; 7191c6a0718SPierre Ossman 7201c6a0718SPierre Ossman /* 7211c6a0718SPierre Ossman * Any leftover data? 7221c6a0718SPierre Ossman */ 7231c6a0718SPierre Ossman if (count) { 724a3c76eb9SGirish K S pr_err("%s: Incomplete DMA transfer. " 7251c6a0718SPierre Ossman "%d bytes left.\n", 7261c6a0718SPierre Ossman mmc_hostname(host->mmc), count); 7271c6a0718SPierre Ossman 72817b0429dSPierre Ossman if (!data->error) 72917b0429dSPierre Ossman data->error = -EIO; 7301c6a0718SPierre Ossman } else { 7311c6a0718SPierre Ossman /* 7321c6a0718SPierre Ossman * Transfer data from DMA buffer to 7331c6a0718SPierre Ossman * SG list. 7341c6a0718SPierre Ossman */ 7351c6a0718SPierre Ossman if (data->flags & MMC_DATA_READ) 7361c6a0718SPierre Ossman wbsd_dma_to_sg(host, data); 7371c6a0718SPierre Ossman } 7381c6a0718SPierre Ossman 73917b0429dSPierre Ossman if (data->error) { 7401c6a0718SPierre Ossman if (data->bytes_xfered) 7411c6a0718SPierre Ossman data->bytes_xfered -= data->blksz; 7421c6a0718SPierre Ossman } 7431c6a0718SPierre Ossman } 7441c6a0718SPierre Ossman 7451c6a0718SPierre Ossman wbsd_request_end(host, host->mrq); 7461c6a0718SPierre Ossman } 7471c6a0718SPierre Ossman 7481c6a0718SPierre Ossman /*****************************************************************************\ 7491c6a0718SPierre Ossman * * 7501c6a0718SPierre Ossman * MMC layer callbacks * 7511c6a0718SPierre Ossman * * 7521c6a0718SPierre Ossman \*****************************************************************************/ 7531c6a0718SPierre Ossman 7541c6a0718SPierre Ossman static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) 7551c6a0718SPierre Ossman { 7561c6a0718SPierre Ossman struct wbsd_host *host = mmc_priv(mmc); 7571c6a0718SPierre Ossman struct mmc_command *cmd; 7581c6a0718SPierre Ossman 7591c6a0718SPierre Ossman /* 7601c6a0718SPierre Ossman * Disable tasklets to avoid a deadlock. 7611c6a0718SPierre Ossman */ 7621c6a0718SPierre Ossman spin_lock_bh(&host->lock); 7631c6a0718SPierre Ossman 7641c6a0718SPierre Ossman BUG_ON(host->mrq != NULL); 7651c6a0718SPierre Ossman 7661c6a0718SPierre Ossman cmd = mrq->cmd; 7671c6a0718SPierre Ossman 7681c6a0718SPierre Ossman host->mrq = mrq; 7691c6a0718SPierre Ossman 7701c6a0718SPierre Ossman /* 77117b0429dSPierre Ossman * Check that there is actually a card in the slot. 7721c6a0718SPierre Ossman */ 7731c6a0718SPierre Ossman if (!(host->flags & WBSD_FCARD_PRESENT)) { 77417b0429dSPierre Ossman cmd->error = -ENOMEDIUM; 7751c6a0718SPierre Ossman goto done; 7761c6a0718SPierre Ossman } 7771c6a0718SPierre Ossman 7781c6a0718SPierre Ossman if (cmd->data) { 7791c6a0718SPierre Ossman /* 7801c6a0718SPierre Ossman * The hardware is so delightfully stupid that it has a list 7811c6a0718SPierre Ossman * of "data" commands. If a command isn't on this list, it'll 7821c6a0718SPierre Ossman * just go back to the idle state and won't send any data 7831c6a0718SPierre Ossman * interrupts. 7841c6a0718SPierre Ossman */ 7851c6a0718SPierre Ossman switch (cmd->opcode) { 7861c6a0718SPierre Ossman case 11: 7871c6a0718SPierre Ossman case 17: 7881c6a0718SPierre Ossman case 18: 7891c6a0718SPierre Ossman case 20: 7901c6a0718SPierre Ossman case 24: 7911c6a0718SPierre Ossman case 25: 7921c6a0718SPierre Ossman case 26: 7931c6a0718SPierre Ossman case 27: 7941c6a0718SPierre Ossman case 30: 7951c6a0718SPierre Ossman case 42: 7961c6a0718SPierre Ossman case 56: 7971c6a0718SPierre Ossman break; 7981c6a0718SPierre Ossman 7991c6a0718SPierre Ossman /* ACMDs. We don't keep track of state, so we just treat them 8001c6a0718SPierre Ossman * like any other command. */ 8011c6a0718SPierre Ossman case 51: 8021c6a0718SPierre Ossman break; 8031c6a0718SPierre Ossman 8041c6a0718SPierre Ossman default: 8051c6a0718SPierre Ossman #ifdef CONFIG_MMC_DEBUG 8066606110dSJoe Perches pr_warn("%s: Data command %d is not supported by this controller\n", 8071c6a0718SPierre Ossman mmc_hostname(host->mmc), cmd->opcode); 8081c6a0718SPierre Ossman #endif 80917b0429dSPierre Ossman cmd->error = -EINVAL; 8101c6a0718SPierre Ossman 8111c6a0718SPierre Ossman goto done; 81217a90539SJavier Martinez Canillas } 813b2670b1cSPierre Ossman } 8141c6a0718SPierre Ossman 8151c6a0718SPierre Ossman /* 816b2670b1cSPierre Ossman * Does the request include data? 817b2670b1cSPierre Ossman */ 818b2670b1cSPierre Ossman if (cmd->data) { 819b2670b1cSPierre Ossman wbsd_prepare_data(host, cmd->data); 820b2670b1cSPierre Ossman 82117b0429dSPierre Ossman if (cmd->data->error) 822b2670b1cSPierre Ossman goto done; 823b2670b1cSPierre Ossman } 824b2670b1cSPierre Ossman 825b2670b1cSPierre Ossman wbsd_send_command(host, cmd); 826b2670b1cSPierre Ossman 827b2670b1cSPierre Ossman /* 828b2670b1cSPierre Ossman * If this is a data transfer the request 829b2670b1cSPierre Ossman * will be finished after the data has 83025985edcSLucas De Marchi * transferred. 831b2670b1cSPierre Ossman */ 83217b0429dSPierre Ossman if (cmd->data && !cmd->error) { 833b2670b1cSPierre Ossman /* 8341c6a0718SPierre Ossman * Dirty fix for hardware bug. 8351c6a0718SPierre Ossman */ 8361c6a0718SPierre Ossman if (host->dma == -1) 8371c6a0718SPierre Ossman tasklet_schedule(&host->fifo_tasklet); 8381c6a0718SPierre Ossman 8391c6a0718SPierre Ossman spin_unlock_bh(&host->lock); 8401c6a0718SPierre Ossman 8411c6a0718SPierre Ossman return; 8421c6a0718SPierre Ossman } 8431c6a0718SPierre Ossman 8441c6a0718SPierre Ossman done: 8451c6a0718SPierre Ossman wbsd_request_end(host, mrq); 8461c6a0718SPierre Ossman 8471c6a0718SPierre Ossman spin_unlock_bh(&host->lock); 8481c6a0718SPierre Ossman } 8491c6a0718SPierre Ossman 8501c6a0718SPierre Ossman static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 8511c6a0718SPierre Ossman { 8521c6a0718SPierre Ossman struct wbsd_host *host = mmc_priv(mmc); 8531c6a0718SPierre Ossman u8 clk, setup, pwr; 8541c6a0718SPierre Ossman 8551c6a0718SPierre Ossman spin_lock_bh(&host->lock); 8561c6a0718SPierre Ossman 8571c6a0718SPierre Ossman /* 8581c6a0718SPierre Ossman * Reset the chip on each power off. 8591c6a0718SPierre Ossman * Should clear out any weird states. 8601c6a0718SPierre Ossman */ 8611c6a0718SPierre Ossman if (ios->power_mode == MMC_POWER_OFF) 8621c6a0718SPierre Ossman wbsd_init_device(host); 8631c6a0718SPierre Ossman 8641c6a0718SPierre Ossman if (ios->clock >= 24000000) 8651c6a0718SPierre Ossman clk = WBSD_CLK_24M; 8661c6a0718SPierre Ossman else if (ios->clock >= 16000000) 8671c6a0718SPierre Ossman clk = WBSD_CLK_16M; 8681c6a0718SPierre Ossman else if (ios->clock >= 12000000) 8691c6a0718SPierre Ossman clk = WBSD_CLK_12M; 8701c6a0718SPierre Ossman else 8711c6a0718SPierre Ossman clk = WBSD_CLK_375K; 8721c6a0718SPierre Ossman 8731c6a0718SPierre Ossman /* 8741c6a0718SPierre Ossman * Only write to the clock register when 8751c6a0718SPierre Ossman * there is an actual change. 8761c6a0718SPierre Ossman */ 8771c6a0718SPierre Ossman if (clk != host->clk) { 8781c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_CLK, clk); 8791c6a0718SPierre Ossman host->clk = clk; 8801c6a0718SPierre Ossman } 8811c6a0718SPierre Ossman 8821c6a0718SPierre Ossman /* 8831c6a0718SPierre Ossman * Power up card. 8841c6a0718SPierre Ossman */ 8851c6a0718SPierre Ossman if (ios->power_mode != MMC_POWER_OFF) { 8861c6a0718SPierre Ossman pwr = inb(host->base + WBSD_CSR); 8871c6a0718SPierre Ossman pwr &= ~WBSD_POWER_N; 8881c6a0718SPierre Ossman outb(pwr, host->base + WBSD_CSR); 8891c6a0718SPierre Ossman } 8901c6a0718SPierre Ossman 8911c6a0718SPierre Ossman /* 8921c6a0718SPierre Ossman * MMC cards need to have pin 1 high during init. 8931c6a0718SPierre Ossman * It wreaks havoc with the card detection though so 8941c6a0718SPierre Ossman * that needs to be disabled. 8951c6a0718SPierre Ossman */ 8961c6a0718SPierre Ossman setup = wbsd_read_index(host, WBSD_IDX_SETUP); 8971c6a0718SPierre Ossman if (ios->chip_select == MMC_CS_HIGH) { 8981c6a0718SPierre Ossman BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1); 8991c6a0718SPierre Ossman setup |= WBSD_DAT3_H; 9001c6a0718SPierre Ossman host->flags |= WBSD_FIGNORE_DETECT; 9011c6a0718SPierre Ossman } else { 9021c6a0718SPierre Ossman if (setup & WBSD_DAT3_H) { 9031c6a0718SPierre Ossman setup &= ~WBSD_DAT3_H; 9041c6a0718SPierre Ossman 9051c6a0718SPierre Ossman /* 90625985edcSLucas De Marchi * We cannot resume card detection immediately 9071c6a0718SPierre Ossman * because of capacitance and delays in the chip. 9081c6a0718SPierre Ossman */ 9091c6a0718SPierre Ossman mod_timer(&host->ignore_timer, jiffies + HZ / 100); 9101c6a0718SPierre Ossman } 9111c6a0718SPierre Ossman } 9121c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_SETUP, setup); 9131c6a0718SPierre Ossman 9141c6a0718SPierre Ossman /* 9151c6a0718SPierre Ossman * Store bus width for later. Will be used when 9161c6a0718SPierre Ossman * setting up the data transfer. 9171c6a0718SPierre Ossman */ 9181c6a0718SPierre Ossman host->bus_width = ios->bus_width; 9191c6a0718SPierre Ossman 9201c6a0718SPierre Ossman spin_unlock_bh(&host->lock); 9211c6a0718SPierre Ossman } 9221c6a0718SPierre Ossman 9231c6a0718SPierre Ossman static int wbsd_get_ro(struct mmc_host *mmc) 9241c6a0718SPierre Ossman { 9251c6a0718SPierre Ossman struct wbsd_host *host = mmc_priv(mmc); 9261c6a0718SPierre Ossman u8 csr; 9271c6a0718SPierre Ossman 9281c6a0718SPierre Ossman spin_lock_bh(&host->lock); 9291c6a0718SPierre Ossman 9301c6a0718SPierre Ossman csr = inb(host->base + WBSD_CSR); 9311c6a0718SPierre Ossman csr |= WBSD_MSLED; 9321c6a0718SPierre Ossman outb(csr, host->base + WBSD_CSR); 9331c6a0718SPierre Ossman 9341c6a0718SPierre Ossman mdelay(1); 9351c6a0718SPierre Ossman 9361c6a0718SPierre Ossman csr = inb(host->base + WBSD_CSR); 9371c6a0718SPierre Ossman csr &= ~WBSD_MSLED; 9381c6a0718SPierre Ossman outb(csr, host->base + WBSD_CSR); 9391c6a0718SPierre Ossman 9401c6a0718SPierre Ossman spin_unlock_bh(&host->lock); 9411c6a0718SPierre Ossman 94208f80bb5SAnton Vorontsov return !!(csr & WBSD_WRPT); 9431c6a0718SPierre Ossman } 9441c6a0718SPierre Ossman 9451c6a0718SPierre Ossman static const struct mmc_host_ops wbsd_ops = { 9461c6a0718SPierre Ossman .request = wbsd_request, 9471c6a0718SPierre Ossman .set_ios = wbsd_set_ios, 9481c6a0718SPierre Ossman .get_ro = wbsd_get_ro, 9491c6a0718SPierre Ossman }; 9501c6a0718SPierre Ossman 9511c6a0718SPierre Ossman /*****************************************************************************\ 9521c6a0718SPierre Ossman * * 9531c6a0718SPierre Ossman * Interrupt handling * 9541c6a0718SPierre Ossman * * 9551c6a0718SPierre Ossman \*****************************************************************************/ 9561c6a0718SPierre Ossman 9571c6a0718SPierre Ossman /* 9581c6a0718SPierre Ossman * Helper function to reset detection ignore 9591c6a0718SPierre Ossman */ 9601c6a0718SPierre Ossman 9611c6a0718SPierre Ossman static void wbsd_reset_ignore(unsigned long data) 9621c6a0718SPierre Ossman { 9631c6a0718SPierre Ossman struct wbsd_host *host = (struct wbsd_host *)data; 9641c6a0718SPierre Ossman 9651c6a0718SPierre Ossman BUG_ON(host == NULL); 9661c6a0718SPierre Ossman 9671c6a0718SPierre Ossman DBG("Resetting card detection ignore\n"); 9681c6a0718SPierre Ossman 9691c6a0718SPierre Ossman spin_lock_bh(&host->lock); 9701c6a0718SPierre Ossman 9711c6a0718SPierre Ossman host->flags &= ~WBSD_FIGNORE_DETECT; 9721c6a0718SPierre Ossman 9731c6a0718SPierre Ossman /* 9741c6a0718SPierre Ossman * Card status might have changed during the 9751c6a0718SPierre Ossman * blackout. 9761c6a0718SPierre Ossman */ 9771c6a0718SPierre Ossman tasklet_schedule(&host->card_tasklet); 9781c6a0718SPierre Ossman 9791c6a0718SPierre Ossman spin_unlock_bh(&host->lock); 9801c6a0718SPierre Ossman } 9811c6a0718SPierre Ossman 9821c6a0718SPierre Ossman /* 9831c6a0718SPierre Ossman * Tasklets 9841c6a0718SPierre Ossman */ 9851c6a0718SPierre Ossman 9861c6a0718SPierre Ossman static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) 9871c6a0718SPierre Ossman { 9881c6a0718SPierre Ossman WARN_ON(!host->mrq); 9891c6a0718SPierre Ossman if (!host->mrq) 9901c6a0718SPierre Ossman return NULL; 9911c6a0718SPierre Ossman 9921c6a0718SPierre Ossman WARN_ON(!host->mrq->cmd); 9931c6a0718SPierre Ossman if (!host->mrq->cmd) 9941c6a0718SPierre Ossman return NULL; 9951c6a0718SPierre Ossman 9961c6a0718SPierre Ossman WARN_ON(!host->mrq->cmd->data); 9971c6a0718SPierre Ossman if (!host->mrq->cmd->data) 9981c6a0718SPierre Ossman return NULL; 9991c6a0718SPierre Ossman 10001c6a0718SPierre Ossman return host->mrq->cmd->data; 10011c6a0718SPierre Ossman } 10021c6a0718SPierre Ossman 10031c6a0718SPierre Ossman static void wbsd_tasklet_card(unsigned long param) 10041c6a0718SPierre Ossman { 10051c6a0718SPierre Ossman struct wbsd_host *host = (struct wbsd_host *)param; 10061c6a0718SPierre Ossman u8 csr; 10071c6a0718SPierre Ossman int delay = -1; 10081c6a0718SPierre Ossman 10091c6a0718SPierre Ossman spin_lock(&host->lock); 10101c6a0718SPierre Ossman 10111c6a0718SPierre Ossman if (host->flags & WBSD_FIGNORE_DETECT) { 10121c6a0718SPierre Ossman spin_unlock(&host->lock); 10131c6a0718SPierre Ossman return; 10141c6a0718SPierre Ossman } 10151c6a0718SPierre Ossman 10161c6a0718SPierre Ossman csr = inb(host->base + WBSD_CSR); 10171c6a0718SPierre Ossman WARN_ON(csr == 0xff); 10181c6a0718SPierre Ossman 10191c6a0718SPierre Ossman if (csr & WBSD_CARDPRESENT) { 10201c6a0718SPierre Ossman if (!(host->flags & WBSD_FCARD_PRESENT)) { 10211c6a0718SPierre Ossman DBG("Card inserted\n"); 10221c6a0718SPierre Ossman host->flags |= WBSD_FCARD_PRESENT; 10231c6a0718SPierre Ossman 10241c6a0718SPierre Ossman delay = 500; 10251c6a0718SPierre Ossman } 10261c6a0718SPierre Ossman } else if (host->flags & WBSD_FCARD_PRESENT) { 10271c6a0718SPierre Ossman DBG("Card removed\n"); 10281c6a0718SPierre Ossman host->flags &= ~WBSD_FCARD_PRESENT; 10291c6a0718SPierre Ossman 10301c6a0718SPierre Ossman if (host->mrq) { 1031a3c76eb9SGirish K S pr_err("%s: Card removed during transfer!\n", 10321c6a0718SPierre Ossman mmc_hostname(host->mmc)); 10331c6a0718SPierre Ossman wbsd_reset(host); 10341c6a0718SPierre Ossman 103517b0429dSPierre Ossman host->mrq->cmd->error = -ENOMEDIUM; 10361c6a0718SPierre Ossman tasklet_schedule(&host->finish_tasklet); 10371c6a0718SPierre Ossman } 10381c6a0718SPierre Ossman 10391c6a0718SPierre Ossman delay = 0; 10401c6a0718SPierre Ossman } 10411c6a0718SPierre Ossman 10421c6a0718SPierre Ossman /* 10431c6a0718SPierre Ossman * Unlock first since we might get a call back. 10441c6a0718SPierre Ossman */ 10451c6a0718SPierre Ossman 10461c6a0718SPierre Ossman spin_unlock(&host->lock); 10471c6a0718SPierre Ossman 10481c6a0718SPierre Ossman if (delay != -1) 10491c6a0718SPierre Ossman mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); 10501c6a0718SPierre Ossman } 10511c6a0718SPierre Ossman 10521c6a0718SPierre Ossman static void wbsd_tasklet_fifo(unsigned long param) 10531c6a0718SPierre Ossman { 10541c6a0718SPierre Ossman struct wbsd_host *host = (struct wbsd_host *)param; 10551c6a0718SPierre Ossman struct mmc_data *data; 10561c6a0718SPierre Ossman 10571c6a0718SPierre Ossman spin_lock(&host->lock); 10581c6a0718SPierre Ossman 10591c6a0718SPierre Ossman if (!host->mrq) 10601c6a0718SPierre Ossman goto end; 10611c6a0718SPierre Ossman 10621c6a0718SPierre Ossman data = wbsd_get_data(host); 10631c6a0718SPierre Ossman if (!data) 10641c6a0718SPierre Ossman goto end; 10651c6a0718SPierre Ossman 10661c6a0718SPierre Ossman if (data->flags & MMC_DATA_WRITE) 10671c6a0718SPierre Ossman wbsd_fill_fifo(host); 10681c6a0718SPierre Ossman else 10691c6a0718SPierre Ossman wbsd_empty_fifo(host); 10701c6a0718SPierre Ossman 10711c6a0718SPierre Ossman /* 10721c6a0718SPierre Ossman * Done? 10731c6a0718SPierre Ossman */ 10741c6a0718SPierre Ossman if (host->num_sg == 0) { 10751c6a0718SPierre Ossman wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); 10761c6a0718SPierre Ossman tasklet_schedule(&host->finish_tasklet); 10771c6a0718SPierre Ossman } 10781c6a0718SPierre Ossman 10791c6a0718SPierre Ossman end: 10801c6a0718SPierre Ossman spin_unlock(&host->lock); 10811c6a0718SPierre Ossman } 10821c6a0718SPierre Ossman 10831c6a0718SPierre Ossman static void wbsd_tasklet_crc(unsigned long param) 10841c6a0718SPierre Ossman { 10851c6a0718SPierre Ossman struct wbsd_host *host = (struct wbsd_host *)param; 10861c6a0718SPierre Ossman struct mmc_data *data; 10871c6a0718SPierre Ossman 10881c6a0718SPierre Ossman spin_lock(&host->lock); 10891c6a0718SPierre Ossman 10901c6a0718SPierre Ossman if (!host->mrq) 10911c6a0718SPierre Ossman goto end; 10921c6a0718SPierre Ossman 10931c6a0718SPierre Ossman data = wbsd_get_data(host); 10941c6a0718SPierre Ossman if (!data) 10951c6a0718SPierre Ossman goto end; 10961c6a0718SPierre Ossman 10971c6a0718SPierre Ossman DBGF("CRC error\n"); 10981c6a0718SPierre Ossman 109917b0429dSPierre Ossman data->error = -EILSEQ; 11001c6a0718SPierre Ossman 11011c6a0718SPierre Ossman tasklet_schedule(&host->finish_tasklet); 11021c6a0718SPierre Ossman 11031c6a0718SPierre Ossman end: 11041c6a0718SPierre Ossman spin_unlock(&host->lock); 11051c6a0718SPierre Ossman } 11061c6a0718SPierre Ossman 11071c6a0718SPierre Ossman static void wbsd_tasklet_timeout(unsigned long param) 11081c6a0718SPierre Ossman { 11091c6a0718SPierre Ossman struct wbsd_host *host = (struct wbsd_host *)param; 11101c6a0718SPierre Ossman struct mmc_data *data; 11111c6a0718SPierre Ossman 11121c6a0718SPierre Ossman spin_lock(&host->lock); 11131c6a0718SPierre Ossman 11141c6a0718SPierre Ossman if (!host->mrq) 11151c6a0718SPierre Ossman goto end; 11161c6a0718SPierre Ossman 11171c6a0718SPierre Ossman data = wbsd_get_data(host); 11181c6a0718SPierre Ossman if (!data) 11191c6a0718SPierre Ossman goto end; 11201c6a0718SPierre Ossman 11211c6a0718SPierre Ossman DBGF("Timeout\n"); 11221c6a0718SPierre Ossman 112317b0429dSPierre Ossman data->error = -ETIMEDOUT; 11241c6a0718SPierre Ossman 11251c6a0718SPierre Ossman tasklet_schedule(&host->finish_tasklet); 11261c6a0718SPierre Ossman 11271c6a0718SPierre Ossman end: 11281c6a0718SPierre Ossman spin_unlock(&host->lock); 11291c6a0718SPierre Ossman } 11301c6a0718SPierre Ossman 11311c6a0718SPierre Ossman static void wbsd_tasklet_finish(unsigned long param) 11321c6a0718SPierre Ossman { 11331c6a0718SPierre Ossman struct wbsd_host *host = (struct wbsd_host *)param; 11341c6a0718SPierre Ossman struct mmc_data *data; 11351c6a0718SPierre Ossman 11361c6a0718SPierre Ossman spin_lock(&host->lock); 11371c6a0718SPierre Ossman 11381c6a0718SPierre Ossman WARN_ON(!host->mrq); 11391c6a0718SPierre Ossman if (!host->mrq) 11401c6a0718SPierre Ossman goto end; 11411c6a0718SPierre Ossman 11421c6a0718SPierre Ossman data = wbsd_get_data(host); 11431c6a0718SPierre Ossman if (!data) 11441c6a0718SPierre Ossman goto end; 11451c6a0718SPierre Ossman 11461c6a0718SPierre Ossman wbsd_finish_data(host, data); 11471c6a0718SPierre Ossman 11481c6a0718SPierre Ossman end: 11491c6a0718SPierre Ossman spin_unlock(&host->lock); 11501c6a0718SPierre Ossman } 11511c6a0718SPierre Ossman 11521c6a0718SPierre Ossman /* 11531c6a0718SPierre Ossman * Interrupt handling 11541c6a0718SPierre Ossman */ 11551c6a0718SPierre Ossman 11561c6a0718SPierre Ossman static irqreturn_t wbsd_irq(int irq, void *dev_id) 11571c6a0718SPierre Ossman { 11581c6a0718SPierre Ossman struct wbsd_host *host = dev_id; 11591c6a0718SPierre Ossman int isr; 11601c6a0718SPierre Ossman 11611c6a0718SPierre Ossman isr = inb(host->base + WBSD_ISR); 11621c6a0718SPierre Ossman 11631c6a0718SPierre Ossman /* 11641c6a0718SPierre Ossman * Was it actually our hardware that caused the interrupt? 11651c6a0718SPierre Ossman */ 11661c6a0718SPierre Ossman if (isr == 0xff || isr == 0x00) 11671c6a0718SPierre Ossman return IRQ_NONE; 11681c6a0718SPierre Ossman 11691c6a0718SPierre Ossman host->isr |= isr; 11701c6a0718SPierre Ossman 11711c6a0718SPierre Ossman /* 11721c6a0718SPierre Ossman * Schedule tasklets as needed. 11731c6a0718SPierre Ossman */ 11741c6a0718SPierre Ossman if (isr & WBSD_INT_CARD) 11751c6a0718SPierre Ossman tasklet_schedule(&host->card_tasklet); 11761c6a0718SPierre Ossman if (isr & WBSD_INT_FIFO_THRE) 11771c6a0718SPierre Ossman tasklet_schedule(&host->fifo_tasklet); 11781c6a0718SPierre Ossman if (isr & WBSD_INT_CRC) 11791c6a0718SPierre Ossman tasklet_hi_schedule(&host->crc_tasklet); 11801c6a0718SPierre Ossman if (isr & WBSD_INT_TIMEOUT) 11811c6a0718SPierre Ossman tasklet_hi_schedule(&host->timeout_tasklet); 11821c6a0718SPierre Ossman if (isr & WBSD_INT_TC) 11831c6a0718SPierre Ossman tasklet_schedule(&host->finish_tasklet); 11841c6a0718SPierre Ossman 11851c6a0718SPierre Ossman return IRQ_HANDLED; 11861c6a0718SPierre Ossman } 11871c6a0718SPierre Ossman 11881c6a0718SPierre Ossman /*****************************************************************************\ 11891c6a0718SPierre Ossman * * 11901c6a0718SPierre Ossman * Device initialisation and shutdown * 11911c6a0718SPierre Ossman * * 11921c6a0718SPierre Ossman \*****************************************************************************/ 11931c6a0718SPierre Ossman 11941c6a0718SPierre Ossman /* 11951c6a0718SPierre Ossman * Allocate/free MMC structure. 11961c6a0718SPierre Ossman */ 11971c6a0718SPierre Ossman 1198c3be1efdSBill Pemberton static int wbsd_alloc_mmc(struct device *dev) 11991c6a0718SPierre Ossman { 12001c6a0718SPierre Ossman struct mmc_host *mmc; 12011c6a0718SPierre Ossman struct wbsd_host *host; 12021c6a0718SPierre Ossman 12031c6a0718SPierre Ossman /* 12041c6a0718SPierre Ossman * Allocate MMC structure. 12051c6a0718SPierre Ossman */ 12061c6a0718SPierre Ossman mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); 12071c6a0718SPierre Ossman if (!mmc) 12081c6a0718SPierre Ossman return -ENOMEM; 12091c6a0718SPierre Ossman 12101c6a0718SPierre Ossman host = mmc_priv(mmc); 12111c6a0718SPierre Ossman host->mmc = mmc; 12121c6a0718SPierre Ossman 12131c6a0718SPierre Ossman host->dma = -1; 12141c6a0718SPierre Ossman 12151c6a0718SPierre Ossman /* 12161c6a0718SPierre Ossman * Set host parameters. 12171c6a0718SPierre Ossman */ 12181c6a0718SPierre Ossman mmc->ops = &wbsd_ops; 12191c6a0718SPierre Ossman mmc->f_min = 375000; 12201c6a0718SPierre Ossman mmc->f_max = 24000000; 12211c6a0718SPierre Ossman mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 122223af6039SPierre Ossman mmc->caps = MMC_CAP_4_BIT_DATA; 12231c6a0718SPierre Ossman 12241c6a0718SPierre Ossman spin_lock_init(&host->lock); 12251c6a0718SPierre Ossman 12261c6a0718SPierre Ossman /* 12271c6a0718SPierre Ossman * Set up timers 12281c6a0718SPierre Ossman */ 12291c6a0718SPierre Ossman init_timer(&host->ignore_timer); 12301c6a0718SPierre Ossman host->ignore_timer.data = (unsigned long)host; 12311c6a0718SPierre Ossman host->ignore_timer.function = wbsd_reset_ignore; 12321c6a0718SPierre Ossman 12331c6a0718SPierre Ossman /* 12341c6a0718SPierre Ossman * Maximum number of segments. Worst case is one sector per segment 12351c6a0718SPierre Ossman * so this will be 64kB/512. 12361c6a0718SPierre Ossman */ 1237a36274e0SMartin K. Petersen mmc->max_segs = 128; 12381c6a0718SPierre Ossman 12391c6a0718SPierre Ossman /* 12401c6a0718SPierre Ossman * Maximum request size. Also limited by 64KiB buffer. 12411c6a0718SPierre Ossman */ 12421c6a0718SPierre Ossman mmc->max_req_size = 65536; 12431c6a0718SPierre Ossman 12441c6a0718SPierre Ossman /* 12451c6a0718SPierre Ossman * Maximum segment size. Could be one segment with the maximum number 12461c6a0718SPierre Ossman * of bytes. 12471c6a0718SPierre Ossman */ 12481c6a0718SPierre Ossman mmc->max_seg_size = mmc->max_req_size; 12491c6a0718SPierre Ossman 12501c6a0718SPierre Ossman /* 12511c6a0718SPierre Ossman * Maximum block size. We have 12 bits (= 4095) but have to subtract 12521c6a0718SPierre Ossman * space for CRC. So the maximum is 4095 - 4*2 = 4087. 12531c6a0718SPierre Ossman */ 12541c6a0718SPierre Ossman mmc->max_blk_size = 4087; 12551c6a0718SPierre Ossman 12561c6a0718SPierre Ossman /* 12571c6a0718SPierre Ossman * Maximum block count. There is no real limit so the maximum 12581c6a0718SPierre Ossman * request size will be the only restriction. 12591c6a0718SPierre Ossman */ 12601c6a0718SPierre Ossman mmc->max_blk_count = mmc->max_req_size; 12611c6a0718SPierre Ossman 12621c6a0718SPierre Ossman dev_set_drvdata(dev, mmc); 12631c6a0718SPierre Ossman 12641c6a0718SPierre Ossman return 0; 12651c6a0718SPierre Ossman } 12661c6a0718SPierre Ossman 1267b3627bb1SGabriel C static void wbsd_free_mmc(struct device *dev) 12681c6a0718SPierre Ossman { 12691c6a0718SPierre Ossman struct mmc_host *mmc; 12701c6a0718SPierre Ossman struct wbsd_host *host; 12711c6a0718SPierre Ossman 12721c6a0718SPierre Ossman mmc = dev_get_drvdata(dev); 12731c6a0718SPierre Ossman if (!mmc) 12741c6a0718SPierre Ossman return; 12751c6a0718SPierre Ossman 12761c6a0718SPierre Ossman host = mmc_priv(mmc); 12771c6a0718SPierre Ossman BUG_ON(host == NULL); 12781c6a0718SPierre Ossman 12791c6a0718SPierre Ossman del_timer_sync(&host->ignore_timer); 12801c6a0718SPierre Ossman 12811c6a0718SPierre Ossman mmc_free_host(mmc); 12821c6a0718SPierre Ossman 12831c6a0718SPierre Ossman dev_set_drvdata(dev, NULL); 12841c6a0718SPierre Ossman } 12851c6a0718SPierre Ossman 12861c6a0718SPierre Ossman /* 12871c6a0718SPierre Ossman * Scan for known chip id:s 12881c6a0718SPierre Ossman */ 12891c6a0718SPierre Ossman 1290c3be1efdSBill Pemberton static int wbsd_scan(struct wbsd_host *host) 12911c6a0718SPierre Ossman { 12921c6a0718SPierre Ossman int i, j, k; 12931c6a0718SPierre Ossman int id; 12941c6a0718SPierre Ossman 12951c6a0718SPierre Ossman /* 12961c6a0718SPierre Ossman * Iterate through all ports, all codes to 12971c6a0718SPierre Ossman * find hardware that is in our known list. 12981c6a0718SPierre Ossman */ 12991c6a0718SPierre Ossman for (i = 0; i < ARRAY_SIZE(config_ports); i++) { 13001c6a0718SPierre Ossman if (!request_region(config_ports[i], 2, DRIVER_NAME)) 13011c6a0718SPierre Ossman continue; 13021c6a0718SPierre Ossman 13031c6a0718SPierre Ossman for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) { 13041c6a0718SPierre Ossman id = 0xFFFF; 13051c6a0718SPierre Ossman 13061c6a0718SPierre Ossman host->config = config_ports[i]; 13071c6a0718SPierre Ossman host->unlock_code = unlock_codes[j]; 13081c6a0718SPierre Ossman 13091c6a0718SPierre Ossman wbsd_unlock_config(host); 13101c6a0718SPierre Ossman 13111c6a0718SPierre Ossman outb(WBSD_CONF_ID_HI, config_ports[i]); 13121c6a0718SPierre Ossman id = inb(config_ports[i] + 1) << 8; 13131c6a0718SPierre Ossman 13141c6a0718SPierre Ossman outb(WBSD_CONF_ID_LO, config_ports[i]); 13151c6a0718SPierre Ossman id |= inb(config_ports[i] + 1); 13161c6a0718SPierre Ossman 13171c6a0718SPierre Ossman wbsd_lock_config(host); 13181c6a0718SPierre Ossman 13191c6a0718SPierre Ossman for (k = 0; k < ARRAY_SIZE(valid_ids); k++) { 13201c6a0718SPierre Ossman if (id == valid_ids[k]) { 13211c6a0718SPierre Ossman host->chip_id = id; 13221c6a0718SPierre Ossman 13231c6a0718SPierre Ossman return 0; 13241c6a0718SPierre Ossman } 13251c6a0718SPierre Ossman } 13261c6a0718SPierre Ossman 13271c6a0718SPierre Ossman if (id != 0xFFFF) { 13281c6a0718SPierre Ossman DBG("Unknown hardware (id %x) found at %x\n", 13291c6a0718SPierre Ossman id, config_ports[i]); 13301c6a0718SPierre Ossman } 13311c6a0718SPierre Ossman } 13321c6a0718SPierre Ossman 13331c6a0718SPierre Ossman release_region(config_ports[i], 2); 13341c6a0718SPierre Ossman } 13351c6a0718SPierre Ossman 13361c6a0718SPierre Ossman host->config = 0; 13371c6a0718SPierre Ossman host->unlock_code = 0; 13381c6a0718SPierre Ossman 13391c6a0718SPierre Ossman return -ENODEV; 13401c6a0718SPierre Ossman } 13411c6a0718SPierre Ossman 13421c6a0718SPierre Ossman /* 13431c6a0718SPierre Ossman * Allocate/free io port ranges 13441c6a0718SPierre Ossman */ 13451c6a0718SPierre Ossman 1346c3be1efdSBill Pemberton static int wbsd_request_region(struct wbsd_host *host, int base) 13471c6a0718SPierre Ossman { 13481c6a0718SPierre Ossman if (base & 0x7) 13491c6a0718SPierre Ossman return -EINVAL; 13501c6a0718SPierre Ossman 13511c6a0718SPierre Ossman if (!request_region(base, 8, DRIVER_NAME)) 13521c6a0718SPierre Ossman return -EIO; 13531c6a0718SPierre Ossman 13541c6a0718SPierre Ossman host->base = base; 13551c6a0718SPierre Ossman 13561c6a0718SPierre Ossman return 0; 13571c6a0718SPierre Ossman } 13581c6a0718SPierre Ossman 1359b3627bb1SGabriel C static void wbsd_release_regions(struct wbsd_host *host) 13601c6a0718SPierre Ossman { 13611c6a0718SPierre Ossman if (host->base) 13621c6a0718SPierre Ossman release_region(host->base, 8); 13631c6a0718SPierre Ossman 13641c6a0718SPierre Ossman host->base = 0; 13651c6a0718SPierre Ossman 13661c6a0718SPierre Ossman if (host->config) 13671c6a0718SPierre Ossman release_region(host->config, 2); 13681c6a0718SPierre Ossman 13691c6a0718SPierre Ossman host->config = 0; 13701c6a0718SPierre Ossman } 13711c6a0718SPierre Ossman 13721c6a0718SPierre Ossman /* 13731c6a0718SPierre Ossman * Allocate/free DMA port and buffer 13741c6a0718SPierre Ossman */ 13751c6a0718SPierre Ossman 1376c3be1efdSBill Pemberton static void wbsd_request_dma(struct wbsd_host *host, int dma) 13771c6a0718SPierre Ossman { 13781c6a0718SPierre Ossman if (dma < 0) 13791c6a0718SPierre Ossman return; 13801c6a0718SPierre Ossman 13811c6a0718SPierre Ossman if (request_dma(dma, DRIVER_NAME)) 13821c6a0718SPierre Ossman goto err; 13831c6a0718SPierre Ossman 13841c6a0718SPierre Ossman /* 13851c6a0718SPierre Ossman * We need to allocate a special buffer in 13861c6a0718SPierre Ossman * order for ISA to be able to DMA to it. 13871c6a0718SPierre Ossman */ 13881c6a0718SPierre Ossman host->dma_buffer = kmalloc(WBSD_DMA_SIZE, 13891c6a0718SPierre Ossman GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); 13901c6a0718SPierre Ossman if (!host->dma_buffer) 13911c6a0718SPierre Ossman goto free; 13921c6a0718SPierre Ossman 13931c6a0718SPierre Ossman /* 13941c6a0718SPierre Ossman * Translate the address to a physical address. 13951c6a0718SPierre Ossman */ 13961c6a0718SPierre Ossman host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, 13971c6a0718SPierre Ossman WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); 1398a5488a35SAlexey Khoroshilov if (dma_mapping_error(mmc_dev(host->mmc), host->dma_addr)) 1399a5488a35SAlexey Khoroshilov goto kfree; 14001c6a0718SPierre Ossman 14011c6a0718SPierre Ossman /* 14021c6a0718SPierre Ossman * ISA DMA must be aligned on a 64k basis. 14031c6a0718SPierre Ossman */ 14041c6a0718SPierre Ossman if ((host->dma_addr & 0xffff) != 0) 1405a5488a35SAlexey Khoroshilov goto unmap; 14061c6a0718SPierre Ossman /* 14071c6a0718SPierre Ossman * ISA cannot access memory above 16 MB. 14081c6a0718SPierre Ossman */ 14091c6a0718SPierre Ossman else if (host->dma_addr >= 0x1000000) 1410a5488a35SAlexey Khoroshilov goto unmap; 14111c6a0718SPierre Ossman 14121c6a0718SPierre Ossman host->dma = dma; 14131c6a0718SPierre Ossman 14141c6a0718SPierre Ossman return; 14151c6a0718SPierre Ossman 1416a5488a35SAlexey Khoroshilov unmap: 14171c6a0718SPierre Ossman /* 14181c6a0718SPierre Ossman * If we've gotten here then there is some kind of alignment bug 14191c6a0718SPierre Ossman */ 14201c6a0718SPierre Ossman BUG_ON(1); 14211c6a0718SPierre Ossman 14221c6a0718SPierre Ossman dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, 14231c6a0718SPierre Ossman WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); 142497067d55SPierre Ossman host->dma_addr = 0; 14251c6a0718SPierre Ossman 1426a5488a35SAlexey Khoroshilov kfree: 14271c6a0718SPierre Ossman kfree(host->dma_buffer); 14281c6a0718SPierre Ossman host->dma_buffer = NULL; 14291c6a0718SPierre Ossman 14301c6a0718SPierre Ossman free: 14311c6a0718SPierre Ossman free_dma(dma); 14321c6a0718SPierre Ossman 14331c6a0718SPierre Ossman err: 14346606110dSJoe Perches pr_warn(DRIVER_NAME ": Unable to allocate DMA %d - falling back on FIFO\n", 14356606110dSJoe Perches dma); 14361c6a0718SPierre Ossman } 14371c6a0718SPierre Ossman 1438b3627bb1SGabriel C static void wbsd_release_dma(struct wbsd_host *host) 14391c6a0718SPierre Ossman { 1440e81c022aSAlexey Khoroshilov /* 1441e81c022aSAlexey Khoroshilov * host->dma_addr is valid here iff host->dma_buffer is not NULL. 1442e81c022aSAlexey Khoroshilov */ 1443e81c022aSAlexey Khoroshilov if (host->dma_buffer) { 14441c6a0718SPierre Ossman dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, 14451c6a0718SPierre Ossman WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); 14461c6a0718SPierre Ossman kfree(host->dma_buffer); 1447e81c022aSAlexey Khoroshilov } 14481c6a0718SPierre Ossman if (host->dma >= 0) 14491c6a0718SPierre Ossman free_dma(host->dma); 14501c6a0718SPierre Ossman 14511c6a0718SPierre Ossman host->dma = -1; 14521c6a0718SPierre Ossman host->dma_buffer = NULL; 145397067d55SPierre Ossman host->dma_addr = 0; 14541c6a0718SPierre Ossman } 14551c6a0718SPierre Ossman 14561c6a0718SPierre Ossman /* 14571c6a0718SPierre Ossman * Allocate/free IRQ. 14581c6a0718SPierre Ossman */ 14591c6a0718SPierre Ossman 1460c3be1efdSBill Pemberton static int wbsd_request_irq(struct wbsd_host *host, int irq) 14611c6a0718SPierre Ossman { 14621c6a0718SPierre Ossman int ret; 14631c6a0718SPierre Ossman 14641c6a0718SPierre Ossman /* 1465cef33400SChuck Ebbert * Set up tasklets. Must be done before requesting interrupt. 14661c6a0718SPierre Ossman */ 14671c6a0718SPierre Ossman tasklet_init(&host->card_tasklet, wbsd_tasklet_card, 14681c6a0718SPierre Ossman (unsigned long)host); 14691c6a0718SPierre Ossman tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, 14701c6a0718SPierre Ossman (unsigned long)host); 14711c6a0718SPierre Ossman tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, 14721c6a0718SPierre Ossman (unsigned long)host); 14731c6a0718SPierre Ossman tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, 14741c6a0718SPierre Ossman (unsigned long)host); 14751c6a0718SPierre Ossman tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, 14761c6a0718SPierre Ossman (unsigned long)host); 14771c6a0718SPierre Ossman 1478cef33400SChuck Ebbert /* 1479cef33400SChuck Ebbert * Allocate interrupt. 1480cef33400SChuck Ebbert */ 1481cef33400SChuck Ebbert ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); 1482cef33400SChuck Ebbert if (ret) 1483cef33400SChuck Ebbert return ret; 1484cef33400SChuck Ebbert 1485cef33400SChuck Ebbert host->irq = irq; 1486cef33400SChuck Ebbert 14871c6a0718SPierre Ossman return 0; 14881c6a0718SPierre Ossman } 14891c6a0718SPierre Ossman 1490b3627bb1SGabriel C static void wbsd_release_irq(struct wbsd_host *host) 14911c6a0718SPierre Ossman { 14921c6a0718SPierre Ossman if (!host->irq) 14931c6a0718SPierre Ossman return; 14941c6a0718SPierre Ossman 14951c6a0718SPierre Ossman free_irq(host->irq, host); 14961c6a0718SPierre Ossman 14971c6a0718SPierre Ossman host->irq = 0; 14981c6a0718SPierre Ossman 14991c6a0718SPierre Ossman tasklet_kill(&host->card_tasklet); 15001c6a0718SPierre Ossman tasklet_kill(&host->fifo_tasklet); 15011c6a0718SPierre Ossman tasklet_kill(&host->crc_tasklet); 15021c6a0718SPierre Ossman tasklet_kill(&host->timeout_tasklet); 15031c6a0718SPierre Ossman tasklet_kill(&host->finish_tasklet); 15041c6a0718SPierre Ossman } 15051c6a0718SPierre Ossman 15061c6a0718SPierre Ossman /* 15071c6a0718SPierre Ossman * Allocate all resources for the host. 15081c6a0718SPierre Ossman */ 15091c6a0718SPierre Ossman 1510c3be1efdSBill Pemberton static int wbsd_request_resources(struct wbsd_host *host, 15111c6a0718SPierre Ossman int base, int irq, int dma) 15121c6a0718SPierre Ossman { 15131c6a0718SPierre Ossman int ret; 15141c6a0718SPierre Ossman 15151c6a0718SPierre Ossman /* 15161c6a0718SPierre Ossman * Allocate I/O ports. 15171c6a0718SPierre Ossman */ 15181c6a0718SPierre Ossman ret = wbsd_request_region(host, base); 15191c6a0718SPierre Ossman if (ret) 15201c6a0718SPierre Ossman return ret; 15211c6a0718SPierre Ossman 15221c6a0718SPierre Ossman /* 15231c6a0718SPierre Ossman * Allocate interrupt. 15241c6a0718SPierre Ossman */ 15251c6a0718SPierre Ossman ret = wbsd_request_irq(host, irq); 15261c6a0718SPierre Ossman if (ret) 15271c6a0718SPierre Ossman return ret; 15281c6a0718SPierre Ossman 15291c6a0718SPierre Ossman /* 15301c6a0718SPierre Ossman * Allocate DMA. 15311c6a0718SPierre Ossman */ 15321c6a0718SPierre Ossman wbsd_request_dma(host, dma); 15331c6a0718SPierre Ossman 15341c6a0718SPierre Ossman return 0; 15351c6a0718SPierre Ossman } 15361c6a0718SPierre Ossman 15371c6a0718SPierre Ossman /* 15381c6a0718SPierre Ossman * Release all resources for the host. 15391c6a0718SPierre Ossman */ 15401c6a0718SPierre Ossman 1541b3627bb1SGabriel C static void wbsd_release_resources(struct wbsd_host *host) 15421c6a0718SPierre Ossman { 15431c6a0718SPierre Ossman wbsd_release_dma(host); 15441c6a0718SPierre Ossman wbsd_release_irq(host); 15451c6a0718SPierre Ossman wbsd_release_regions(host); 15461c6a0718SPierre Ossman } 15471c6a0718SPierre Ossman 15481c6a0718SPierre Ossman /* 15491c6a0718SPierre Ossman * Configure the resources the chip should use. 15501c6a0718SPierre Ossman */ 15511c6a0718SPierre Ossman 15521c6a0718SPierre Ossman static void wbsd_chip_config(struct wbsd_host *host) 15531c6a0718SPierre Ossman { 15541c6a0718SPierre Ossman wbsd_unlock_config(host); 15551c6a0718SPierre Ossman 15561c6a0718SPierre Ossman /* 15571c6a0718SPierre Ossman * Reset the chip. 15581c6a0718SPierre Ossman */ 15591c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_SWRST, 1); 15601c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_SWRST, 0); 15611c6a0718SPierre Ossman 15621c6a0718SPierre Ossman /* 15631c6a0718SPierre Ossman * Select SD/MMC function. 15641c6a0718SPierre Ossman */ 15651c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); 15661c6a0718SPierre Ossman 15671c6a0718SPierre Ossman /* 15681c6a0718SPierre Ossman * Set up card detection. 15691c6a0718SPierre Ossman */ 15701c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); 15711c6a0718SPierre Ossman 15721c6a0718SPierre Ossman /* 15731c6a0718SPierre Ossman * Configure chip 15741c6a0718SPierre Ossman */ 15751c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); 15761c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); 15771c6a0718SPierre Ossman 15781c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); 15791c6a0718SPierre Ossman 15801c6a0718SPierre Ossman if (host->dma >= 0) 15811c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); 15821c6a0718SPierre Ossman 15831c6a0718SPierre Ossman /* 15841c6a0718SPierre Ossman * Enable and power up chip. 15851c6a0718SPierre Ossman */ 15861c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_ENABLE, 1); 15871c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_POWER, 0x20); 15881c6a0718SPierre Ossman 15891c6a0718SPierre Ossman wbsd_lock_config(host); 15901c6a0718SPierre Ossman } 15911c6a0718SPierre Ossman 15921c6a0718SPierre Ossman /* 15931c6a0718SPierre Ossman * Check that configured resources are correct. 15941c6a0718SPierre Ossman */ 15951c6a0718SPierre Ossman 15961c6a0718SPierre Ossman static int wbsd_chip_validate(struct wbsd_host *host) 15971c6a0718SPierre Ossman { 15981c6a0718SPierre Ossman int base, irq, dma; 15991c6a0718SPierre Ossman 16001c6a0718SPierre Ossman wbsd_unlock_config(host); 16011c6a0718SPierre Ossman 16021c6a0718SPierre Ossman /* 16031c6a0718SPierre Ossman * Select SD/MMC function. 16041c6a0718SPierre Ossman */ 16051c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); 16061c6a0718SPierre Ossman 16071c6a0718SPierre Ossman /* 16081c6a0718SPierre Ossman * Read configuration. 16091c6a0718SPierre Ossman */ 16101c6a0718SPierre Ossman base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; 16111c6a0718SPierre Ossman base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); 16121c6a0718SPierre Ossman 16131c6a0718SPierre Ossman irq = wbsd_read_config(host, WBSD_CONF_IRQ); 16141c6a0718SPierre Ossman 16151c6a0718SPierre Ossman dma = wbsd_read_config(host, WBSD_CONF_DRQ); 16161c6a0718SPierre Ossman 16171c6a0718SPierre Ossman wbsd_lock_config(host); 16181c6a0718SPierre Ossman 16191c6a0718SPierre Ossman /* 16201c6a0718SPierre Ossman * Validate against given configuration. 16211c6a0718SPierre Ossman */ 16221c6a0718SPierre Ossman if (base != host->base) 16231c6a0718SPierre Ossman return 0; 16241c6a0718SPierre Ossman if (irq != host->irq) 16251c6a0718SPierre Ossman return 0; 16261c6a0718SPierre Ossman if ((dma != host->dma) && (host->dma != -1)) 16271c6a0718SPierre Ossman return 0; 16281c6a0718SPierre Ossman 16291c6a0718SPierre Ossman return 1; 16301c6a0718SPierre Ossman } 16311c6a0718SPierre Ossman 16321c6a0718SPierre Ossman /* 16331c6a0718SPierre Ossman * Powers down the SD function 16341c6a0718SPierre Ossman */ 16351c6a0718SPierre Ossman 16361c6a0718SPierre Ossman static void wbsd_chip_poweroff(struct wbsd_host *host) 16371c6a0718SPierre Ossman { 16381c6a0718SPierre Ossman wbsd_unlock_config(host); 16391c6a0718SPierre Ossman 16401c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); 16411c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_ENABLE, 0); 16421c6a0718SPierre Ossman 16431c6a0718SPierre Ossman wbsd_lock_config(host); 16441c6a0718SPierre Ossman } 16451c6a0718SPierre Ossman 16461c6a0718SPierre Ossman /*****************************************************************************\ 16471c6a0718SPierre Ossman * * 16481c6a0718SPierre Ossman * Devices setup and shutdown * 16491c6a0718SPierre Ossman * * 16501c6a0718SPierre Ossman \*****************************************************************************/ 16511c6a0718SPierre Ossman 1652c3be1efdSBill Pemberton static int wbsd_init(struct device *dev, int base, int irq, int dma, 16531c6a0718SPierre Ossman int pnp) 16541c6a0718SPierre Ossman { 16551c6a0718SPierre Ossman struct wbsd_host *host = NULL; 16561c6a0718SPierre Ossman struct mmc_host *mmc = NULL; 16571c6a0718SPierre Ossman int ret; 16581c6a0718SPierre Ossman 16591c6a0718SPierre Ossman ret = wbsd_alloc_mmc(dev); 16601c6a0718SPierre Ossman if (ret) 16611c6a0718SPierre Ossman return ret; 16621c6a0718SPierre Ossman 16631c6a0718SPierre Ossman mmc = dev_get_drvdata(dev); 16641c6a0718SPierre Ossman host = mmc_priv(mmc); 16651c6a0718SPierre Ossman 16661c6a0718SPierre Ossman /* 16671c6a0718SPierre Ossman * Scan for hardware. 16681c6a0718SPierre Ossman */ 16691c6a0718SPierre Ossman ret = wbsd_scan(host); 16701c6a0718SPierre Ossman if (ret) { 16711c6a0718SPierre Ossman if (pnp && (ret == -ENODEV)) { 16726606110dSJoe Perches pr_warn(DRIVER_NAME ": Unable to confirm device presence - you may experience lock-ups\n"); 16731c6a0718SPierre Ossman } else { 16741c6a0718SPierre Ossman wbsd_free_mmc(dev); 16751c6a0718SPierre Ossman return ret; 16761c6a0718SPierre Ossman } 16771c6a0718SPierre Ossman } 16781c6a0718SPierre Ossman 16791c6a0718SPierre Ossman /* 16801c6a0718SPierre Ossman * Request resources. 16811c6a0718SPierre Ossman */ 16821c6a0718SPierre Ossman ret = wbsd_request_resources(host, base, irq, dma); 16831c6a0718SPierre Ossman if (ret) { 16841c6a0718SPierre Ossman wbsd_release_resources(host); 16851c6a0718SPierre Ossman wbsd_free_mmc(dev); 16861c6a0718SPierre Ossman return ret; 16871c6a0718SPierre Ossman } 16881c6a0718SPierre Ossman 16891c6a0718SPierre Ossman /* 16901c6a0718SPierre Ossman * See if chip needs to be configured. 16911c6a0718SPierre Ossman */ 16921c6a0718SPierre Ossman if (pnp) { 16931c6a0718SPierre Ossman if ((host->config != 0) && !wbsd_chip_validate(host)) { 16946606110dSJoe Perches pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n"); 16951c6a0718SPierre Ossman wbsd_chip_config(host); 16961c6a0718SPierre Ossman } 16971c6a0718SPierre Ossman } else 16981c6a0718SPierre Ossman wbsd_chip_config(host); 16991c6a0718SPierre Ossman 17001c6a0718SPierre Ossman /* 17011c6a0718SPierre Ossman * Power Management stuff. No idea how this works. 17021c6a0718SPierre Ossman * Not tested. 17031c6a0718SPierre Ossman */ 17041c6a0718SPierre Ossman #ifdef CONFIG_PM 17051c6a0718SPierre Ossman if (host->config) { 17061c6a0718SPierre Ossman wbsd_unlock_config(host); 17071c6a0718SPierre Ossman wbsd_write_config(host, WBSD_CONF_PME, 0xA0); 17081c6a0718SPierre Ossman wbsd_lock_config(host); 17091c6a0718SPierre Ossman } 17101c6a0718SPierre Ossman #endif 17111c6a0718SPierre Ossman /* 17121c6a0718SPierre Ossman * Allow device to initialise itself properly. 17131c6a0718SPierre Ossman */ 17141c6a0718SPierre Ossman mdelay(5); 17151c6a0718SPierre Ossman 17161c6a0718SPierre Ossman /* 17171c6a0718SPierre Ossman * Reset the chip into a known state. 17181c6a0718SPierre Ossman */ 17191c6a0718SPierre Ossman wbsd_init_device(host); 17201c6a0718SPierre Ossman 17211c6a0718SPierre Ossman mmc_add_host(mmc); 17221c6a0718SPierre Ossman 1723a3c76eb9SGirish K S pr_info("%s: W83L51xD", mmc_hostname(mmc)); 17241c6a0718SPierre Ossman if (host->chip_id != 0) 17251c6a0718SPierre Ossman printk(" id %x", (int)host->chip_id); 17261c6a0718SPierre Ossman printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); 17271c6a0718SPierre Ossman if (host->dma >= 0) 17281c6a0718SPierre Ossman printk(" dma %d", (int)host->dma); 17291c6a0718SPierre Ossman else 17301c6a0718SPierre Ossman printk(" FIFO"); 17311c6a0718SPierre Ossman if (pnp) 17321c6a0718SPierre Ossman printk(" PnP"); 17331c6a0718SPierre Ossman printk("\n"); 17341c6a0718SPierre Ossman 17351c6a0718SPierre Ossman return 0; 17361c6a0718SPierre Ossman } 17371c6a0718SPierre Ossman 17386e0ee714SBill Pemberton static void wbsd_shutdown(struct device *dev, int pnp) 17391c6a0718SPierre Ossman { 17401c6a0718SPierre Ossman struct mmc_host *mmc = dev_get_drvdata(dev); 17411c6a0718SPierre Ossman struct wbsd_host *host; 17421c6a0718SPierre Ossman 17431c6a0718SPierre Ossman if (!mmc) 17441c6a0718SPierre Ossman return; 17451c6a0718SPierre Ossman 17461c6a0718SPierre Ossman host = mmc_priv(mmc); 17471c6a0718SPierre Ossman 17481c6a0718SPierre Ossman mmc_remove_host(mmc); 17491c6a0718SPierre Ossman 17501c6a0718SPierre Ossman /* 17511c6a0718SPierre Ossman * Power down the SD/MMC function. 17521c6a0718SPierre Ossman */ 17531c6a0718SPierre Ossman if (!pnp) 17541c6a0718SPierre Ossman wbsd_chip_poweroff(host); 17551c6a0718SPierre Ossman 17561c6a0718SPierre Ossman wbsd_release_resources(host); 17571c6a0718SPierre Ossman 17581c6a0718SPierre Ossman wbsd_free_mmc(dev); 17591c6a0718SPierre Ossman } 17601c6a0718SPierre Ossman 17611c6a0718SPierre Ossman /* 17621c6a0718SPierre Ossman * Non-PnP 17631c6a0718SPierre Ossman */ 17641c6a0718SPierre Ossman 1765c3be1efdSBill Pemberton static int wbsd_probe(struct platform_device *dev) 17661c6a0718SPierre Ossman { 17671c6a0718SPierre Ossman /* Use the module parameters for resources */ 17689eeebd22STomas Winkler return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0); 17691c6a0718SPierre Ossman } 17701c6a0718SPierre Ossman 17716e0ee714SBill Pemberton static int wbsd_remove(struct platform_device *dev) 17721c6a0718SPierre Ossman { 17731c6a0718SPierre Ossman wbsd_shutdown(&dev->dev, 0); 17741c6a0718SPierre Ossman 17751c6a0718SPierre Ossman return 0; 17761c6a0718SPierre Ossman } 17771c6a0718SPierre Ossman 17781c6a0718SPierre Ossman /* 17791c6a0718SPierre Ossman * PnP 17801c6a0718SPierre Ossman */ 17811c6a0718SPierre Ossman 17821c6a0718SPierre Ossman #ifdef CONFIG_PNP 17831c6a0718SPierre Ossman 1784c3be1efdSBill Pemberton static int 17851c6a0718SPierre Ossman wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) 17861c6a0718SPierre Ossman { 17871c6a0718SPierre Ossman int io, irq, dma; 17881c6a0718SPierre Ossman 17891c6a0718SPierre Ossman /* 17901c6a0718SPierre Ossman * Get resources from PnP layer. 17911c6a0718SPierre Ossman */ 17921c6a0718SPierre Ossman io = pnp_port_start(pnpdev, 0); 17931c6a0718SPierre Ossman irq = pnp_irq(pnpdev, 0); 17941c6a0718SPierre Ossman if (pnp_dma_valid(pnpdev, 0)) 17951c6a0718SPierre Ossman dma = pnp_dma(pnpdev, 0); 17961c6a0718SPierre Ossman else 17971c6a0718SPierre Ossman dma = -1; 17981c6a0718SPierre Ossman 17991c6a0718SPierre Ossman DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); 18001c6a0718SPierre Ossman 18011c6a0718SPierre Ossman return wbsd_init(&pnpdev->dev, io, irq, dma, 1); 18021c6a0718SPierre Ossman } 18031c6a0718SPierre Ossman 18046e0ee714SBill Pemberton static void wbsd_pnp_remove(struct pnp_dev *dev) 18051c6a0718SPierre Ossman { 18061c6a0718SPierre Ossman wbsd_shutdown(&dev->dev, 1); 18071c6a0718SPierre Ossman } 18081c6a0718SPierre Ossman 18091c6a0718SPierre Ossman #endif /* CONFIG_PNP */ 18101c6a0718SPierre Ossman 18111c6a0718SPierre Ossman /* 18121c6a0718SPierre Ossman * Power management 18131c6a0718SPierre Ossman */ 18141c6a0718SPierre Ossman 18151c6a0718SPierre Ossman #ifdef CONFIG_PM 18161c6a0718SPierre Ossman 18171c6a0718SPierre Ossman static int wbsd_platform_suspend(struct platform_device *dev, 18181c6a0718SPierre Ossman pm_message_t state) 18191c6a0718SPierre Ossman { 18201c6a0718SPierre Ossman struct mmc_host *mmc = platform_get_drvdata(dev); 18211c6a0718SPierre Ossman struct wbsd_host *host; 18221c6a0718SPierre Ossman 18231c6a0718SPierre Ossman if (mmc == NULL) 18241c6a0718SPierre Ossman return 0; 18251c6a0718SPierre Ossman 18261c6a0718SPierre Ossman DBGF("Suspending...\n"); 18271c6a0718SPierre Ossman 18281c6a0718SPierre Ossman host = mmc_priv(mmc); 18291c6a0718SPierre Ossman 18301c6a0718SPierre Ossman wbsd_chip_poweroff(host); 18311c6a0718SPierre Ossman return 0; 18321c6a0718SPierre Ossman } 18331c6a0718SPierre Ossman 18341c6a0718SPierre Ossman static int wbsd_platform_resume(struct platform_device *dev) 18351c6a0718SPierre Ossman { 18361c6a0718SPierre Ossman struct mmc_host *mmc = platform_get_drvdata(dev); 18371c6a0718SPierre Ossman struct wbsd_host *host; 18381c6a0718SPierre Ossman 18391c6a0718SPierre Ossman if (mmc == NULL) 18401c6a0718SPierre Ossman return 0; 18411c6a0718SPierre Ossman 18421c6a0718SPierre Ossman DBGF("Resuming...\n"); 18431c6a0718SPierre Ossman 18441c6a0718SPierre Ossman host = mmc_priv(mmc); 18451c6a0718SPierre Ossman 18461c6a0718SPierre Ossman wbsd_chip_config(host); 18471c6a0718SPierre Ossman 18481c6a0718SPierre Ossman /* 18491c6a0718SPierre Ossman * Allow device to initialise itself properly. 18501c6a0718SPierre Ossman */ 18511c6a0718SPierre Ossman mdelay(5); 18521c6a0718SPierre Ossman 185383234ac8SUlf Hansson wbsd_init_device(host); 185483234ac8SUlf Hansson return 0; 18551c6a0718SPierre Ossman } 18561c6a0718SPierre Ossman 18571c6a0718SPierre Ossman #ifdef CONFIG_PNP 18581c6a0718SPierre Ossman 18591c6a0718SPierre Ossman static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) 18601c6a0718SPierre Ossman { 18611c6a0718SPierre Ossman struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); 18621c6a0718SPierre Ossman 18631c6a0718SPierre Ossman if (mmc == NULL) 18641c6a0718SPierre Ossman return 0; 18651c6a0718SPierre Ossman 18661c6a0718SPierre Ossman DBGF("Suspending...\n"); 186783234ac8SUlf Hansson return 0; 18681c6a0718SPierre Ossman } 18691c6a0718SPierre Ossman 18701c6a0718SPierre Ossman static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) 18711c6a0718SPierre Ossman { 18721c6a0718SPierre Ossman struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); 18731c6a0718SPierre Ossman struct wbsd_host *host; 18741c6a0718SPierre Ossman 18751c6a0718SPierre Ossman if (mmc == NULL) 18761c6a0718SPierre Ossman return 0; 18771c6a0718SPierre Ossman 18781c6a0718SPierre Ossman DBGF("Resuming...\n"); 18791c6a0718SPierre Ossman 18801c6a0718SPierre Ossman host = mmc_priv(mmc); 18811c6a0718SPierre Ossman 18821c6a0718SPierre Ossman /* 18831c6a0718SPierre Ossman * See if chip needs to be configured. 18841c6a0718SPierre Ossman */ 18851c6a0718SPierre Ossman if (host->config != 0) { 18861c6a0718SPierre Ossman if (!wbsd_chip_validate(host)) { 18876606110dSJoe Perches pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n"); 18881c6a0718SPierre Ossman wbsd_chip_config(host); 18891c6a0718SPierre Ossman } 18901c6a0718SPierre Ossman } 18911c6a0718SPierre Ossman 18921c6a0718SPierre Ossman /* 18931c6a0718SPierre Ossman * Allow device to initialise itself properly. 18941c6a0718SPierre Ossman */ 18951c6a0718SPierre Ossman mdelay(5); 18961c6a0718SPierre Ossman 189783234ac8SUlf Hansson wbsd_init_device(host); 189883234ac8SUlf Hansson return 0; 18991c6a0718SPierre Ossman } 19001c6a0718SPierre Ossman 19011c6a0718SPierre Ossman #endif /* CONFIG_PNP */ 19021c6a0718SPierre Ossman 19031c6a0718SPierre Ossman #else /* CONFIG_PM */ 19041c6a0718SPierre Ossman 19051c6a0718SPierre Ossman #define wbsd_platform_suspend NULL 19061c6a0718SPierre Ossman #define wbsd_platform_resume NULL 19071c6a0718SPierre Ossman 19081c6a0718SPierre Ossman #define wbsd_pnp_suspend NULL 19091c6a0718SPierre Ossman #define wbsd_pnp_resume NULL 19101c6a0718SPierre Ossman 19111c6a0718SPierre Ossman #endif /* CONFIG_PM */ 19121c6a0718SPierre Ossman 19131c6a0718SPierre Ossman static struct platform_device *wbsd_device; 19141c6a0718SPierre Ossman 19151c6a0718SPierre Ossman static struct platform_driver wbsd_driver = { 19161c6a0718SPierre Ossman .probe = wbsd_probe, 19170433c143SBill Pemberton .remove = wbsd_remove, 19181c6a0718SPierre Ossman 19191c6a0718SPierre Ossman .suspend = wbsd_platform_suspend, 19201c6a0718SPierre Ossman .resume = wbsd_platform_resume, 19211c6a0718SPierre Ossman .driver = { 19221c6a0718SPierre Ossman .name = DRIVER_NAME, 19231c6a0718SPierre Ossman }, 19241c6a0718SPierre Ossman }; 19251c6a0718SPierre Ossman 19261c6a0718SPierre Ossman #ifdef CONFIG_PNP 19271c6a0718SPierre Ossman 19281c6a0718SPierre Ossman static struct pnp_driver wbsd_pnp_driver = { 19291c6a0718SPierre Ossman .name = DRIVER_NAME, 19301c6a0718SPierre Ossman .id_table = pnp_dev_table, 19311c6a0718SPierre Ossman .probe = wbsd_pnp_probe, 19320433c143SBill Pemberton .remove = wbsd_pnp_remove, 19331c6a0718SPierre Ossman 19341c6a0718SPierre Ossman .suspend = wbsd_pnp_suspend, 19351c6a0718SPierre Ossman .resume = wbsd_pnp_resume, 19361c6a0718SPierre Ossman }; 19371c6a0718SPierre Ossman 19381c6a0718SPierre Ossman #endif /* CONFIG_PNP */ 19391c6a0718SPierre Ossman 19401c6a0718SPierre Ossman /* 19411c6a0718SPierre Ossman * Module loading/unloading 19421c6a0718SPierre Ossman */ 19431c6a0718SPierre Ossman 19441c6a0718SPierre Ossman static int __init wbsd_drv_init(void) 19451c6a0718SPierre Ossman { 19461c6a0718SPierre Ossman int result; 19471c6a0718SPierre Ossman 1948a3c76eb9SGirish K S pr_info(DRIVER_NAME 19491c6a0718SPierre Ossman ": Winbond W83L51xD SD/MMC card interface driver\n"); 1950a3c76eb9SGirish K S pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); 19511c6a0718SPierre Ossman 19521c6a0718SPierre Ossman #ifdef CONFIG_PNP 19531c6a0718SPierre Ossman 19549eeebd22STomas Winkler if (!param_nopnp) { 19551c6a0718SPierre Ossman result = pnp_register_driver(&wbsd_pnp_driver); 19561c6a0718SPierre Ossman if (result < 0) 19571c6a0718SPierre Ossman return result; 19581c6a0718SPierre Ossman } 19591c6a0718SPierre Ossman #endif /* CONFIG_PNP */ 19601c6a0718SPierre Ossman 19619eeebd22STomas Winkler if (param_nopnp) { 19621c6a0718SPierre Ossman result = platform_driver_register(&wbsd_driver); 19631c6a0718SPierre Ossman if (result < 0) 19641c6a0718SPierre Ossman return result; 19651c6a0718SPierre Ossman 19661c6a0718SPierre Ossman wbsd_device = platform_device_alloc(DRIVER_NAME, -1); 19671c6a0718SPierre Ossman if (!wbsd_device) { 19681c6a0718SPierre Ossman platform_driver_unregister(&wbsd_driver); 19691c6a0718SPierre Ossman return -ENOMEM; 19701c6a0718SPierre Ossman } 19711c6a0718SPierre Ossman 19721c6a0718SPierre Ossman result = platform_device_add(wbsd_device); 19731c6a0718SPierre Ossman if (result) { 19741c6a0718SPierre Ossman platform_device_put(wbsd_device); 19751c6a0718SPierre Ossman platform_driver_unregister(&wbsd_driver); 19761c6a0718SPierre Ossman return result; 19771c6a0718SPierre Ossman } 19781c6a0718SPierre Ossman } 19791c6a0718SPierre Ossman 19801c6a0718SPierre Ossman return 0; 19811c6a0718SPierre Ossman } 19821c6a0718SPierre Ossman 19831c6a0718SPierre Ossman static void __exit wbsd_drv_exit(void) 19841c6a0718SPierre Ossman { 19851c6a0718SPierre Ossman #ifdef CONFIG_PNP 19861c6a0718SPierre Ossman 19879eeebd22STomas Winkler if (!param_nopnp) 19881c6a0718SPierre Ossman pnp_unregister_driver(&wbsd_pnp_driver); 19891c6a0718SPierre Ossman 19901c6a0718SPierre Ossman #endif /* CONFIG_PNP */ 19911c6a0718SPierre Ossman 19929eeebd22STomas Winkler if (param_nopnp) { 19931c6a0718SPierre Ossman platform_device_unregister(wbsd_device); 19941c6a0718SPierre Ossman 19951c6a0718SPierre Ossman platform_driver_unregister(&wbsd_driver); 19961c6a0718SPierre Ossman } 19971c6a0718SPierre Ossman 19981c6a0718SPierre Ossman DBG("unloaded\n"); 19991c6a0718SPierre Ossman } 20001c6a0718SPierre Ossman 20011c6a0718SPierre Ossman module_init(wbsd_drv_init); 20021c6a0718SPierre Ossman module_exit(wbsd_drv_exit); 20031c6a0718SPierre Ossman #ifdef CONFIG_PNP 20049eeebd22STomas Winkler module_param_named(nopnp, param_nopnp, uint, 0444); 20051c6a0718SPierre Ossman #endif 20069eeebd22STomas Winkler module_param_named(io, param_io, uint, 0444); 20079eeebd22STomas Winkler module_param_named(irq, param_irq, uint, 0444); 20089eeebd22STomas Winkler module_param_named(dma, param_dma, int, 0444); 20091c6a0718SPierre Ossman 20101c6a0718SPierre Ossman MODULE_LICENSE("GPL"); 201132710e8fSPierre Ossman MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>"); 20121c6a0718SPierre Ossman MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); 20131c6a0718SPierre Ossman 20141c6a0718SPierre Ossman #ifdef CONFIG_PNP 20151c6a0718SPierre Ossman MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); 20161c6a0718SPierre Ossman #endif 20171c6a0718SPierre Ossman MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); 20181c6a0718SPierre Ossman MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); 20191c6a0718SPierre Ossman MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); 2020