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