11c6a0718SPierre Ossman /* 21c6a0718SPierre Ossman * linux/drivers/media/mmc/omap.c 31c6a0718SPierre Ossman * 41c6a0718SPierre Ossman * Copyright (C) 2004 Nokia Corporation 51c6a0718SPierre Ossman * Written by Tuukka Tikkanen and Juha Yrj�l�<juha.yrjola@nokia.com> 61c6a0718SPierre Ossman * Misc hacks here and there by Tony Lindgren <tony@atomide.com> 71c6a0718SPierre Ossman * Other hacks (DMA, SD, etc) by David Brownell 81c6a0718SPierre Ossman * 91c6a0718SPierre Ossman * This program is free software; you can redistribute it and/or modify 101c6a0718SPierre Ossman * it under the terms of the GNU General Public License version 2 as 111c6a0718SPierre Ossman * published by the Free Software Foundation. 121c6a0718SPierre Ossman */ 131c6a0718SPierre Ossman 141c6a0718SPierre Ossman #include <linux/module.h> 151c6a0718SPierre Ossman #include <linux/moduleparam.h> 161c6a0718SPierre Ossman #include <linux/init.h> 171c6a0718SPierre Ossman #include <linux/ioport.h> 181c6a0718SPierre Ossman #include <linux/platform_device.h> 191c6a0718SPierre Ossman #include <linux/interrupt.h> 201c6a0718SPierre Ossman #include <linux/dma-mapping.h> 211c6a0718SPierre Ossman #include <linux/delay.h> 221c6a0718SPierre Ossman #include <linux/spinlock.h> 231c6a0718SPierre Ossman #include <linux/timer.h> 241c6a0718SPierre Ossman #include <linux/mmc/host.h> 251c6a0718SPierre Ossman #include <linux/mmc/card.h> 261c6a0718SPierre Ossman #include <linux/clk.h> 271c6a0718SPierre Ossman 281c6a0718SPierre Ossman #include <asm/io.h> 291c6a0718SPierre Ossman #include <asm/irq.h> 301c6a0718SPierre Ossman #include <asm/scatterlist.h> 311c6a0718SPierre Ossman #include <asm/mach-types.h> 321c6a0718SPierre Ossman 331c6a0718SPierre Ossman #include <asm/arch/board.h> 341c6a0718SPierre Ossman #include <asm/arch/gpio.h> 351c6a0718SPierre Ossman #include <asm/arch/dma.h> 361c6a0718SPierre Ossman #include <asm/arch/mux.h> 371c6a0718SPierre Ossman #include <asm/arch/fpga.h> 381c6a0718SPierre Ossman #include <asm/arch/tps65010.h> 391c6a0718SPierre Ossman 401c6a0718SPierre Ossman #define OMAP_MMC_REG_CMD 0x00 411c6a0718SPierre Ossman #define OMAP_MMC_REG_ARGL 0x04 421c6a0718SPierre Ossman #define OMAP_MMC_REG_ARGH 0x08 431c6a0718SPierre Ossman #define OMAP_MMC_REG_CON 0x0c 441c6a0718SPierre Ossman #define OMAP_MMC_REG_STAT 0x10 451c6a0718SPierre Ossman #define OMAP_MMC_REG_IE 0x14 461c6a0718SPierre Ossman #define OMAP_MMC_REG_CTO 0x18 471c6a0718SPierre Ossman #define OMAP_MMC_REG_DTO 0x1c 481c6a0718SPierre Ossman #define OMAP_MMC_REG_DATA 0x20 491c6a0718SPierre Ossman #define OMAP_MMC_REG_BLEN 0x24 501c6a0718SPierre Ossman #define OMAP_MMC_REG_NBLK 0x28 511c6a0718SPierre Ossman #define OMAP_MMC_REG_BUF 0x2c 521c6a0718SPierre Ossman #define OMAP_MMC_REG_SDIO 0x34 531c6a0718SPierre Ossman #define OMAP_MMC_REG_REV 0x3c 541c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP0 0x40 551c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP1 0x44 561c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP2 0x48 571c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP3 0x4c 581c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP4 0x50 591c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP5 0x54 601c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP6 0x58 611c6a0718SPierre Ossman #define OMAP_MMC_REG_RSP7 0x5c 621c6a0718SPierre Ossman #define OMAP_MMC_REG_IOSR 0x60 631c6a0718SPierre Ossman #define OMAP_MMC_REG_SYSC 0x64 641c6a0718SPierre Ossman #define OMAP_MMC_REG_SYSS 0x68 651c6a0718SPierre Ossman 661c6a0718SPierre Ossman #define OMAP_MMC_STAT_CARD_ERR (1 << 14) 671c6a0718SPierre Ossman #define OMAP_MMC_STAT_CARD_IRQ (1 << 13) 681c6a0718SPierre Ossman #define OMAP_MMC_STAT_OCR_BUSY (1 << 12) 691c6a0718SPierre Ossman #define OMAP_MMC_STAT_A_EMPTY (1 << 11) 701c6a0718SPierre Ossman #define OMAP_MMC_STAT_A_FULL (1 << 10) 711c6a0718SPierre Ossman #define OMAP_MMC_STAT_CMD_CRC (1 << 8) 721c6a0718SPierre Ossman #define OMAP_MMC_STAT_CMD_TOUT (1 << 7) 731c6a0718SPierre Ossman #define OMAP_MMC_STAT_DATA_CRC (1 << 6) 741c6a0718SPierre Ossman #define OMAP_MMC_STAT_DATA_TOUT (1 << 5) 751c6a0718SPierre Ossman #define OMAP_MMC_STAT_END_BUSY (1 << 4) 761c6a0718SPierre Ossman #define OMAP_MMC_STAT_END_OF_DATA (1 << 3) 771c6a0718SPierre Ossman #define OMAP_MMC_STAT_CARD_BUSY (1 << 2) 781c6a0718SPierre Ossman #define OMAP_MMC_STAT_END_OF_CMD (1 << 0) 791c6a0718SPierre Ossman 801c6a0718SPierre Ossman #define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg) 811c6a0718SPierre Ossman #define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg) 821c6a0718SPierre Ossman 831c6a0718SPierre Ossman /* 841c6a0718SPierre Ossman * Command types 851c6a0718SPierre Ossman */ 861c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_BC 0 871c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_BCR 1 881c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_AC 2 891c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_ADTC 3 901c6a0718SPierre Ossman 911c6a0718SPierre Ossman 921c6a0718SPierre Ossman #define DRIVER_NAME "mmci-omap" 931c6a0718SPierre Ossman 941c6a0718SPierre Ossman /* Specifies how often in millisecs to poll for card status changes 951c6a0718SPierre Ossman * when the cover switch is open */ 961c6a0718SPierre Ossman #define OMAP_MMC_SWITCH_POLL_DELAY 500 971c6a0718SPierre Ossman 981c6a0718SPierre Ossman static int mmc_omap_enable_poll = 1; 991c6a0718SPierre Ossman 1001c6a0718SPierre Ossman struct mmc_omap_host { 1011c6a0718SPierre Ossman int initialized; 1021c6a0718SPierre Ossman int suspended; 1031c6a0718SPierre Ossman struct mmc_request * mrq; 1041c6a0718SPierre Ossman struct mmc_command * cmd; 1051c6a0718SPierre Ossman struct mmc_data * data; 1061c6a0718SPierre Ossman struct mmc_host * mmc; 1071c6a0718SPierre Ossman struct device * dev; 1081c6a0718SPierre Ossman unsigned char id; /* 16xx chips have 2 MMC blocks */ 1091c6a0718SPierre Ossman struct clk * iclk; 1101c6a0718SPierre Ossman struct clk * fclk; 1111c6a0718SPierre Ossman struct resource *mem_res; 1121c6a0718SPierre Ossman void __iomem *virt_base; 1131c6a0718SPierre Ossman unsigned int phys_base; 1141c6a0718SPierre Ossman int irq; 1151c6a0718SPierre Ossman unsigned char bus_mode; 1161c6a0718SPierre Ossman unsigned char hw_bus_mode; 1171c6a0718SPierre Ossman 1181c6a0718SPierre Ossman unsigned int sg_len; 1191c6a0718SPierre Ossman int sg_idx; 1201c6a0718SPierre Ossman u16 * buffer; 1211c6a0718SPierre Ossman u32 buffer_bytes_left; 1221c6a0718SPierre Ossman u32 total_bytes_left; 1231c6a0718SPierre Ossman 1241c6a0718SPierre Ossman unsigned use_dma:1; 1251c6a0718SPierre Ossman unsigned brs_received:1, dma_done:1; 1261c6a0718SPierre Ossman unsigned dma_is_read:1; 1271c6a0718SPierre Ossman unsigned dma_in_use:1; 1281c6a0718SPierre Ossman int dma_ch; 1291c6a0718SPierre Ossman spinlock_t dma_lock; 1301c6a0718SPierre Ossman struct timer_list dma_timer; 1311c6a0718SPierre Ossman unsigned dma_len; 1321c6a0718SPierre Ossman 1331c6a0718SPierre Ossman short power_pin; 1341c6a0718SPierre Ossman short wp_pin; 1351c6a0718SPierre Ossman 1361c6a0718SPierre Ossman int switch_pin; 1371c6a0718SPierre Ossman struct work_struct switch_work; 1381c6a0718SPierre Ossman struct timer_list switch_timer; 1391c6a0718SPierre Ossman int switch_last_state; 1401c6a0718SPierre Ossman }; 1411c6a0718SPierre Ossman 1421c6a0718SPierre Ossman static inline int 1431c6a0718SPierre Ossman mmc_omap_cover_is_open(struct mmc_omap_host *host) 1441c6a0718SPierre Ossman { 1451c6a0718SPierre Ossman if (host->switch_pin < 0) 1461c6a0718SPierre Ossman return 0; 1471c6a0718SPierre Ossman return omap_get_gpio_datain(host->switch_pin); 1481c6a0718SPierre Ossman } 1491c6a0718SPierre Ossman 1501c6a0718SPierre Ossman static ssize_t 1511c6a0718SPierre Ossman mmc_omap_show_cover_switch(struct device *dev, 1521c6a0718SPierre Ossman struct device_attribute *attr, char *buf) 1531c6a0718SPierre Ossman { 1541c6a0718SPierre Ossman struct mmc_omap_host *host = dev_get_drvdata(dev); 1551c6a0718SPierre Ossman 1561c6a0718SPierre Ossman return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : 1571c6a0718SPierre Ossman "closed"); 1581c6a0718SPierre Ossman } 1591c6a0718SPierre Ossman 1601c6a0718SPierre Ossman static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); 1611c6a0718SPierre Ossman 1621c6a0718SPierre Ossman static ssize_t 1631c6a0718SPierre Ossman mmc_omap_show_enable_poll(struct device *dev, 1641c6a0718SPierre Ossman struct device_attribute *attr, char *buf) 1651c6a0718SPierre Ossman { 1661c6a0718SPierre Ossman return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); 1671c6a0718SPierre Ossman } 1681c6a0718SPierre Ossman 1691c6a0718SPierre Ossman static ssize_t 1701c6a0718SPierre Ossman mmc_omap_store_enable_poll(struct device *dev, 1711c6a0718SPierre Ossman struct device_attribute *attr, const char *buf, 1721c6a0718SPierre Ossman size_t size) 1731c6a0718SPierre Ossman { 1741c6a0718SPierre Ossman int enable_poll; 1751c6a0718SPierre Ossman 1761c6a0718SPierre Ossman if (sscanf(buf, "%10d", &enable_poll) != 1) 1771c6a0718SPierre Ossman return -EINVAL; 1781c6a0718SPierre Ossman 1791c6a0718SPierre Ossman if (enable_poll != mmc_omap_enable_poll) { 1801c6a0718SPierre Ossman struct mmc_omap_host *host = dev_get_drvdata(dev); 1811c6a0718SPierre Ossman 1821c6a0718SPierre Ossman mmc_omap_enable_poll = enable_poll; 1831c6a0718SPierre Ossman if (enable_poll && host->switch_pin >= 0) 1841c6a0718SPierre Ossman schedule_work(&host->switch_work); 1851c6a0718SPierre Ossman } 1861c6a0718SPierre Ossman return size; 1871c6a0718SPierre Ossman } 1881c6a0718SPierre Ossman 1891c6a0718SPierre Ossman static DEVICE_ATTR(enable_poll, 0664, 1901c6a0718SPierre Ossman mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); 1911c6a0718SPierre Ossman 1921c6a0718SPierre Ossman static void 1931c6a0718SPierre Ossman mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) 1941c6a0718SPierre Ossman { 1951c6a0718SPierre Ossman u32 cmdreg; 1961c6a0718SPierre Ossman u32 resptype; 1971c6a0718SPierre Ossman u32 cmdtype; 1981c6a0718SPierre Ossman 1991c6a0718SPierre Ossman host->cmd = cmd; 2001c6a0718SPierre Ossman 2011c6a0718SPierre Ossman resptype = 0; 2021c6a0718SPierre Ossman cmdtype = 0; 2031c6a0718SPierre Ossman 2041c6a0718SPierre Ossman /* Our hardware needs to know exact type */ 2051c6a0718SPierre Ossman switch (mmc_resp_type(cmd)) { 2061c6a0718SPierre Ossman case MMC_RSP_NONE: 2071c6a0718SPierre Ossman break; 2081c6a0718SPierre Ossman case MMC_RSP_R1: 2091c6a0718SPierre Ossman case MMC_RSP_R1B: 2101c6a0718SPierre Ossman /* resp 1, 1b, 6, 7 */ 2111c6a0718SPierre Ossman resptype = 1; 2121c6a0718SPierre Ossman break; 2131c6a0718SPierre Ossman case MMC_RSP_R2: 2141c6a0718SPierre Ossman resptype = 2; 2151c6a0718SPierre Ossman break; 2161c6a0718SPierre Ossman case MMC_RSP_R3: 2171c6a0718SPierre Ossman resptype = 3; 2181c6a0718SPierre Ossman break; 2191c6a0718SPierre Ossman default: 2201c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd)); 2211c6a0718SPierre Ossman break; 2221c6a0718SPierre Ossman } 2231c6a0718SPierre Ossman 2241c6a0718SPierre Ossman if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { 2251c6a0718SPierre Ossman cmdtype = OMAP_MMC_CMDTYPE_ADTC; 2261c6a0718SPierre Ossman } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) { 2271c6a0718SPierre Ossman cmdtype = OMAP_MMC_CMDTYPE_BC; 2281c6a0718SPierre Ossman } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) { 2291c6a0718SPierre Ossman cmdtype = OMAP_MMC_CMDTYPE_BCR; 2301c6a0718SPierre Ossman } else { 2311c6a0718SPierre Ossman cmdtype = OMAP_MMC_CMDTYPE_AC; 2321c6a0718SPierre Ossman } 2331c6a0718SPierre Ossman 2341c6a0718SPierre Ossman cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); 2351c6a0718SPierre Ossman 2361c6a0718SPierre Ossman if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) 2371c6a0718SPierre Ossman cmdreg |= 1 << 6; 2381c6a0718SPierre Ossman 2391c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_BUSY) 2401c6a0718SPierre Ossman cmdreg |= 1 << 11; 2411c6a0718SPierre Ossman 2421c6a0718SPierre Ossman if (host->data && !(host->data->flags & MMC_DATA_WRITE)) 2431c6a0718SPierre Ossman cmdreg |= 1 << 15; 2441c6a0718SPierre Ossman 2451c6a0718SPierre Ossman clk_enable(host->fclk); 2461c6a0718SPierre Ossman 2471c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CTO, 200); 2481c6a0718SPierre Ossman OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); 2491c6a0718SPierre Ossman OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); 2501c6a0718SPierre Ossman OMAP_MMC_WRITE(host, IE, 2511c6a0718SPierre Ossman OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | 2521c6a0718SPierre Ossman OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | 2531c6a0718SPierre Ossman OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | 2541c6a0718SPierre Ossman OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | 2551c6a0718SPierre Ossman OMAP_MMC_STAT_END_OF_DATA); 2561c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CMD, cmdreg); 2571c6a0718SPierre Ossman } 2581c6a0718SPierre Ossman 2591c6a0718SPierre Ossman static void 2601c6a0718SPierre Ossman mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) 2611c6a0718SPierre Ossman { 2621c6a0718SPierre Ossman if (host->dma_in_use) { 2631c6a0718SPierre Ossman enum dma_data_direction dma_data_dir; 2641c6a0718SPierre Ossman 2651c6a0718SPierre Ossman BUG_ON(host->dma_ch < 0); 2661c6a0718SPierre Ossman if (data->error != MMC_ERR_NONE) 2671c6a0718SPierre Ossman omap_stop_dma(host->dma_ch); 2681c6a0718SPierre Ossman /* Release DMA channel lazily */ 2691c6a0718SPierre Ossman mod_timer(&host->dma_timer, jiffies + HZ); 2701c6a0718SPierre Ossman if (data->flags & MMC_DATA_WRITE) 2711c6a0718SPierre Ossman dma_data_dir = DMA_TO_DEVICE; 2721c6a0718SPierre Ossman else 2731c6a0718SPierre Ossman dma_data_dir = DMA_FROM_DEVICE; 2741c6a0718SPierre Ossman dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, 2751c6a0718SPierre Ossman dma_data_dir); 2761c6a0718SPierre Ossman } 2771c6a0718SPierre Ossman host->data = NULL; 2781c6a0718SPierre Ossman host->sg_len = 0; 2791c6a0718SPierre Ossman clk_disable(host->fclk); 2801c6a0718SPierre Ossman 2811c6a0718SPierre Ossman /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing 2821c6a0718SPierre Ossman * dozens of requests until the card finishes writing data. 2831c6a0718SPierre Ossman * It'd be cheaper to just wait till an EOFB interrupt arrives... 2841c6a0718SPierre Ossman */ 2851c6a0718SPierre Ossman 2861c6a0718SPierre Ossman if (!data->stop) { 2871c6a0718SPierre Ossman host->mrq = NULL; 2881c6a0718SPierre Ossman mmc_request_done(host->mmc, data->mrq); 2891c6a0718SPierre Ossman return; 2901c6a0718SPierre Ossman } 2911c6a0718SPierre Ossman 2921c6a0718SPierre Ossman mmc_omap_start_command(host, data->stop); 2931c6a0718SPierre Ossman } 2941c6a0718SPierre Ossman 2951c6a0718SPierre Ossman static void 2961c6a0718SPierre Ossman mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) 2971c6a0718SPierre Ossman { 2981c6a0718SPierre Ossman unsigned long flags; 2991c6a0718SPierre Ossman int done; 3001c6a0718SPierre Ossman 3011c6a0718SPierre Ossman if (!host->dma_in_use) { 3021c6a0718SPierre Ossman mmc_omap_xfer_done(host, data); 3031c6a0718SPierre Ossman return; 3041c6a0718SPierre Ossman } 3051c6a0718SPierre Ossman done = 0; 3061c6a0718SPierre Ossman spin_lock_irqsave(&host->dma_lock, flags); 3071c6a0718SPierre Ossman if (host->dma_done) 3081c6a0718SPierre Ossman done = 1; 3091c6a0718SPierre Ossman else 3101c6a0718SPierre Ossman host->brs_received = 1; 3111c6a0718SPierre Ossman spin_unlock_irqrestore(&host->dma_lock, flags); 3121c6a0718SPierre Ossman if (done) 3131c6a0718SPierre Ossman mmc_omap_xfer_done(host, data); 3141c6a0718SPierre Ossman } 3151c6a0718SPierre Ossman 3161c6a0718SPierre Ossman static void 3171c6a0718SPierre Ossman mmc_omap_dma_timer(unsigned long data) 3181c6a0718SPierre Ossman { 3191c6a0718SPierre Ossman struct mmc_omap_host *host = (struct mmc_omap_host *) data; 3201c6a0718SPierre Ossman 3211c6a0718SPierre Ossman BUG_ON(host->dma_ch < 0); 3221c6a0718SPierre Ossman omap_free_dma(host->dma_ch); 3231c6a0718SPierre Ossman host->dma_ch = -1; 3241c6a0718SPierre Ossman } 3251c6a0718SPierre Ossman 3261c6a0718SPierre Ossman static void 3271c6a0718SPierre Ossman mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data) 3281c6a0718SPierre Ossman { 3291c6a0718SPierre Ossman unsigned long flags; 3301c6a0718SPierre Ossman int done; 3311c6a0718SPierre Ossman 3321c6a0718SPierre Ossman done = 0; 3331c6a0718SPierre Ossman spin_lock_irqsave(&host->dma_lock, flags); 3341c6a0718SPierre Ossman if (host->brs_received) 3351c6a0718SPierre Ossman done = 1; 3361c6a0718SPierre Ossman else 3371c6a0718SPierre Ossman host->dma_done = 1; 3381c6a0718SPierre Ossman spin_unlock_irqrestore(&host->dma_lock, flags); 3391c6a0718SPierre Ossman if (done) 3401c6a0718SPierre Ossman mmc_omap_xfer_done(host, data); 3411c6a0718SPierre Ossman } 3421c6a0718SPierre Ossman 3431c6a0718SPierre Ossman static void 3441c6a0718SPierre Ossman mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) 3451c6a0718SPierre Ossman { 3461c6a0718SPierre Ossman host->cmd = NULL; 3471c6a0718SPierre Ossman 3481c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_PRESENT) { 3491c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_136) { 3501c6a0718SPierre Ossman /* response type 2 */ 3511c6a0718SPierre Ossman cmd->resp[3] = 3521c6a0718SPierre Ossman OMAP_MMC_READ(host, RSP0) | 3531c6a0718SPierre Ossman (OMAP_MMC_READ(host, RSP1) << 16); 3541c6a0718SPierre Ossman cmd->resp[2] = 3551c6a0718SPierre Ossman OMAP_MMC_READ(host, RSP2) | 3561c6a0718SPierre Ossman (OMAP_MMC_READ(host, RSP3) << 16); 3571c6a0718SPierre Ossman cmd->resp[1] = 3581c6a0718SPierre Ossman OMAP_MMC_READ(host, RSP4) | 3591c6a0718SPierre Ossman (OMAP_MMC_READ(host, RSP5) << 16); 3601c6a0718SPierre Ossman cmd->resp[0] = 3611c6a0718SPierre Ossman OMAP_MMC_READ(host, RSP6) | 3621c6a0718SPierre Ossman (OMAP_MMC_READ(host, RSP7) << 16); 3631c6a0718SPierre Ossman } else { 3641c6a0718SPierre Ossman /* response types 1, 1b, 3, 4, 5, 6 */ 3651c6a0718SPierre Ossman cmd->resp[0] = 3661c6a0718SPierre Ossman OMAP_MMC_READ(host, RSP6) | 3671c6a0718SPierre Ossman (OMAP_MMC_READ(host, RSP7) << 16); 3681c6a0718SPierre Ossman } 3691c6a0718SPierre Ossman } 3701c6a0718SPierre Ossman 3711c6a0718SPierre Ossman if (host->data == NULL || cmd->error != MMC_ERR_NONE) { 3721c6a0718SPierre Ossman host->mrq = NULL; 3731c6a0718SPierre Ossman clk_disable(host->fclk); 3741c6a0718SPierre Ossman mmc_request_done(host->mmc, cmd->mrq); 3751c6a0718SPierre Ossman } 3761c6a0718SPierre Ossman } 3771c6a0718SPierre Ossman 3781c6a0718SPierre Ossman /* PIO only */ 3791c6a0718SPierre Ossman static void 3801c6a0718SPierre Ossman mmc_omap_sg_to_buf(struct mmc_omap_host *host) 3811c6a0718SPierre Ossman { 3821c6a0718SPierre Ossman struct scatterlist *sg; 3831c6a0718SPierre Ossman 3841c6a0718SPierre Ossman sg = host->data->sg + host->sg_idx; 3851c6a0718SPierre Ossman host->buffer_bytes_left = sg->length; 3861c6a0718SPierre Ossman host->buffer = page_address(sg->page) + sg->offset; 3871c6a0718SPierre Ossman if (host->buffer_bytes_left > host->total_bytes_left) 3881c6a0718SPierre Ossman host->buffer_bytes_left = host->total_bytes_left; 3891c6a0718SPierre Ossman } 3901c6a0718SPierre Ossman 3911c6a0718SPierre Ossman /* PIO only */ 3921c6a0718SPierre Ossman static void 3931c6a0718SPierre Ossman mmc_omap_xfer_data(struct mmc_omap_host *host, int write) 3941c6a0718SPierre Ossman { 3951c6a0718SPierre Ossman int n; 3961c6a0718SPierre Ossman 3971c6a0718SPierre Ossman if (host->buffer_bytes_left == 0) { 3981c6a0718SPierre Ossman host->sg_idx++; 3991c6a0718SPierre Ossman BUG_ON(host->sg_idx == host->sg_len); 4001c6a0718SPierre Ossman mmc_omap_sg_to_buf(host); 4011c6a0718SPierre Ossman } 4021c6a0718SPierre Ossman n = 64; 4031c6a0718SPierre Ossman if (n > host->buffer_bytes_left) 4041c6a0718SPierre Ossman n = host->buffer_bytes_left; 4051c6a0718SPierre Ossman host->buffer_bytes_left -= n; 4061c6a0718SPierre Ossman host->total_bytes_left -= n; 4071c6a0718SPierre Ossman host->data->bytes_xfered += n; 4081c6a0718SPierre Ossman 4091c6a0718SPierre Ossman if (write) { 4101c6a0718SPierre Ossman __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); 4111c6a0718SPierre Ossman } else { 4121c6a0718SPierre Ossman __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); 4131c6a0718SPierre Ossman } 4141c6a0718SPierre Ossman } 4151c6a0718SPierre Ossman 4161c6a0718SPierre Ossman static inline void mmc_omap_report_irq(u16 status) 4171c6a0718SPierre Ossman { 4181c6a0718SPierre Ossman static const char *mmc_omap_status_bits[] = { 4191c6a0718SPierre Ossman "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO", 4201c6a0718SPierre Ossman "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR" 4211c6a0718SPierre Ossman }; 4221c6a0718SPierre Ossman int i, c = 0; 4231c6a0718SPierre Ossman 4241c6a0718SPierre Ossman for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) 4251c6a0718SPierre Ossman if (status & (1 << i)) { 4261c6a0718SPierre Ossman if (c) 4271c6a0718SPierre Ossman printk(" "); 4281c6a0718SPierre Ossman printk("%s", mmc_omap_status_bits[i]); 4291c6a0718SPierre Ossman c++; 4301c6a0718SPierre Ossman } 4311c6a0718SPierre Ossman } 4321c6a0718SPierre Ossman 4331c6a0718SPierre Ossman static irqreturn_t mmc_omap_irq(int irq, void *dev_id) 4341c6a0718SPierre Ossman { 4351c6a0718SPierre Ossman struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; 4361c6a0718SPierre Ossman u16 status; 4371c6a0718SPierre Ossman int end_command; 4381c6a0718SPierre Ossman int end_transfer; 4391c6a0718SPierre Ossman int transfer_error; 4401c6a0718SPierre Ossman 4411c6a0718SPierre Ossman if (host->cmd == NULL && host->data == NULL) { 4421c6a0718SPierre Ossman status = OMAP_MMC_READ(host, STAT); 4431c6a0718SPierre Ossman dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); 4441c6a0718SPierre Ossman if (status != 0) { 4451c6a0718SPierre Ossman OMAP_MMC_WRITE(host, STAT, status); 4461c6a0718SPierre Ossman OMAP_MMC_WRITE(host, IE, 0); 4471c6a0718SPierre Ossman } 4481c6a0718SPierre Ossman return IRQ_HANDLED; 4491c6a0718SPierre Ossman } 4501c6a0718SPierre Ossman 4511c6a0718SPierre Ossman end_command = 0; 4521c6a0718SPierre Ossman end_transfer = 0; 4531c6a0718SPierre Ossman transfer_error = 0; 4541c6a0718SPierre Ossman 4551c6a0718SPierre Ossman while ((status = OMAP_MMC_READ(host, STAT)) != 0) { 4561c6a0718SPierre Ossman OMAP_MMC_WRITE(host, STAT, status); 4571c6a0718SPierre Ossman #ifdef CONFIG_MMC_DEBUG 4581c6a0718SPierre Ossman dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", 4591c6a0718SPierre Ossman status, host->cmd != NULL ? host->cmd->opcode : -1); 4601c6a0718SPierre Ossman mmc_omap_report_irq(status); 4611c6a0718SPierre Ossman printk("\n"); 4621c6a0718SPierre Ossman #endif 4631c6a0718SPierre Ossman if (host->total_bytes_left) { 4641c6a0718SPierre Ossman if ((status & OMAP_MMC_STAT_A_FULL) || 4651c6a0718SPierre Ossman (status & OMAP_MMC_STAT_END_OF_DATA)) 4661c6a0718SPierre Ossman mmc_omap_xfer_data(host, 0); 4671c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_A_EMPTY) 4681c6a0718SPierre Ossman mmc_omap_xfer_data(host, 1); 4691c6a0718SPierre Ossman } 4701c6a0718SPierre Ossman 4711c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_END_OF_DATA) { 4721c6a0718SPierre Ossman end_transfer = 1; 4731c6a0718SPierre Ossman } 4741c6a0718SPierre Ossman 4751c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_DATA_TOUT) { 4761c6a0718SPierre Ossman dev_dbg(mmc_dev(host->mmc), "data timeout\n"); 4771c6a0718SPierre Ossman if (host->data) { 4781c6a0718SPierre Ossman host->data->error |= MMC_ERR_TIMEOUT; 4791c6a0718SPierre Ossman transfer_error = 1; 4801c6a0718SPierre Ossman } 4811c6a0718SPierre Ossman } 4821c6a0718SPierre Ossman 4831c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_DATA_CRC) { 4841c6a0718SPierre Ossman if (host->data) { 4851c6a0718SPierre Ossman host->data->error |= MMC_ERR_BADCRC; 4861c6a0718SPierre Ossman dev_dbg(mmc_dev(host->mmc), 4871c6a0718SPierre Ossman "data CRC error, bytes left %d\n", 4881c6a0718SPierre Ossman host->total_bytes_left); 4891c6a0718SPierre Ossman transfer_error = 1; 4901c6a0718SPierre Ossman } else { 4911c6a0718SPierre Ossman dev_dbg(mmc_dev(host->mmc), "data CRC error\n"); 4921c6a0718SPierre Ossman } 4931c6a0718SPierre Ossman } 4941c6a0718SPierre Ossman 4951c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_CMD_TOUT) { 4961c6a0718SPierre Ossman /* Timeouts are routine with some commands */ 4971c6a0718SPierre Ossman if (host->cmd) { 4981c6a0718SPierre Ossman if (host->cmd->opcode != MMC_ALL_SEND_CID && 4991c6a0718SPierre Ossman host->cmd->opcode != 5001c6a0718SPierre Ossman MMC_SEND_OP_COND && 5011c6a0718SPierre Ossman host->cmd->opcode != 5021c6a0718SPierre Ossman MMC_APP_CMD && 5031c6a0718SPierre Ossman !mmc_omap_cover_is_open(host)) 5041c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), 5051c6a0718SPierre Ossman "command timeout, CMD %d\n", 5061c6a0718SPierre Ossman host->cmd->opcode); 5071c6a0718SPierre Ossman host->cmd->error = MMC_ERR_TIMEOUT; 5081c6a0718SPierre Ossman end_command = 1; 5091c6a0718SPierre Ossman } 5101c6a0718SPierre Ossman } 5111c6a0718SPierre Ossman 5121c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_CMD_CRC) { 5131c6a0718SPierre Ossman if (host->cmd) { 5141c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), 5151c6a0718SPierre Ossman "command CRC error (CMD%d, arg 0x%08x)\n", 5161c6a0718SPierre Ossman host->cmd->opcode, host->cmd->arg); 5171c6a0718SPierre Ossman host->cmd->error = MMC_ERR_BADCRC; 5181c6a0718SPierre Ossman end_command = 1; 5191c6a0718SPierre Ossman } else 5201c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), 5211c6a0718SPierre Ossman "command CRC error without cmd?\n"); 5221c6a0718SPierre Ossman } 5231c6a0718SPierre Ossman 5241c6a0718SPierre Ossman if (status & OMAP_MMC_STAT_CARD_ERR) { 5251c6a0718SPierre Ossman if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) { 5261c6a0718SPierre Ossman u32 response = OMAP_MMC_READ(host, RSP6) 5271c6a0718SPierre Ossman | (OMAP_MMC_READ(host, RSP7) << 16); 5281c6a0718SPierre Ossman /* STOP sometimes sets must-ignore bits */ 5291c6a0718SPierre Ossman if (!(response & (R1_CC_ERROR 5301c6a0718SPierre Ossman | R1_ILLEGAL_COMMAND 5311c6a0718SPierre Ossman | R1_COM_CRC_ERROR))) { 5321c6a0718SPierre Ossman end_command = 1; 5331c6a0718SPierre Ossman continue; 5341c6a0718SPierre Ossman } 5351c6a0718SPierre Ossman } 5361c6a0718SPierre Ossman 5371c6a0718SPierre Ossman dev_dbg(mmc_dev(host->mmc), "card status error (CMD%d)\n", 5381c6a0718SPierre Ossman host->cmd->opcode); 5391c6a0718SPierre Ossman if (host->cmd) { 5401c6a0718SPierre Ossman host->cmd->error = MMC_ERR_FAILED; 5411c6a0718SPierre Ossman end_command = 1; 5421c6a0718SPierre Ossman } 5431c6a0718SPierre Ossman if (host->data) { 5441c6a0718SPierre Ossman host->data->error = MMC_ERR_FAILED; 5451c6a0718SPierre Ossman transfer_error = 1; 5461c6a0718SPierre Ossman } 5471c6a0718SPierre Ossman } 5481c6a0718SPierre Ossman 5491c6a0718SPierre Ossman /* 5501c6a0718SPierre Ossman * NOTE: On 1610 the END_OF_CMD may come too early when 5511c6a0718SPierre Ossman * starting a write 5521c6a0718SPierre Ossman */ 5531c6a0718SPierre Ossman if ((status & OMAP_MMC_STAT_END_OF_CMD) && 5541c6a0718SPierre Ossman (!(status & OMAP_MMC_STAT_A_EMPTY))) { 5551c6a0718SPierre Ossman end_command = 1; 5561c6a0718SPierre Ossman } 5571c6a0718SPierre Ossman } 5581c6a0718SPierre Ossman 5591c6a0718SPierre Ossman if (end_command) { 5601c6a0718SPierre Ossman mmc_omap_cmd_done(host, host->cmd); 5611c6a0718SPierre Ossman } 5621c6a0718SPierre Ossman if (transfer_error) 5631c6a0718SPierre Ossman mmc_omap_xfer_done(host, host->data); 5641c6a0718SPierre Ossman else if (end_transfer) 5651c6a0718SPierre Ossman mmc_omap_end_of_data(host, host->data); 5661c6a0718SPierre Ossman 5671c6a0718SPierre Ossman return IRQ_HANDLED; 5681c6a0718SPierre Ossman } 5691c6a0718SPierre Ossman 5701c6a0718SPierre Ossman static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) 5711c6a0718SPierre Ossman { 5721c6a0718SPierre Ossman struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; 5731c6a0718SPierre Ossman 5741c6a0718SPierre Ossman schedule_work(&host->switch_work); 5751c6a0718SPierre Ossman 5761c6a0718SPierre Ossman return IRQ_HANDLED; 5771c6a0718SPierre Ossman } 5781c6a0718SPierre Ossman 5791c6a0718SPierre Ossman static void mmc_omap_switch_timer(unsigned long arg) 5801c6a0718SPierre Ossman { 5811c6a0718SPierre Ossman struct mmc_omap_host *host = (struct mmc_omap_host *) arg; 5821c6a0718SPierre Ossman 5831c6a0718SPierre Ossman schedule_work(&host->switch_work); 5841c6a0718SPierre Ossman } 5851c6a0718SPierre Ossman 5861c6a0718SPierre Ossman static void mmc_omap_switch_handler(struct work_struct *work) 5871c6a0718SPierre Ossman { 5881c6a0718SPierre Ossman struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); 5891c6a0718SPierre Ossman struct mmc_card *card; 5901c6a0718SPierre Ossman static int complained = 0; 5911c6a0718SPierre Ossman int cards = 0, cover_open; 5921c6a0718SPierre Ossman 5931c6a0718SPierre Ossman if (host->switch_pin == -1) 5941c6a0718SPierre Ossman return; 5951c6a0718SPierre Ossman cover_open = mmc_omap_cover_is_open(host); 5961c6a0718SPierre Ossman if (cover_open != host->switch_last_state) { 5971c6a0718SPierre Ossman kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); 5981c6a0718SPierre Ossman host->switch_last_state = cover_open; 5991c6a0718SPierre Ossman } 6001c6a0718SPierre Ossman mmc_detect_change(host->mmc, 0); 6011c6a0718SPierre Ossman list_for_each_entry(card, &host->mmc->cards, node) { 6021c6a0718SPierre Ossman if (mmc_card_present(card)) 6031c6a0718SPierre Ossman cards++; 6041c6a0718SPierre Ossman } 6051c6a0718SPierre Ossman if (mmc_omap_cover_is_open(host)) { 6061c6a0718SPierre Ossman if (!complained) { 6073647afceSArnaud Patard dev_info(mmc_dev(host->mmc), "cover is open\n"); 6081c6a0718SPierre Ossman complained = 1; 6091c6a0718SPierre Ossman } 6101c6a0718SPierre Ossman if (mmc_omap_enable_poll) 6111c6a0718SPierre Ossman mod_timer(&host->switch_timer, jiffies + 6121c6a0718SPierre Ossman msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); 6131c6a0718SPierre Ossman } else { 6141c6a0718SPierre Ossman complained = 0; 6151c6a0718SPierre Ossman } 6161c6a0718SPierre Ossman } 6171c6a0718SPierre Ossman 6181c6a0718SPierre Ossman /* Prepare to transfer the next segment of a scatterlist */ 6191c6a0718SPierre Ossman static void 6201c6a0718SPierre Ossman mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) 6211c6a0718SPierre Ossman { 6221c6a0718SPierre Ossman int dma_ch = host->dma_ch; 6231c6a0718SPierre Ossman unsigned long data_addr; 6241c6a0718SPierre Ossman u16 buf, frame; 6251c6a0718SPierre Ossman u32 count; 6261c6a0718SPierre Ossman struct scatterlist *sg = &data->sg[host->sg_idx]; 6271c6a0718SPierre Ossman int src_port = 0; 6281c6a0718SPierre Ossman int dst_port = 0; 6291c6a0718SPierre Ossman int sync_dev = 0; 6301c6a0718SPierre Ossman 6311c6a0718SPierre Ossman data_addr = host->phys_base + OMAP_MMC_REG_DATA; 6321c6a0718SPierre Ossman frame = data->blksz; 6331c6a0718SPierre Ossman count = sg_dma_len(sg); 6341c6a0718SPierre Ossman 6351c6a0718SPierre Ossman if ((data->blocks == 1) && (count > data->blksz)) 6361c6a0718SPierre Ossman count = frame; 6371c6a0718SPierre Ossman 6381c6a0718SPierre Ossman host->dma_len = count; 6391c6a0718SPierre Ossman 6401c6a0718SPierre Ossman /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. 6411c6a0718SPierre Ossman * Use 16 or 32 word frames when the blocksize is at least that large. 6421c6a0718SPierre Ossman * Blocksize is usually 512 bytes; but not for some SD reads. 6431c6a0718SPierre Ossman */ 6441c6a0718SPierre Ossman if (cpu_is_omap15xx() && frame > 32) 6451c6a0718SPierre Ossman frame = 32; 6461c6a0718SPierre Ossman else if (frame > 64) 6471c6a0718SPierre Ossman frame = 64; 6481c6a0718SPierre Ossman count /= frame; 6491c6a0718SPierre Ossman frame >>= 1; 6501c6a0718SPierre Ossman 6511c6a0718SPierre Ossman if (!(data->flags & MMC_DATA_WRITE)) { 6521c6a0718SPierre Ossman buf = 0x800f | ((frame - 1) << 8); 6531c6a0718SPierre Ossman 6541c6a0718SPierre Ossman if (cpu_class_is_omap1()) { 6551c6a0718SPierre Ossman src_port = OMAP_DMA_PORT_TIPB; 6561c6a0718SPierre Ossman dst_port = OMAP_DMA_PORT_EMIFF; 6571c6a0718SPierre Ossman } 6581c6a0718SPierre Ossman if (cpu_is_omap24xx()) 6591c6a0718SPierre Ossman sync_dev = OMAP24XX_DMA_MMC1_RX; 6601c6a0718SPierre Ossman 6611c6a0718SPierre Ossman omap_set_dma_src_params(dma_ch, src_port, 6621c6a0718SPierre Ossman OMAP_DMA_AMODE_CONSTANT, 6631c6a0718SPierre Ossman data_addr, 0, 0); 6641c6a0718SPierre Ossman omap_set_dma_dest_params(dma_ch, dst_port, 6651c6a0718SPierre Ossman OMAP_DMA_AMODE_POST_INC, 6661c6a0718SPierre Ossman sg_dma_address(sg), 0, 0); 6671c6a0718SPierre Ossman omap_set_dma_dest_data_pack(dma_ch, 1); 6681c6a0718SPierre Ossman omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); 6691c6a0718SPierre Ossman } else { 6701c6a0718SPierre Ossman buf = 0x0f80 | ((frame - 1) << 0); 6711c6a0718SPierre Ossman 6721c6a0718SPierre Ossman if (cpu_class_is_omap1()) { 6731c6a0718SPierre Ossman src_port = OMAP_DMA_PORT_EMIFF; 6741c6a0718SPierre Ossman dst_port = OMAP_DMA_PORT_TIPB; 6751c6a0718SPierre Ossman } 6761c6a0718SPierre Ossman if (cpu_is_omap24xx()) 6771c6a0718SPierre Ossman sync_dev = OMAP24XX_DMA_MMC1_TX; 6781c6a0718SPierre Ossman 6791c6a0718SPierre Ossman omap_set_dma_dest_params(dma_ch, dst_port, 6801c6a0718SPierre Ossman OMAP_DMA_AMODE_CONSTANT, 6811c6a0718SPierre Ossman data_addr, 0, 0); 6821c6a0718SPierre Ossman omap_set_dma_src_params(dma_ch, src_port, 6831c6a0718SPierre Ossman OMAP_DMA_AMODE_POST_INC, 6841c6a0718SPierre Ossman sg_dma_address(sg), 0, 0); 6851c6a0718SPierre Ossman omap_set_dma_src_data_pack(dma_ch, 1); 6861c6a0718SPierre Ossman omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); 6871c6a0718SPierre Ossman } 6881c6a0718SPierre Ossman 6891c6a0718SPierre Ossman /* Max limit for DMA frame count is 0xffff */ 6901c6a0718SPierre Ossman BUG_ON(count > 0xffff); 6911c6a0718SPierre Ossman 6921c6a0718SPierre Ossman OMAP_MMC_WRITE(host, BUF, buf); 6931c6a0718SPierre Ossman omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, 6941c6a0718SPierre Ossman frame, count, OMAP_DMA_SYNC_FRAME, 6951c6a0718SPierre Ossman sync_dev, 0); 6961c6a0718SPierre Ossman } 6971c6a0718SPierre Ossman 6981c6a0718SPierre Ossman /* A scatterlist segment completed */ 6991c6a0718SPierre Ossman static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) 7001c6a0718SPierre Ossman { 7011c6a0718SPierre Ossman struct mmc_omap_host *host = (struct mmc_omap_host *) data; 7021c6a0718SPierre Ossman struct mmc_data *mmcdat = host->data; 7031c6a0718SPierre Ossman 7041c6a0718SPierre Ossman if (unlikely(host->dma_ch < 0)) { 7051c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), 7061c6a0718SPierre Ossman "DMA callback while DMA not enabled\n"); 7071c6a0718SPierre Ossman return; 7081c6a0718SPierre Ossman } 7091c6a0718SPierre Ossman /* FIXME: We really should do something to _handle_ the errors */ 7101c6a0718SPierre Ossman if (ch_status & OMAP1_DMA_TOUT_IRQ) { 7111c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc),"DMA timeout\n"); 7121c6a0718SPierre Ossman return; 7131c6a0718SPierre Ossman } 7141c6a0718SPierre Ossman if (ch_status & OMAP_DMA_DROP_IRQ) { 7151c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), "DMA sync error\n"); 7161c6a0718SPierre Ossman return; 7171c6a0718SPierre Ossman } 7181c6a0718SPierre Ossman if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { 7191c6a0718SPierre Ossman return; 7201c6a0718SPierre Ossman } 7211c6a0718SPierre Ossman mmcdat->bytes_xfered += host->dma_len; 7221c6a0718SPierre Ossman host->sg_idx++; 7231c6a0718SPierre Ossman if (host->sg_idx < host->sg_len) { 7241c6a0718SPierre Ossman mmc_omap_prepare_dma(host, host->data); 7251c6a0718SPierre Ossman omap_start_dma(host->dma_ch); 7261c6a0718SPierre Ossman } else 7271c6a0718SPierre Ossman mmc_omap_dma_done(host, host->data); 7281c6a0718SPierre Ossman } 7291c6a0718SPierre Ossman 7301c6a0718SPierre Ossman static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data) 7311c6a0718SPierre Ossman { 7321c6a0718SPierre Ossman const char *dev_name; 7331c6a0718SPierre Ossman int sync_dev, dma_ch, is_read, r; 7341c6a0718SPierre Ossman 7351c6a0718SPierre Ossman is_read = !(data->flags & MMC_DATA_WRITE); 7361c6a0718SPierre Ossman del_timer_sync(&host->dma_timer); 7371c6a0718SPierre Ossman if (host->dma_ch >= 0) { 7381c6a0718SPierre Ossman if (is_read == host->dma_is_read) 7391c6a0718SPierre Ossman return 0; 7401c6a0718SPierre Ossman omap_free_dma(host->dma_ch); 7411c6a0718SPierre Ossman host->dma_ch = -1; 7421c6a0718SPierre Ossman } 7431c6a0718SPierre Ossman 7441c6a0718SPierre Ossman if (is_read) { 7451c6a0718SPierre Ossman if (host->id == 1) { 7461c6a0718SPierre Ossman sync_dev = OMAP_DMA_MMC_RX; 7471c6a0718SPierre Ossman dev_name = "MMC1 read"; 7481c6a0718SPierre Ossman } else { 7491c6a0718SPierre Ossman sync_dev = OMAP_DMA_MMC2_RX; 7501c6a0718SPierre Ossman dev_name = "MMC2 read"; 7511c6a0718SPierre Ossman } 7521c6a0718SPierre Ossman } else { 7531c6a0718SPierre Ossman if (host->id == 1) { 7541c6a0718SPierre Ossman sync_dev = OMAP_DMA_MMC_TX; 7551c6a0718SPierre Ossman dev_name = "MMC1 write"; 7561c6a0718SPierre Ossman } else { 7571c6a0718SPierre Ossman sync_dev = OMAP_DMA_MMC2_TX; 7581c6a0718SPierre Ossman dev_name = "MMC2 write"; 7591c6a0718SPierre Ossman } 7601c6a0718SPierre Ossman } 7611c6a0718SPierre Ossman r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, 7621c6a0718SPierre Ossman host, &dma_ch); 7631c6a0718SPierre Ossman if (r != 0) { 7641c6a0718SPierre Ossman dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r); 7651c6a0718SPierre Ossman return r; 7661c6a0718SPierre Ossman } 7671c6a0718SPierre Ossman host->dma_ch = dma_ch; 7681c6a0718SPierre Ossman host->dma_is_read = is_read; 7691c6a0718SPierre Ossman 7701c6a0718SPierre Ossman return 0; 7711c6a0718SPierre Ossman } 7721c6a0718SPierre Ossman 7731c6a0718SPierre Ossman static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) 7741c6a0718SPierre Ossman { 7751c6a0718SPierre Ossman u16 reg; 7761c6a0718SPierre Ossman 7771c6a0718SPierre Ossman reg = OMAP_MMC_READ(host, SDIO); 7781c6a0718SPierre Ossman reg &= ~(1 << 5); 7791c6a0718SPierre Ossman OMAP_MMC_WRITE(host, SDIO, reg); 7801c6a0718SPierre Ossman /* Set maximum timeout */ 7811c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CTO, 0xff); 7821c6a0718SPierre Ossman } 7831c6a0718SPierre Ossman 7841c6a0718SPierre Ossman static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) 7851c6a0718SPierre Ossman { 7861c6a0718SPierre Ossman int timeout; 7871c6a0718SPierre Ossman u16 reg; 7881c6a0718SPierre Ossman 7891c6a0718SPierre Ossman /* Convert ns to clock cycles by assuming 20MHz frequency 7901c6a0718SPierre Ossman * 1 cycle at 20MHz = 500 ns 7911c6a0718SPierre Ossman */ 7921c6a0718SPierre Ossman timeout = req->data->timeout_clks + req->data->timeout_ns / 500; 7931c6a0718SPierre Ossman 7941c6a0718SPierre Ossman /* Check if we need to use timeout multiplier register */ 7951c6a0718SPierre Ossman reg = OMAP_MMC_READ(host, SDIO); 7961c6a0718SPierre Ossman if (timeout > 0xffff) { 7971c6a0718SPierre Ossman reg |= (1 << 5); 7981c6a0718SPierre Ossman timeout /= 1024; 7991c6a0718SPierre Ossman } else 8001c6a0718SPierre Ossman reg &= ~(1 << 5); 8011c6a0718SPierre Ossman OMAP_MMC_WRITE(host, SDIO, reg); 8021c6a0718SPierre Ossman OMAP_MMC_WRITE(host, DTO, timeout); 8031c6a0718SPierre Ossman } 8041c6a0718SPierre Ossman 8051c6a0718SPierre Ossman static void 8061c6a0718SPierre Ossman mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) 8071c6a0718SPierre Ossman { 8081c6a0718SPierre Ossman struct mmc_data *data = req->data; 8091c6a0718SPierre Ossman int i, use_dma, block_size; 8101c6a0718SPierre Ossman unsigned sg_len; 8111c6a0718SPierre Ossman 8121c6a0718SPierre Ossman host->data = data; 8131c6a0718SPierre Ossman if (data == NULL) { 8141c6a0718SPierre Ossman OMAP_MMC_WRITE(host, BLEN, 0); 8151c6a0718SPierre Ossman OMAP_MMC_WRITE(host, NBLK, 0); 8161c6a0718SPierre Ossman OMAP_MMC_WRITE(host, BUF, 0); 8171c6a0718SPierre Ossman host->dma_in_use = 0; 8181c6a0718SPierre Ossman set_cmd_timeout(host, req); 8191c6a0718SPierre Ossman return; 8201c6a0718SPierre Ossman } 8211c6a0718SPierre Ossman 8221c6a0718SPierre Ossman block_size = data->blksz; 8231c6a0718SPierre Ossman 8241c6a0718SPierre Ossman OMAP_MMC_WRITE(host, NBLK, data->blocks - 1); 8251c6a0718SPierre Ossman OMAP_MMC_WRITE(host, BLEN, block_size - 1); 8261c6a0718SPierre Ossman set_data_timeout(host, req); 8271c6a0718SPierre Ossman 8281c6a0718SPierre Ossman /* cope with calling layer confusion; it issues "single 8291c6a0718SPierre Ossman * block" writes using multi-block scatterlists. 8301c6a0718SPierre Ossman */ 8311c6a0718SPierre Ossman sg_len = (data->blocks == 1) ? 1 : data->sg_len; 8321c6a0718SPierre Ossman 8331c6a0718SPierre Ossman /* Only do DMA for entire blocks */ 8341c6a0718SPierre Ossman use_dma = host->use_dma; 8351c6a0718SPierre Ossman if (use_dma) { 8361c6a0718SPierre Ossman for (i = 0; i < sg_len; i++) { 8371c6a0718SPierre Ossman if ((data->sg[i].length % block_size) != 0) { 8381c6a0718SPierre Ossman use_dma = 0; 8391c6a0718SPierre Ossman break; 8401c6a0718SPierre Ossman } 8411c6a0718SPierre Ossman } 8421c6a0718SPierre Ossman } 8431c6a0718SPierre Ossman 8441c6a0718SPierre Ossman host->sg_idx = 0; 8451c6a0718SPierre Ossman if (use_dma) { 8461c6a0718SPierre Ossman if (mmc_omap_get_dma_channel(host, data) == 0) { 8471c6a0718SPierre Ossman enum dma_data_direction dma_data_dir; 8481c6a0718SPierre Ossman 8491c6a0718SPierre Ossman if (data->flags & MMC_DATA_WRITE) 8501c6a0718SPierre Ossman dma_data_dir = DMA_TO_DEVICE; 8511c6a0718SPierre Ossman else 8521c6a0718SPierre Ossman dma_data_dir = DMA_FROM_DEVICE; 8531c6a0718SPierre Ossman 8541c6a0718SPierre Ossman host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, 8551c6a0718SPierre Ossman sg_len, dma_data_dir); 8561c6a0718SPierre Ossman host->total_bytes_left = 0; 8571c6a0718SPierre Ossman mmc_omap_prepare_dma(host, req->data); 8581c6a0718SPierre Ossman host->brs_received = 0; 8591c6a0718SPierre Ossman host->dma_done = 0; 8601c6a0718SPierre Ossman host->dma_in_use = 1; 8611c6a0718SPierre Ossman } else 8621c6a0718SPierre Ossman use_dma = 0; 8631c6a0718SPierre Ossman } 8641c6a0718SPierre Ossman 8651c6a0718SPierre Ossman /* Revert to PIO? */ 8661c6a0718SPierre Ossman if (!use_dma) { 8671c6a0718SPierre Ossman OMAP_MMC_WRITE(host, BUF, 0x1f1f); 8681c6a0718SPierre Ossman host->total_bytes_left = data->blocks * block_size; 8691c6a0718SPierre Ossman host->sg_len = sg_len; 8701c6a0718SPierre Ossman mmc_omap_sg_to_buf(host); 8711c6a0718SPierre Ossman host->dma_in_use = 0; 8721c6a0718SPierre Ossman } 8731c6a0718SPierre Ossman } 8741c6a0718SPierre Ossman 8751c6a0718SPierre Ossman static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) 8761c6a0718SPierre Ossman { 8771c6a0718SPierre Ossman struct mmc_omap_host *host = mmc_priv(mmc); 8781c6a0718SPierre Ossman 8791c6a0718SPierre Ossman WARN_ON(host->mrq != NULL); 8801c6a0718SPierre Ossman 8811c6a0718SPierre Ossman host->mrq = req; 8821c6a0718SPierre Ossman 8831c6a0718SPierre Ossman /* only touch fifo AFTER the controller readies it */ 8841c6a0718SPierre Ossman mmc_omap_prepare_data(host, req); 8851c6a0718SPierre Ossman mmc_omap_start_command(host, req->cmd); 8861c6a0718SPierre Ossman if (host->dma_in_use) 8871c6a0718SPierre Ossman omap_start_dma(host->dma_ch); 8881c6a0718SPierre Ossman } 8891c6a0718SPierre Ossman 8901c6a0718SPierre Ossman static void innovator_fpga_socket_power(int on) 8911c6a0718SPierre Ossman { 8921c6a0718SPierre Ossman #if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) 8931c6a0718SPierre Ossman if (on) { 8941c6a0718SPierre Ossman fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), 8951c6a0718SPierre Ossman OMAP1510_FPGA_POWER); 8961c6a0718SPierre Ossman } else { 8971c6a0718SPierre Ossman fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), 8981c6a0718SPierre Ossman OMAP1510_FPGA_POWER); 8991c6a0718SPierre Ossman } 9001c6a0718SPierre Ossman #endif 9011c6a0718SPierre Ossman } 9021c6a0718SPierre Ossman 9031c6a0718SPierre Ossman /* 9041c6a0718SPierre Ossman * Turn the socket power on/off. Innovator uses FPGA, most boards 9051c6a0718SPierre Ossman * probably use GPIO. 9061c6a0718SPierre Ossman */ 9071c6a0718SPierre Ossman static void mmc_omap_power(struct mmc_omap_host *host, int on) 9081c6a0718SPierre Ossman { 9091c6a0718SPierre Ossman if (on) { 9101c6a0718SPierre Ossman if (machine_is_omap_innovator()) 9111c6a0718SPierre Ossman innovator_fpga_socket_power(1); 9121c6a0718SPierre Ossman else if (machine_is_omap_h2()) 9131c6a0718SPierre Ossman tps65010_set_gpio_out_value(GPIO3, HIGH); 9141c6a0718SPierre Ossman else if (machine_is_omap_h3()) 9151c6a0718SPierre Ossman /* GPIO 4 of TPS65010 sends SD_EN signal */ 9161c6a0718SPierre Ossman tps65010_set_gpio_out_value(GPIO4, HIGH); 9171c6a0718SPierre Ossman else if (cpu_is_omap24xx()) { 9181c6a0718SPierre Ossman u16 reg = OMAP_MMC_READ(host, CON); 9191c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CON, reg | (1 << 11)); 9201c6a0718SPierre Ossman } else 9211c6a0718SPierre Ossman if (host->power_pin >= 0) 9221c6a0718SPierre Ossman omap_set_gpio_dataout(host->power_pin, 1); 9231c6a0718SPierre Ossman } else { 9241c6a0718SPierre Ossman if (machine_is_omap_innovator()) 9251c6a0718SPierre Ossman innovator_fpga_socket_power(0); 9261c6a0718SPierre Ossman else if (machine_is_omap_h2()) 9271c6a0718SPierre Ossman tps65010_set_gpio_out_value(GPIO3, LOW); 9281c6a0718SPierre Ossman else if (machine_is_omap_h3()) 9291c6a0718SPierre Ossman tps65010_set_gpio_out_value(GPIO4, LOW); 9301c6a0718SPierre Ossman else if (cpu_is_omap24xx()) { 9311c6a0718SPierre Ossman u16 reg = OMAP_MMC_READ(host, CON); 9321c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11)); 9331c6a0718SPierre Ossman } else 9341c6a0718SPierre Ossman if (host->power_pin >= 0) 9351c6a0718SPierre Ossman omap_set_gpio_dataout(host->power_pin, 0); 9361c6a0718SPierre Ossman } 9371c6a0718SPierre Ossman } 9381c6a0718SPierre Ossman 9391c6a0718SPierre Ossman static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 9401c6a0718SPierre Ossman { 9411c6a0718SPierre Ossman struct mmc_omap_host *host = mmc_priv(mmc); 9421c6a0718SPierre Ossman int dsor; 9431c6a0718SPierre Ossman int realclock, i; 9441c6a0718SPierre Ossman 9451c6a0718SPierre Ossman realclock = ios->clock; 9461c6a0718SPierre Ossman 9471c6a0718SPierre Ossman if (ios->clock == 0) 9481c6a0718SPierre Ossman dsor = 0; 9491c6a0718SPierre Ossman else { 9501c6a0718SPierre Ossman int func_clk_rate = clk_get_rate(host->fclk); 9511c6a0718SPierre Ossman 9521c6a0718SPierre Ossman dsor = func_clk_rate / realclock; 9531c6a0718SPierre Ossman if (dsor < 1) 9541c6a0718SPierre Ossman dsor = 1; 9551c6a0718SPierre Ossman 9561c6a0718SPierre Ossman if (func_clk_rate / dsor > realclock) 9571c6a0718SPierre Ossman dsor++; 9581c6a0718SPierre Ossman 9591c6a0718SPierre Ossman if (dsor > 250) 9601c6a0718SPierre Ossman dsor = 250; 9611c6a0718SPierre Ossman dsor++; 9621c6a0718SPierre Ossman 9631c6a0718SPierre Ossman if (ios->bus_width == MMC_BUS_WIDTH_4) 9641c6a0718SPierre Ossman dsor |= 1 << 15; 9651c6a0718SPierre Ossman } 9661c6a0718SPierre Ossman 9671c6a0718SPierre Ossman switch (ios->power_mode) { 9681c6a0718SPierre Ossman case MMC_POWER_OFF: 9691c6a0718SPierre Ossman mmc_omap_power(host, 0); 9701c6a0718SPierre Ossman break; 9711c6a0718SPierre Ossman case MMC_POWER_UP: 972*46a6730eSTony Lindgren /* Cannot touch dsor yet, just power up MMC */ 9731c6a0718SPierre Ossman mmc_omap_power(host, 1); 974*46a6730eSTony Lindgren return; 975*46a6730eSTony Lindgren case MMC_POWER_ON: 9761c6a0718SPierre Ossman dsor |= 1 << 11; 9771c6a0718SPierre Ossman break; 9781c6a0718SPierre Ossman } 9791c6a0718SPierre Ossman 9801c6a0718SPierre Ossman host->bus_mode = ios->bus_mode; 9811c6a0718SPierre Ossman host->hw_bus_mode = host->bus_mode; 9821c6a0718SPierre Ossman 9831c6a0718SPierre Ossman clk_enable(host->fclk); 9841c6a0718SPierre Ossman 9851c6a0718SPierre Ossman /* On insanely high arm_per frequencies something sometimes 9861c6a0718SPierre Ossman * goes somehow out of sync, and the POW bit is not being set, 9871c6a0718SPierre Ossman * which results in the while loop below getting stuck. 9881c6a0718SPierre Ossman * Writing to the CON register twice seems to do the trick. */ 9891c6a0718SPierre Ossman for (i = 0; i < 2; i++) 9901c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CON, dsor); 991*46a6730eSTony Lindgren if (ios->power_mode == MMC_POWER_ON) { 9921c6a0718SPierre Ossman /* Send clock cycles, poll completion */ 9931c6a0718SPierre Ossman OMAP_MMC_WRITE(host, IE, 0); 9941c6a0718SPierre Ossman OMAP_MMC_WRITE(host, STAT, 0xffff); 9951c6a0718SPierre Ossman OMAP_MMC_WRITE(host, CMD, 1 << 7); 9961c6a0718SPierre Ossman while ((OMAP_MMC_READ(host, STAT) & 1) == 0); 9971c6a0718SPierre Ossman OMAP_MMC_WRITE(host, STAT, 1); 9981c6a0718SPierre Ossman } 9991c6a0718SPierre Ossman clk_disable(host->fclk); 10001c6a0718SPierre Ossman } 10011c6a0718SPierre Ossman 10021c6a0718SPierre Ossman static int mmc_omap_get_ro(struct mmc_host *mmc) 10031c6a0718SPierre Ossman { 10041c6a0718SPierre Ossman struct mmc_omap_host *host = mmc_priv(mmc); 10051c6a0718SPierre Ossman 10061c6a0718SPierre Ossman return host->wp_pin && omap_get_gpio_datain(host->wp_pin); 10071c6a0718SPierre Ossman } 10081c6a0718SPierre Ossman 10091c6a0718SPierre Ossman static const struct mmc_host_ops mmc_omap_ops = { 10101c6a0718SPierre Ossman .request = mmc_omap_request, 10111c6a0718SPierre Ossman .set_ios = mmc_omap_set_ios, 10121c6a0718SPierre Ossman .get_ro = mmc_omap_get_ro, 10131c6a0718SPierre Ossman }; 10141c6a0718SPierre Ossman 10151c6a0718SPierre Ossman static int __init mmc_omap_probe(struct platform_device *pdev) 10161c6a0718SPierre Ossman { 10171c6a0718SPierre Ossman struct omap_mmc_conf *minfo = pdev->dev.platform_data; 10181c6a0718SPierre Ossman struct mmc_host *mmc; 10191c6a0718SPierre Ossman struct mmc_omap_host *host = NULL; 10201c6a0718SPierre Ossman struct resource *res; 10211c6a0718SPierre Ossman int ret = 0; 10221c6a0718SPierre Ossman int irq; 10231c6a0718SPierre Ossman 10241c6a0718SPierre Ossman if (minfo == NULL) { 10251c6a0718SPierre Ossman dev_err(&pdev->dev, "platform data missing\n"); 10261c6a0718SPierre Ossman return -ENXIO; 10271c6a0718SPierre Ossman } 10281c6a0718SPierre Ossman 10291c6a0718SPierre Ossman res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10301c6a0718SPierre Ossman irq = platform_get_irq(pdev, 0); 10311c6a0718SPierre Ossman if (res == NULL || irq < 0) 10321c6a0718SPierre Ossman return -ENXIO; 10331c6a0718SPierre Ossman 10341c6a0718SPierre Ossman res = request_mem_region(res->start, res->end - res->start + 1, 10351c6a0718SPierre Ossman pdev->name); 10361c6a0718SPierre Ossman if (res == NULL) 10371c6a0718SPierre Ossman return -EBUSY; 10381c6a0718SPierre Ossman 10391c6a0718SPierre Ossman mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); 10401c6a0718SPierre Ossman if (mmc == NULL) { 10411c6a0718SPierre Ossman ret = -ENOMEM; 10421c6a0718SPierre Ossman goto err_free_mem_region; 10431c6a0718SPierre Ossman } 10441c6a0718SPierre Ossman 10451c6a0718SPierre Ossman host = mmc_priv(mmc); 10461c6a0718SPierre Ossman host->mmc = mmc; 10471c6a0718SPierre Ossman 10481c6a0718SPierre Ossman spin_lock_init(&host->dma_lock); 10491c6a0718SPierre Ossman init_timer(&host->dma_timer); 10501c6a0718SPierre Ossman host->dma_timer.function = mmc_omap_dma_timer; 10511c6a0718SPierre Ossman host->dma_timer.data = (unsigned long) host; 10521c6a0718SPierre Ossman 10531c6a0718SPierre Ossman host->id = pdev->id; 10541c6a0718SPierre Ossman host->mem_res = res; 10551c6a0718SPierre Ossman host->irq = irq; 10561c6a0718SPierre Ossman 10571c6a0718SPierre Ossman if (cpu_is_omap24xx()) { 10581c6a0718SPierre Ossman host->iclk = clk_get(&pdev->dev, "mmc_ick"); 10591c6a0718SPierre Ossman if (IS_ERR(host->iclk)) 10601c6a0718SPierre Ossman goto err_free_mmc_host; 10611c6a0718SPierre Ossman clk_enable(host->iclk); 10621c6a0718SPierre Ossman } 10631c6a0718SPierre Ossman 10641c6a0718SPierre Ossman if (!cpu_is_omap24xx()) 10651c6a0718SPierre Ossman host->fclk = clk_get(&pdev->dev, "mmc_ck"); 10661c6a0718SPierre Ossman else 10671c6a0718SPierre Ossman host->fclk = clk_get(&pdev->dev, "mmc_fck"); 10681c6a0718SPierre Ossman 10691c6a0718SPierre Ossman if (IS_ERR(host->fclk)) { 10701c6a0718SPierre Ossman ret = PTR_ERR(host->fclk); 10711c6a0718SPierre Ossman goto err_free_iclk; 10721c6a0718SPierre Ossman } 10731c6a0718SPierre Ossman 10741c6a0718SPierre Ossman /* REVISIT: 10751c6a0718SPierre Ossman * Also, use minfo->cover to decide how to manage 10761c6a0718SPierre Ossman * the card detect sensing. 10771c6a0718SPierre Ossman */ 10781c6a0718SPierre Ossman host->power_pin = minfo->power_pin; 10791c6a0718SPierre Ossman host->switch_pin = minfo->switch_pin; 10801c6a0718SPierre Ossman host->wp_pin = minfo->wp_pin; 10811c6a0718SPierre Ossman host->use_dma = 1; 10821c6a0718SPierre Ossman host->dma_ch = -1; 10831c6a0718SPierre Ossman 10841c6a0718SPierre Ossman host->irq = irq; 10851c6a0718SPierre Ossman host->phys_base = host->mem_res->start; 10861c6a0718SPierre Ossman host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); 10871c6a0718SPierre Ossman 10881c6a0718SPierre Ossman mmc->ops = &mmc_omap_ops; 10891c6a0718SPierre Ossman mmc->f_min = 400000; 10901c6a0718SPierre Ossman mmc->f_max = 24000000; 10911c6a0718SPierre Ossman mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 10921c6a0718SPierre Ossman mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; 10931c6a0718SPierre Ossman 10941c6a0718SPierre Ossman if (minfo->wire4) 10951c6a0718SPierre Ossman mmc->caps |= MMC_CAP_4_BIT_DATA; 10961c6a0718SPierre Ossman 10971c6a0718SPierre Ossman /* Use scatterlist DMA to reduce per-transfer costs. 10981c6a0718SPierre Ossman * NOTE max_seg_size assumption that small blocks aren't 10991c6a0718SPierre Ossman * normally used (except e.g. for reading SD registers). 11001c6a0718SPierre Ossman */ 11011c6a0718SPierre Ossman mmc->max_phys_segs = 32; 11021c6a0718SPierre Ossman mmc->max_hw_segs = 32; 11031c6a0718SPierre Ossman mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ 11041c6a0718SPierre Ossman mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ 11051c6a0718SPierre Ossman mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 11061c6a0718SPierre Ossman mmc->max_seg_size = mmc->max_req_size; 11071c6a0718SPierre Ossman 11081c6a0718SPierre Ossman if (host->power_pin >= 0) { 11091c6a0718SPierre Ossman if ((ret = omap_request_gpio(host->power_pin)) != 0) { 11101c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), 11111c6a0718SPierre Ossman "Unable to get GPIO pin for MMC power\n"); 11121c6a0718SPierre Ossman goto err_free_fclk; 11131c6a0718SPierre Ossman } 11141c6a0718SPierre Ossman omap_set_gpio_direction(host->power_pin, 0); 11151c6a0718SPierre Ossman } 11161c6a0718SPierre Ossman 11171c6a0718SPierre Ossman ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); 11181c6a0718SPierre Ossman if (ret) 11191c6a0718SPierre Ossman goto err_free_power_gpio; 11201c6a0718SPierre Ossman 11211c6a0718SPierre Ossman host->dev = &pdev->dev; 11221c6a0718SPierre Ossman platform_set_drvdata(pdev, host); 11231c6a0718SPierre Ossman 11241c6a0718SPierre Ossman if (host->switch_pin >= 0) { 11251c6a0718SPierre Ossman INIT_WORK(&host->switch_work, mmc_omap_switch_handler); 11261c6a0718SPierre Ossman init_timer(&host->switch_timer); 11271c6a0718SPierre Ossman host->switch_timer.function = mmc_omap_switch_timer; 11281c6a0718SPierre Ossman host->switch_timer.data = (unsigned long) host; 11291c6a0718SPierre Ossman if (omap_request_gpio(host->switch_pin) != 0) { 11301c6a0718SPierre Ossman dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n"); 11311c6a0718SPierre Ossman host->switch_pin = -1; 11321c6a0718SPierre Ossman goto no_switch; 11331c6a0718SPierre Ossman } 11341c6a0718SPierre Ossman 11351c6a0718SPierre Ossman omap_set_gpio_direction(host->switch_pin, 1); 11361c6a0718SPierre Ossman ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), 11371c6a0718SPierre Ossman mmc_omap_switch_irq, IRQF_TRIGGER_RISING, DRIVER_NAME, host); 11381c6a0718SPierre Ossman if (ret) { 11391c6a0718SPierre Ossman dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n"); 11401c6a0718SPierre Ossman omap_free_gpio(host->switch_pin); 11411c6a0718SPierre Ossman host->switch_pin = -1; 11421c6a0718SPierre Ossman goto no_switch; 11431c6a0718SPierre Ossman } 11441c6a0718SPierre Ossman ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); 11451c6a0718SPierre Ossman if (ret == 0) { 11461c6a0718SPierre Ossman ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); 11471c6a0718SPierre Ossman if (ret != 0) 11481c6a0718SPierre Ossman device_remove_file(&pdev->dev, &dev_attr_cover_switch); 11491c6a0718SPierre Ossman } 11501c6a0718SPierre Ossman if (ret) { 11511c6a0718SPierre Ossman dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); 11521c6a0718SPierre Ossman free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); 11531c6a0718SPierre Ossman omap_free_gpio(host->switch_pin); 11541c6a0718SPierre Ossman host->switch_pin = -1; 11551c6a0718SPierre Ossman goto no_switch; 11561c6a0718SPierre Ossman } 11571c6a0718SPierre Ossman if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) 11581c6a0718SPierre Ossman schedule_work(&host->switch_work); 11591c6a0718SPierre Ossman } 11601c6a0718SPierre Ossman 11611c6a0718SPierre Ossman mmc_add_host(mmc); 11621c6a0718SPierre Ossman 11631c6a0718SPierre Ossman return 0; 11641c6a0718SPierre Ossman 11651c6a0718SPierre Ossman no_switch: 11661c6a0718SPierre Ossman /* FIXME: Free other resources too. */ 11671c6a0718SPierre Ossman if (host) { 11681c6a0718SPierre Ossman if (host->iclk && !IS_ERR(host->iclk)) 11691c6a0718SPierre Ossman clk_put(host->iclk); 11701c6a0718SPierre Ossman if (host->fclk && !IS_ERR(host->fclk)) 11711c6a0718SPierre Ossman clk_put(host->fclk); 11721c6a0718SPierre Ossman mmc_free_host(host->mmc); 11731c6a0718SPierre Ossman } 11741c6a0718SPierre Ossman err_free_power_gpio: 11751c6a0718SPierre Ossman if (host->power_pin >= 0) 11761c6a0718SPierre Ossman omap_free_gpio(host->power_pin); 11771c6a0718SPierre Ossman err_free_fclk: 11781c6a0718SPierre Ossman clk_put(host->fclk); 11791c6a0718SPierre Ossman err_free_iclk: 11801c6a0718SPierre Ossman if (host->iclk != NULL) { 11811c6a0718SPierre Ossman clk_disable(host->iclk); 11821c6a0718SPierre Ossman clk_put(host->iclk); 11831c6a0718SPierre Ossman } 11841c6a0718SPierre Ossman err_free_mmc_host: 11851c6a0718SPierre Ossman mmc_free_host(host->mmc); 11861c6a0718SPierre Ossman err_free_mem_region: 11871c6a0718SPierre Ossman release_mem_region(res->start, res->end - res->start + 1); 11881c6a0718SPierre Ossman return ret; 11891c6a0718SPierre Ossman } 11901c6a0718SPierre Ossman 11911c6a0718SPierre Ossman static int mmc_omap_remove(struct platform_device *pdev) 11921c6a0718SPierre Ossman { 11931c6a0718SPierre Ossman struct mmc_omap_host *host = platform_get_drvdata(pdev); 11941c6a0718SPierre Ossman 11951c6a0718SPierre Ossman platform_set_drvdata(pdev, NULL); 11961c6a0718SPierre Ossman 11971c6a0718SPierre Ossman BUG_ON(host == NULL); 11981c6a0718SPierre Ossman 11991c6a0718SPierre Ossman mmc_remove_host(host->mmc); 12001c6a0718SPierre Ossman free_irq(host->irq, host); 12011c6a0718SPierre Ossman 12021c6a0718SPierre Ossman if (host->power_pin >= 0) 12031c6a0718SPierre Ossman omap_free_gpio(host->power_pin); 12041c6a0718SPierre Ossman if (host->switch_pin >= 0) { 12051c6a0718SPierre Ossman device_remove_file(&pdev->dev, &dev_attr_enable_poll); 12061c6a0718SPierre Ossman device_remove_file(&pdev->dev, &dev_attr_cover_switch); 12071c6a0718SPierre Ossman free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); 12081c6a0718SPierre Ossman omap_free_gpio(host->switch_pin); 12091c6a0718SPierre Ossman host->switch_pin = -1; 12101c6a0718SPierre Ossman del_timer_sync(&host->switch_timer); 12111c6a0718SPierre Ossman flush_scheduled_work(); 12121c6a0718SPierre Ossman } 12131c6a0718SPierre Ossman if (host->iclk && !IS_ERR(host->iclk)) 12141c6a0718SPierre Ossman clk_put(host->iclk); 12151c6a0718SPierre Ossman if (host->fclk && !IS_ERR(host->fclk)) 12161c6a0718SPierre Ossman clk_put(host->fclk); 12171c6a0718SPierre Ossman 12181c6a0718SPierre Ossman release_mem_region(pdev->resource[0].start, 12191c6a0718SPierre Ossman pdev->resource[0].end - pdev->resource[0].start + 1); 12201c6a0718SPierre Ossman 12211c6a0718SPierre Ossman mmc_free_host(host->mmc); 12221c6a0718SPierre Ossman 12231c6a0718SPierre Ossman return 0; 12241c6a0718SPierre Ossman } 12251c6a0718SPierre Ossman 12261c6a0718SPierre Ossman #ifdef CONFIG_PM 12271c6a0718SPierre Ossman static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) 12281c6a0718SPierre Ossman { 12291c6a0718SPierre Ossman int ret = 0; 12301c6a0718SPierre Ossman struct mmc_omap_host *host = platform_get_drvdata(pdev); 12311c6a0718SPierre Ossman 12321c6a0718SPierre Ossman if (host && host->suspended) 12331c6a0718SPierre Ossman return 0; 12341c6a0718SPierre Ossman 12351c6a0718SPierre Ossman if (host) { 12361c6a0718SPierre Ossman ret = mmc_suspend_host(host->mmc, mesg); 12371c6a0718SPierre Ossman if (ret == 0) 12381c6a0718SPierre Ossman host->suspended = 1; 12391c6a0718SPierre Ossman } 12401c6a0718SPierre Ossman return ret; 12411c6a0718SPierre Ossman } 12421c6a0718SPierre Ossman 12431c6a0718SPierre Ossman static int mmc_omap_resume(struct platform_device *pdev) 12441c6a0718SPierre Ossman { 12451c6a0718SPierre Ossman int ret = 0; 12461c6a0718SPierre Ossman struct mmc_omap_host *host = platform_get_drvdata(pdev); 12471c6a0718SPierre Ossman 12481c6a0718SPierre Ossman if (host && !host->suspended) 12491c6a0718SPierre Ossman return 0; 12501c6a0718SPierre Ossman 12511c6a0718SPierre Ossman if (host) { 12521c6a0718SPierre Ossman ret = mmc_resume_host(host->mmc); 12531c6a0718SPierre Ossman if (ret == 0) 12541c6a0718SPierre Ossman host->suspended = 0; 12551c6a0718SPierre Ossman } 12561c6a0718SPierre Ossman 12571c6a0718SPierre Ossman return ret; 12581c6a0718SPierre Ossman } 12591c6a0718SPierre Ossman #else 12601c6a0718SPierre Ossman #define mmc_omap_suspend NULL 12611c6a0718SPierre Ossman #define mmc_omap_resume NULL 12621c6a0718SPierre Ossman #endif 12631c6a0718SPierre Ossman 12641c6a0718SPierre Ossman static struct platform_driver mmc_omap_driver = { 12651c6a0718SPierre Ossman .probe = mmc_omap_probe, 12661c6a0718SPierre Ossman .remove = mmc_omap_remove, 12671c6a0718SPierre Ossman .suspend = mmc_omap_suspend, 12681c6a0718SPierre Ossman .resume = mmc_omap_resume, 12691c6a0718SPierre Ossman .driver = { 12701c6a0718SPierre Ossman .name = DRIVER_NAME, 12711c6a0718SPierre Ossman }, 12721c6a0718SPierre Ossman }; 12731c6a0718SPierre Ossman 12741c6a0718SPierre Ossman static int __init mmc_omap_init(void) 12751c6a0718SPierre Ossman { 12761c6a0718SPierre Ossman return platform_driver_register(&mmc_omap_driver); 12771c6a0718SPierre Ossman } 12781c6a0718SPierre Ossman 12791c6a0718SPierre Ossman static void __exit mmc_omap_exit(void) 12801c6a0718SPierre Ossman { 12811c6a0718SPierre Ossman platform_driver_unregister(&mmc_omap_driver); 12821c6a0718SPierre Ossman } 12831c6a0718SPierre Ossman 12841c6a0718SPierre Ossman module_init(mmc_omap_init); 12851c6a0718SPierre Ossman module_exit(mmc_omap_exit); 12861c6a0718SPierre Ossman 12871c6a0718SPierre Ossman MODULE_DESCRIPTION("OMAP Multimedia Card driver"); 12881c6a0718SPierre Ossman MODULE_LICENSE("GPL"); 12891c6a0718SPierre Ossman MODULE_ALIAS(DRIVER_NAME); 12901c6a0718SPierre Ossman MODULE_AUTHOR("Juha Yrj�l�"); 1291