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