11c6a0718SPierre Ossman /* 270f10482SPierre Ossman * linux/drivers/mmc/host/au1xmmc.c - AU1XX0 MMC driver 31c6a0718SPierre Ossman * 41c6a0718SPierre Ossman * Copyright (c) 2005, Advanced Micro Devices, Inc. 51c6a0718SPierre Ossman * 61c6a0718SPierre Ossman * Developed with help from the 2.4.30 MMC AU1XXX controller including 71c6a0718SPierre Ossman * the following copyright notices: 81c6a0718SPierre Ossman * Copyright (c) 2003-2004 Embedded Edge, LLC. 91c6a0718SPierre Ossman * Portions Copyright (C) 2002 Embedix, Inc 101c6a0718SPierre Ossman * Copyright 2002 Hewlett-Packard Company 111c6a0718SPierre Ossman 121c6a0718SPierre Ossman * 2.6 version of this driver inspired by: 131c6a0718SPierre Ossman * (drivers/mmc/wbsd.c) Copyright (C) 2004-2005 Pierre Ossman, 141c6a0718SPierre Ossman * All Rights Reserved. 151c6a0718SPierre Ossman * (drivers/mmc/pxa.c) Copyright (C) 2003 Russell King, 161c6a0718SPierre Ossman * All Rights Reserved. 171c6a0718SPierre Ossman * 181c6a0718SPierre Ossman 191c6a0718SPierre Ossman * This program is free software; you can redistribute it and/or modify 201c6a0718SPierre Ossman * it under the terms of the GNU General Public License version 2 as 211c6a0718SPierre Ossman * published by the Free Software Foundation. 221c6a0718SPierre Ossman */ 231c6a0718SPierre Ossman 241c6a0718SPierre Ossman /* Why is a timer used to detect insert events? 251c6a0718SPierre Ossman * 261c6a0718SPierre Ossman * From the AU1100 MMC application guide: 271c6a0718SPierre Ossman * If the Au1100-based design is intended to support both MultiMediaCards 281c6a0718SPierre Ossman * and 1- or 4-data bit SecureDigital cards, then the solution is to 291c6a0718SPierre Ossman * connect a weak (560KOhm) pull-up resistor to connector pin 1. 301c6a0718SPierre Ossman * In doing so, a MMC card never enters SPI-mode communications, 311c6a0718SPierre Ossman * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective 321c6a0718SPierre Ossman * (the low to high transition will not occur). 331c6a0718SPierre Ossman * 341c6a0718SPierre Ossman * So we use the timer to check the status manually. 351c6a0718SPierre Ossman */ 361c6a0718SPierre Ossman 371c6a0718SPierre Ossman #include <linux/module.h> 381c6a0718SPierre Ossman #include <linux/init.h> 391c6a0718SPierre Ossman #include <linux/platform_device.h> 401c6a0718SPierre Ossman #include <linux/mm.h> 411c6a0718SPierre Ossman #include <linux/interrupt.h> 421c6a0718SPierre Ossman #include <linux/dma-mapping.h> 43bd6dee6fSJens Axboe #include <scatterlist/scatterlist.h> 441c6a0718SPierre Ossman 451c6a0718SPierre Ossman #include <linux/mmc/host.h> 461c6a0718SPierre Ossman #include <asm/io.h> 471c6a0718SPierre Ossman #include <asm/mach-au1x00/au1000.h> 481c6a0718SPierre Ossman #include <asm/mach-au1x00/au1xxx_dbdma.h> 491c6a0718SPierre Ossman #include <asm/mach-au1x00/au1100_mmc.h> 501c6a0718SPierre Ossman 511c6a0718SPierre Ossman #include <au1xxx.h> 521c6a0718SPierre Ossman #include "au1xmmc.h" 531c6a0718SPierre Ossman 541c6a0718SPierre Ossman #define DRIVER_NAME "au1xxx-mmc" 551c6a0718SPierre Ossman 561c6a0718SPierre Ossman /* Set this to enable special debugging macros */ 571c6a0718SPierre Ossman 581c6a0718SPierre Ossman #ifdef DEBUG 591c6a0718SPierre Ossman #define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) 601c6a0718SPierre Ossman #else 611c6a0718SPierre Ossman #define DBG(fmt, idx, args...) 621c6a0718SPierre Ossman #endif 631c6a0718SPierre Ossman 641c6a0718SPierre Ossman const struct { 651c6a0718SPierre Ossman u32 iobase; 661c6a0718SPierre Ossman u32 tx_devid, rx_devid; 671c6a0718SPierre Ossman u16 bcsrpwr; 681c6a0718SPierre Ossman u16 bcsrstatus; 691c6a0718SPierre Ossman u16 wpstatus; 701c6a0718SPierre Ossman } au1xmmc_card_table[] = { 711c6a0718SPierre Ossman { SD0_BASE, DSCR_CMD0_SDMS_TX0, DSCR_CMD0_SDMS_RX0, 721c6a0718SPierre Ossman BCSR_BOARD_SD0PWR, BCSR_INT_SD0INSERT, BCSR_STATUS_SD0WP }, 731c6a0718SPierre Ossman #ifndef CONFIG_MIPS_DB1200 741c6a0718SPierre Ossman { SD1_BASE, DSCR_CMD0_SDMS_TX1, DSCR_CMD0_SDMS_RX1, 751c6a0718SPierre Ossman BCSR_BOARD_DS1PWR, BCSR_INT_SD1INSERT, BCSR_STATUS_SD1WP } 761c6a0718SPierre Ossman #endif 771c6a0718SPierre Ossman }; 781c6a0718SPierre Ossman 7960da8de6SRobert P. J. Day #define AU1XMMC_CONTROLLER_COUNT (ARRAY_SIZE(au1xmmc_card_table)) 801c6a0718SPierre Ossman 811c6a0718SPierre Ossman /* This array stores pointers for the hosts (used by the IRQ handler) */ 821c6a0718SPierre Ossman struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT]; 831c6a0718SPierre Ossman static int dma = 1; 841c6a0718SPierre Ossman 851c6a0718SPierre Ossman #ifdef MODULE 861c6a0718SPierre Ossman module_param(dma, bool, 0); 871c6a0718SPierre Ossman MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)"); 881c6a0718SPierre Ossman #endif 891c6a0718SPierre Ossman 901c6a0718SPierre Ossman static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask) 911c6a0718SPierre Ossman { 921c6a0718SPierre Ossman u32 val = au_readl(HOST_CONFIG(host)); 931c6a0718SPierre Ossman val |= mask; 941c6a0718SPierre Ossman au_writel(val, HOST_CONFIG(host)); 951c6a0718SPierre Ossman au_sync(); 961c6a0718SPierre Ossman } 971c6a0718SPierre Ossman 981c6a0718SPierre Ossman static inline void FLUSH_FIFO(struct au1xmmc_host *host) 991c6a0718SPierre Ossman { 1001c6a0718SPierre Ossman u32 val = au_readl(HOST_CONFIG2(host)); 1011c6a0718SPierre Ossman 1021c6a0718SPierre Ossman au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host)); 1031c6a0718SPierre Ossman au_sync_delay(1); 1041c6a0718SPierre Ossman 1051c6a0718SPierre Ossman /* SEND_STOP will turn off clock control - this re-enables it */ 1061c6a0718SPierre Ossman val &= ~SD_CONFIG2_DF; 1071c6a0718SPierre Ossman 1081c6a0718SPierre Ossman au_writel(val, HOST_CONFIG2(host)); 1091c6a0718SPierre Ossman au_sync(); 1101c6a0718SPierre Ossman } 1111c6a0718SPierre Ossman 1121c6a0718SPierre Ossman static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask) 1131c6a0718SPierre Ossman { 1141c6a0718SPierre Ossman u32 val = au_readl(HOST_CONFIG(host)); 1151c6a0718SPierre Ossman val &= ~mask; 1161c6a0718SPierre Ossman au_writel(val, HOST_CONFIG(host)); 1171c6a0718SPierre Ossman au_sync(); 1181c6a0718SPierre Ossman } 1191c6a0718SPierre Ossman 1201c6a0718SPierre Ossman static inline void SEND_STOP(struct au1xmmc_host *host) 1211c6a0718SPierre Ossman { 1221c6a0718SPierre Ossman 1231c6a0718SPierre Ossman /* We know the value of CONFIG2, so avoid a read we don't need */ 1241c6a0718SPierre Ossman u32 mask = SD_CONFIG2_EN; 1251c6a0718SPierre Ossman 1261c6a0718SPierre Ossman WARN_ON(host->status != HOST_S_DATA); 1271c6a0718SPierre Ossman host->status = HOST_S_STOP; 1281c6a0718SPierre Ossman 1291c6a0718SPierre Ossman au_writel(mask | SD_CONFIG2_DF, HOST_CONFIG2(host)); 1301c6a0718SPierre Ossman au_sync(); 1311c6a0718SPierre Ossman 1321c6a0718SPierre Ossman /* Send the stop commmand */ 1331c6a0718SPierre Ossman au_writel(STOP_CMD, HOST_CMD(host)); 1341c6a0718SPierre Ossman } 1351c6a0718SPierre Ossman 1361c6a0718SPierre Ossman static void au1xmmc_set_power(struct au1xmmc_host *host, int state) 1371c6a0718SPierre Ossman { 1381c6a0718SPierre Ossman 1391c6a0718SPierre Ossman u32 val = au1xmmc_card_table[host->id].bcsrpwr; 1401c6a0718SPierre Ossman 1411c6a0718SPierre Ossman bcsr->board &= ~val; 1421c6a0718SPierre Ossman if (state) bcsr->board |= val; 1431c6a0718SPierre Ossman 1441c6a0718SPierre Ossman au_sync_delay(1); 1451c6a0718SPierre Ossman } 1461c6a0718SPierre Ossman 1471c6a0718SPierre Ossman static inline int au1xmmc_card_inserted(struct au1xmmc_host *host) 1481c6a0718SPierre Ossman { 1491c6a0718SPierre Ossman return (bcsr->sig_status & au1xmmc_card_table[host->id].bcsrstatus) 1501c6a0718SPierre Ossman ? 1 : 0; 1511c6a0718SPierre Ossman } 1521c6a0718SPierre Ossman 1531c6a0718SPierre Ossman static int au1xmmc_card_readonly(struct mmc_host *mmc) 1541c6a0718SPierre Ossman { 1551c6a0718SPierre Ossman struct au1xmmc_host *host = mmc_priv(mmc); 1561c6a0718SPierre Ossman return (bcsr->status & au1xmmc_card_table[host->id].wpstatus) 1571c6a0718SPierre Ossman ? 1 : 0; 1581c6a0718SPierre Ossman } 1591c6a0718SPierre Ossman 1601c6a0718SPierre Ossman static void au1xmmc_finish_request(struct au1xmmc_host *host) 1611c6a0718SPierre Ossman { 1621c6a0718SPierre Ossman 1631c6a0718SPierre Ossman struct mmc_request *mrq = host->mrq; 1641c6a0718SPierre Ossman 1651c6a0718SPierre Ossman host->mrq = NULL; 1661c6a0718SPierre Ossman host->flags &= HOST_F_ACTIVE; 1671c6a0718SPierre Ossman 1681c6a0718SPierre Ossman host->dma.len = 0; 1691c6a0718SPierre Ossman host->dma.dir = 0; 1701c6a0718SPierre Ossman 1711c6a0718SPierre Ossman host->pio.index = 0; 1721c6a0718SPierre Ossman host->pio.offset = 0; 1731c6a0718SPierre Ossman host->pio.len = 0; 1741c6a0718SPierre Ossman 1751c6a0718SPierre Ossman host->status = HOST_S_IDLE; 1761c6a0718SPierre Ossman 1771c6a0718SPierre Ossman bcsr->disk_leds |= (1 << 8); 1781c6a0718SPierre Ossman 1791c6a0718SPierre Ossman mmc_request_done(host->mmc, mrq); 1801c6a0718SPierre Ossman } 1811c6a0718SPierre Ossman 1821c6a0718SPierre Ossman static void au1xmmc_tasklet_finish(unsigned long param) 1831c6a0718SPierre Ossman { 1841c6a0718SPierre Ossman struct au1xmmc_host *host = (struct au1xmmc_host *) param; 1851c6a0718SPierre Ossman au1xmmc_finish_request(host); 1861c6a0718SPierre Ossman } 1871c6a0718SPierre Ossman 1881c6a0718SPierre Ossman static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, 189be0192aaSPierre Ossman struct mmc_command *cmd, struct mmc_data *data) 1901c6a0718SPierre Ossman { 1911c6a0718SPierre Ossman u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); 1921c6a0718SPierre Ossman 1931c6a0718SPierre Ossman switch (mmc_resp_type(cmd)) { 1941c6a0718SPierre Ossman case MMC_RSP_NONE: 1951c6a0718SPierre Ossman break; 1961c6a0718SPierre Ossman case MMC_RSP_R1: 1971c6a0718SPierre Ossman mmccmd |= SD_CMD_RT_1; 1981c6a0718SPierre Ossman break; 1991c6a0718SPierre Ossman case MMC_RSP_R1B: 2001c6a0718SPierre Ossman mmccmd |= SD_CMD_RT_1B; 2011c6a0718SPierre Ossman break; 2021c6a0718SPierre Ossman case MMC_RSP_R2: 2031c6a0718SPierre Ossman mmccmd |= SD_CMD_RT_2; 2041c6a0718SPierre Ossman break; 2051c6a0718SPierre Ossman case MMC_RSP_R3: 2061c6a0718SPierre Ossman mmccmd |= SD_CMD_RT_3; 2071c6a0718SPierre Ossman break; 2081c6a0718SPierre Ossman default: 2091c6a0718SPierre Ossman printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", 2101c6a0718SPierre Ossman mmc_resp_type(cmd)); 21117b0429dSPierre Ossman return -EINVAL; 2121c6a0718SPierre Ossman } 2131c6a0718SPierre Ossman 214be0192aaSPierre Ossman if (data) { 2156356a9d9SPierre Ossman if (data->flags & MMC_DATA_READ) { 216be0192aaSPierre Ossman if (data->blocks > 1) 2171c6a0718SPierre Ossman mmccmd |= SD_CMD_CT_4; 218c0f3b6c7SYoichi Yuasa else 219c0f3b6c7SYoichi Yuasa mmccmd |= SD_CMD_CT_2; 2206356a9d9SPierre Ossman } else if (data->flags & MMC_DATA_WRITE) { 221be0192aaSPierre Ossman if (data->blocks > 1) 2221c6a0718SPierre Ossman mmccmd |= SD_CMD_CT_3; 223c0f3b6c7SYoichi Yuasa else 224c0f3b6c7SYoichi Yuasa mmccmd |= SD_CMD_CT_1; 2251c6a0718SPierre Ossman } 226be0192aaSPierre Ossman } 2271c6a0718SPierre Ossman 2281c6a0718SPierre Ossman au_writel(cmd->arg, HOST_CMDARG(host)); 2291c6a0718SPierre Ossman au_sync(); 2301c6a0718SPierre Ossman 2311c6a0718SPierre Ossman if (wait) 2321c6a0718SPierre Ossman IRQ_OFF(host, SD_CONFIG_CR); 2331c6a0718SPierre Ossman 2341c6a0718SPierre Ossman au_writel((mmccmd | SD_CMD_GO), HOST_CMD(host)); 2351c6a0718SPierre Ossman au_sync(); 2361c6a0718SPierre Ossman 2371c6a0718SPierre Ossman /* Wait for the command to go on the line */ 2381c6a0718SPierre Ossman 2391c6a0718SPierre Ossman while(1) { 2401c6a0718SPierre Ossman if (!(au_readl(HOST_CMD(host)) & SD_CMD_GO)) 2411c6a0718SPierre Ossman break; 2421c6a0718SPierre Ossman } 2431c6a0718SPierre Ossman 2441c6a0718SPierre Ossman /* Wait for the command to come back */ 2451c6a0718SPierre Ossman 2461c6a0718SPierre Ossman if (wait) { 2471c6a0718SPierre Ossman u32 status = au_readl(HOST_STATUS(host)); 2481c6a0718SPierre Ossman 2491c6a0718SPierre Ossman while(!(status & SD_STATUS_CR)) 2501c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 2511c6a0718SPierre Ossman 2521c6a0718SPierre Ossman /* Clear the CR status */ 2531c6a0718SPierre Ossman au_writel(SD_STATUS_CR, HOST_STATUS(host)); 2541c6a0718SPierre Ossman 2551c6a0718SPierre Ossman IRQ_ON(host, SD_CONFIG_CR); 2561c6a0718SPierre Ossman } 2571c6a0718SPierre Ossman 25817b0429dSPierre Ossman return 0; 2591c6a0718SPierre Ossman } 2601c6a0718SPierre Ossman 2611c6a0718SPierre Ossman static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) 2621c6a0718SPierre Ossman { 2631c6a0718SPierre Ossman 2641c6a0718SPierre Ossman struct mmc_request *mrq = host->mrq; 2651c6a0718SPierre Ossman struct mmc_data *data; 2661c6a0718SPierre Ossman u32 crc; 2671c6a0718SPierre Ossman 2681c6a0718SPierre Ossman WARN_ON(host->status != HOST_S_DATA && host->status != HOST_S_STOP); 2691c6a0718SPierre Ossman 2701c6a0718SPierre Ossman if (host->mrq == NULL) 2711c6a0718SPierre Ossman return; 2721c6a0718SPierre Ossman 2731c6a0718SPierre Ossman data = mrq->cmd->data; 2741c6a0718SPierre Ossman 2751c6a0718SPierre Ossman if (status == 0) 2761c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 2771c6a0718SPierre Ossman 2781c6a0718SPierre Ossman /* The transaction is really over when the SD_STATUS_DB bit is clear */ 2791c6a0718SPierre Ossman 2801c6a0718SPierre Ossman while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB)) 2811c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 2821c6a0718SPierre Ossman 28317b0429dSPierre Ossman data->error = 0; 2841c6a0718SPierre Ossman dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir); 2851c6a0718SPierre Ossman 2861c6a0718SPierre Ossman /* Process any errors */ 2871c6a0718SPierre Ossman 2881c6a0718SPierre Ossman crc = (status & (SD_STATUS_WC | SD_STATUS_RC)); 2891c6a0718SPierre Ossman if (host->flags & HOST_F_XMIT) 2901c6a0718SPierre Ossman crc |= ((status & 0x07) == 0x02) ? 0 : 1; 2911c6a0718SPierre Ossman 2921c6a0718SPierre Ossman if (crc) 29317b0429dSPierre Ossman data->error = -EILSEQ; 2941c6a0718SPierre Ossman 2951c6a0718SPierre Ossman /* Clear the CRC bits */ 2961c6a0718SPierre Ossman au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host)); 2971c6a0718SPierre Ossman 2981c6a0718SPierre Ossman data->bytes_xfered = 0; 2991c6a0718SPierre Ossman 30017b0429dSPierre Ossman if (!data->error) { 3011c6a0718SPierre Ossman if (host->flags & HOST_F_DMA) { 3021c6a0718SPierre Ossman u32 chan = DMA_CHANNEL(host); 3031c6a0718SPierre Ossman 3041c6a0718SPierre Ossman chan_tab_t *c = *((chan_tab_t **) chan); 3051c6a0718SPierre Ossman au1x_dma_chan_t *cp = c->chan_ptr; 3061c6a0718SPierre Ossman data->bytes_xfered = cp->ddma_bytecnt; 3071c6a0718SPierre Ossman } 3081c6a0718SPierre Ossman else 3091c6a0718SPierre Ossman data->bytes_xfered = 3101c6a0718SPierre Ossman (data->blocks * data->blksz) - 3111c6a0718SPierre Ossman host->pio.len; 3121c6a0718SPierre Ossman } 3131c6a0718SPierre Ossman 3141c6a0718SPierre Ossman au1xmmc_finish_request(host); 3151c6a0718SPierre Ossman } 3161c6a0718SPierre Ossman 3171c6a0718SPierre Ossman static void au1xmmc_tasklet_data(unsigned long param) 3181c6a0718SPierre Ossman { 3191c6a0718SPierre Ossman struct au1xmmc_host *host = (struct au1xmmc_host *) param; 3201c6a0718SPierre Ossman 3211c6a0718SPierre Ossman u32 status = au_readl(HOST_STATUS(host)); 3221c6a0718SPierre Ossman au1xmmc_data_complete(host, status); 3231c6a0718SPierre Ossman } 3241c6a0718SPierre Ossman 3251c6a0718SPierre Ossman #define AU1XMMC_MAX_TRANSFER 8 3261c6a0718SPierre Ossman 3271c6a0718SPierre Ossman static void au1xmmc_send_pio(struct au1xmmc_host *host) 3281c6a0718SPierre Ossman { 3291c6a0718SPierre Ossman 3301c6a0718SPierre Ossman struct mmc_data *data = 0; 3311c6a0718SPierre Ossman int sg_len, max, count = 0; 3321c6a0718SPierre Ossman unsigned char *sg_ptr; 3331c6a0718SPierre Ossman u32 status = 0; 3341c6a0718SPierre Ossman struct scatterlist *sg; 3351c6a0718SPierre Ossman 3361c6a0718SPierre Ossman data = host->mrq->data; 3371c6a0718SPierre Ossman 3381c6a0718SPierre Ossman if (!(host->flags & HOST_F_XMIT)) 3391c6a0718SPierre Ossman return; 3401c6a0718SPierre Ossman 3411c6a0718SPierre Ossman /* This is the pointer to the data buffer */ 3421c6a0718SPierre Ossman sg = &data->sg[host->pio.index]; 34345711f1aSJens Axboe sg_ptr = sg_virt(sg) + host->pio.offset; 3441c6a0718SPierre Ossman 3451c6a0718SPierre Ossman /* This is the space left inside the buffer */ 3461c6a0718SPierre Ossman sg_len = data->sg[host->pio.index].length - host->pio.offset; 3471c6a0718SPierre Ossman 3481c6a0718SPierre Ossman /* Check to if we need less then the size of the sg_buffer */ 3491c6a0718SPierre Ossman 3501c6a0718SPierre Ossman max = (sg_len > host->pio.len) ? host->pio.len : sg_len; 3511c6a0718SPierre Ossman if (max > AU1XMMC_MAX_TRANSFER) max = AU1XMMC_MAX_TRANSFER; 3521c6a0718SPierre Ossman 3531c6a0718SPierre Ossman for(count = 0; count < max; count++ ) { 3541c6a0718SPierre Ossman unsigned char val; 3551c6a0718SPierre Ossman 3561c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 3571c6a0718SPierre Ossman 3581c6a0718SPierre Ossman if (!(status & SD_STATUS_TH)) 3591c6a0718SPierre Ossman break; 3601c6a0718SPierre Ossman 3611c6a0718SPierre Ossman val = *sg_ptr++; 3621c6a0718SPierre Ossman 3631c6a0718SPierre Ossman au_writel((unsigned long) val, HOST_TXPORT(host)); 3641c6a0718SPierre Ossman au_sync(); 3651c6a0718SPierre Ossman } 3661c6a0718SPierre Ossman 3671c6a0718SPierre Ossman host->pio.len -= count; 3681c6a0718SPierre Ossman host->pio.offset += count; 3691c6a0718SPierre Ossman 3701c6a0718SPierre Ossman if (count == sg_len) { 3711c6a0718SPierre Ossman host->pio.index++; 3721c6a0718SPierre Ossman host->pio.offset = 0; 3731c6a0718SPierre Ossman } 3741c6a0718SPierre Ossman 3751c6a0718SPierre Ossman if (host->pio.len == 0) { 3761c6a0718SPierre Ossman IRQ_OFF(host, SD_CONFIG_TH); 3771c6a0718SPierre Ossman 3781c6a0718SPierre Ossman if (host->flags & HOST_F_STOP) 3791c6a0718SPierre Ossman SEND_STOP(host); 3801c6a0718SPierre Ossman 3811c6a0718SPierre Ossman tasklet_schedule(&host->data_task); 3821c6a0718SPierre Ossman } 3831c6a0718SPierre Ossman } 3841c6a0718SPierre Ossman 3851c6a0718SPierre Ossman static void au1xmmc_receive_pio(struct au1xmmc_host *host) 3861c6a0718SPierre Ossman { 3871c6a0718SPierre Ossman 3881c6a0718SPierre Ossman struct mmc_data *data = 0; 3891c6a0718SPierre Ossman int sg_len = 0, max = 0, count = 0; 3901c6a0718SPierre Ossman unsigned char *sg_ptr = 0; 3911c6a0718SPierre Ossman u32 status = 0; 3921c6a0718SPierre Ossman struct scatterlist *sg; 3931c6a0718SPierre Ossman 3941c6a0718SPierre Ossman data = host->mrq->data; 3951c6a0718SPierre Ossman 3961c6a0718SPierre Ossman if (!(host->flags & HOST_F_RECV)) 3971c6a0718SPierre Ossman return; 3981c6a0718SPierre Ossman 3991c6a0718SPierre Ossman max = host->pio.len; 4001c6a0718SPierre Ossman 4011c6a0718SPierre Ossman if (host->pio.index < host->dma.len) { 4021c6a0718SPierre Ossman sg = &data->sg[host->pio.index]; 40345711f1aSJens Axboe sg_ptr = sg_virt(sg) + host->pio.offset; 4041c6a0718SPierre Ossman 4051c6a0718SPierre Ossman /* This is the space left inside the buffer */ 4061c6a0718SPierre Ossman sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; 4071c6a0718SPierre Ossman 4081c6a0718SPierre Ossman /* Check to if we need less then the size of the sg_buffer */ 4091c6a0718SPierre Ossman if (sg_len < max) max = sg_len; 4101c6a0718SPierre Ossman } 4111c6a0718SPierre Ossman 4121c6a0718SPierre Ossman if (max > AU1XMMC_MAX_TRANSFER) 4131c6a0718SPierre Ossman max = AU1XMMC_MAX_TRANSFER; 4141c6a0718SPierre Ossman 4151c6a0718SPierre Ossman for(count = 0; count < max; count++ ) { 4161c6a0718SPierre Ossman u32 val; 4171c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 4181c6a0718SPierre Ossman 4191c6a0718SPierre Ossman if (!(status & SD_STATUS_NE)) 4201c6a0718SPierre Ossman break; 4211c6a0718SPierre Ossman 4221c6a0718SPierre Ossman if (status & SD_STATUS_RC) { 4231c6a0718SPierre Ossman DBG("RX CRC Error [%d + %d].\n", host->id, 4241c6a0718SPierre Ossman host->pio.len, count); 4251c6a0718SPierre Ossman break; 4261c6a0718SPierre Ossman } 4271c6a0718SPierre Ossman 4281c6a0718SPierre Ossman if (status & SD_STATUS_RO) { 4291c6a0718SPierre Ossman DBG("RX Overrun [%d + %d]\n", host->id, 4301c6a0718SPierre Ossman host->pio.len, count); 4311c6a0718SPierre Ossman break; 4321c6a0718SPierre Ossman } 4331c6a0718SPierre Ossman else if (status & SD_STATUS_RU) { 4341c6a0718SPierre Ossman DBG("RX Underrun [%d + %d]\n", host->id, 4351c6a0718SPierre Ossman host->pio.len, count); 4361c6a0718SPierre Ossman break; 4371c6a0718SPierre Ossman } 4381c6a0718SPierre Ossman 4391c6a0718SPierre Ossman val = au_readl(HOST_RXPORT(host)); 4401c6a0718SPierre Ossman 4411c6a0718SPierre Ossman if (sg_ptr) 4421c6a0718SPierre Ossman *sg_ptr++ = (unsigned char) (val & 0xFF); 4431c6a0718SPierre Ossman } 4441c6a0718SPierre Ossman 4451c6a0718SPierre Ossman host->pio.len -= count; 4461c6a0718SPierre Ossman host->pio.offset += count; 4471c6a0718SPierre Ossman 4481c6a0718SPierre Ossman if (sg_len && count == sg_len) { 4491c6a0718SPierre Ossman host->pio.index++; 4501c6a0718SPierre Ossman host->pio.offset = 0; 4511c6a0718SPierre Ossman } 4521c6a0718SPierre Ossman 4531c6a0718SPierre Ossman if (host->pio.len == 0) { 4541c6a0718SPierre Ossman //IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); 4551c6a0718SPierre Ossman IRQ_OFF(host, SD_CONFIG_NE); 4561c6a0718SPierre Ossman 4571c6a0718SPierre Ossman if (host->flags & HOST_F_STOP) 4581c6a0718SPierre Ossman SEND_STOP(host); 4591c6a0718SPierre Ossman 4601c6a0718SPierre Ossman tasklet_schedule(&host->data_task); 4611c6a0718SPierre Ossman } 4621c6a0718SPierre Ossman } 4631c6a0718SPierre Ossman 4641c6a0718SPierre Ossman /* static void au1xmmc_cmd_complete 4651c6a0718SPierre Ossman This is called when a command has been completed - grab the response 4661c6a0718SPierre Ossman and check for errors. Then start the data transfer if it is indicated. 4671c6a0718SPierre Ossman */ 4681c6a0718SPierre Ossman 4691c6a0718SPierre Ossman static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) 4701c6a0718SPierre Ossman { 4711c6a0718SPierre Ossman 4721c6a0718SPierre Ossman struct mmc_request *mrq = host->mrq; 4731c6a0718SPierre Ossman struct mmc_command *cmd; 4741c6a0718SPierre Ossman int trans; 4751c6a0718SPierre Ossman 4761c6a0718SPierre Ossman if (!host->mrq) 4771c6a0718SPierre Ossman return; 4781c6a0718SPierre Ossman 4791c6a0718SPierre Ossman cmd = mrq->cmd; 48017b0429dSPierre Ossman cmd->error = 0; 4811c6a0718SPierre Ossman 4821c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_PRESENT) { 4831c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_136) { 4841c6a0718SPierre Ossman u32 r[4]; 4851c6a0718SPierre Ossman int i; 4861c6a0718SPierre Ossman 4871c6a0718SPierre Ossman r[0] = au_readl(host->iobase + SD_RESP3); 4881c6a0718SPierre Ossman r[1] = au_readl(host->iobase + SD_RESP2); 4891c6a0718SPierre Ossman r[2] = au_readl(host->iobase + SD_RESP1); 4901c6a0718SPierre Ossman r[3] = au_readl(host->iobase + SD_RESP0); 4911c6a0718SPierre Ossman 4921c6a0718SPierre Ossman /* The CRC is omitted from the response, so really 4931c6a0718SPierre Ossman * we only got 120 bytes, but the engine expects 4941c6a0718SPierre Ossman * 128 bits, so we have to shift things up 4951c6a0718SPierre Ossman */ 4961c6a0718SPierre Ossman 4971c6a0718SPierre Ossman for(i = 0; i < 4; i++) { 4981c6a0718SPierre Ossman cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8; 4991c6a0718SPierre Ossman if (i != 3) 5001c6a0718SPierre Ossman cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24; 5011c6a0718SPierre Ossman } 5021c6a0718SPierre Ossman } else { 5031c6a0718SPierre Ossman /* Techincally, we should be getting all 48 bits of 5041c6a0718SPierre Ossman * the response (SD_RESP1 + SD_RESP2), but because 5051c6a0718SPierre Ossman * our response omits the CRC, our data ends up 5061c6a0718SPierre Ossman * being shifted 8 bits to the right. In this case, 5071c6a0718SPierre Ossman * that means that the OSR data starts at bit 31, 5081c6a0718SPierre Ossman * so we can just read RESP0 and return that 5091c6a0718SPierre Ossman */ 5101c6a0718SPierre Ossman cmd->resp[0] = au_readl(host->iobase + SD_RESP0); 5111c6a0718SPierre Ossman } 5121c6a0718SPierre Ossman } 5131c6a0718SPierre Ossman 5141c6a0718SPierre Ossman /* Figure out errors */ 5151c6a0718SPierre Ossman 5161c6a0718SPierre Ossman if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC)) 51717b0429dSPierre Ossman cmd->error = -EILSEQ; 5181c6a0718SPierre Ossman 5191c6a0718SPierre Ossman trans = host->flags & (HOST_F_XMIT | HOST_F_RECV); 5201c6a0718SPierre Ossman 52117b0429dSPierre Ossman if (!trans || cmd->error) { 5221c6a0718SPierre Ossman 5231c6a0718SPierre Ossman IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF); 5241c6a0718SPierre Ossman tasklet_schedule(&host->finish_task); 5251c6a0718SPierre Ossman return; 5261c6a0718SPierre Ossman } 5271c6a0718SPierre Ossman 5281c6a0718SPierre Ossman host->status = HOST_S_DATA; 5291c6a0718SPierre Ossman 5301c6a0718SPierre Ossman if (host->flags & HOST_F_DMA) { 5311c6a0718SPierre Ossman u32 channel = DMA_CHANNEL(host); 5321c6a0718SPierre Ossman 5331c6a0718SPierre Ossman /* Start the DMA as soon as the buffer gets something in it */ 5341c6a0718SPierre Ossman 5351c6a0718SPierre Ossman if (host->flags & HOST_F_RECV) { 5361c6a0718SPierre Ossman u32 mask = SD_STATUS_DB | SD_STATUS_NE; 5371c6a0718SPierre Ossman 5381c6a0718SPierre Ossman while((status & mask) != mask) 5391c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 5401c6a0718SPierre Ossman } 5411c6a0718SPierre Ossman 5421c6a0718SPierre Ossman au1xxx_dbdma_start(channel); 5431c6a0718SPierre Ossman } 5441c6a0718SPierre Ossman } 5451c6a0718SPierre Ossman 5461c6a0718SPierre Ossman static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate) 5471c6a0718SPierre Ossman { 5481c6a0718SPierre Ossman 5491c6a0718SPierre Ossman unsigned int pbus = get_au1x00_speed(); 5501c6a0718SPierre Ossman unsigned int divisor; 5511c6a0718SPierre Ossman u32 config; 5521c6a0718SPierre Ossman 5531c6a0718SPierre Ossman /* From databook: 5541c6a0718SPierre Ossman divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1 5551c6a0718SPierre Ossman */ 5561c6a0718SPierre Ossman 5571c6a0718SPierre Ossman pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2); 5581c6a0718SPierre Ossman pbus /= 2; 5591c6a0718SPierre Ossman 5601c6a0718SPierre Ossman divisor = ((pbus / rate) / 2) - 1; 5611c6a0718SPierre Ossman 5621c6a0718SPierre Ossman config = au_readl(HOST_CONFIG(host)); 5631c6a0718SPierre Ossman 5641c6a0718SPierre Ossman config &= ~(SD_CONFIG_DIV); 5651c6a0718SPierre Ossman config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE; 5661c6a0718SPierre Ossman 5671c6a0718SPierre Ossman au_writel(config, HOST_CONFIG(host)); 5681c6a0718SPierre Ossman au_sync(); 5691c6a0718SPierre Ossman } 5701c6a0718SPierre Ossman 5711c6a0718SPierre Ossman static int 5721c6a0718SPierre Ossman au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) 5731c6a0718SPierre Ossman { 5741c6a0718SPierre Ossman 5751c6a0718SPierre Ossman int datalen = data->blocks * data->blksz; 5761c6a0718SPierre Ossman 5771c6a0718SPierre Ossman if (dma != 0) 5781c6a0718SPierre Ossman host->flags |= HOST_F_DMA; 5791c6a0718SPierre Ossman 5801c6a0718SPierre Ossman if (data->flags & MMC_DATA_READ) 5811c6a0718SPierre Ossman host->flags |= HOST_F_RECV; 5821c6a0718SPierre Ossman else 5831c6a0718SPierre Ossman host->flags |= HOST_F_XMIT; 5841c6a0718SPierre Ossman 5851c6a0718SPierre Ossman if (host->mrq->stop) 5861c6a0718SPierre Ossman host->flags |= HOST_F_STOP; 5871c6a0718SPierre Ossman 5881c6a0718SPierre Ossman host->dma.dir = DMA_BIDIRECTIONAL; 5891c6a0718SPierre Ossman 5901c6a0718SPierre Ossman host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, 5911c6a0718SPierre Ossman data->sg_len, host->dma.dir); 5921c6a0718SPierre Ossman 5931c6a0718SPierre Ossman if (host->dma.len == 0) 59417b0429dSPierre Ossman return -ETIMEDOUT; 5951c6a0718SPierre Ossman 5961c6a0718SPierre Ossman au_writel(data->blksz - 1, HOST_BLKSIZE(host)); 5971c6a0718SPierre Ossman 5981c6a0718SPierre Ossman if (host->flags & HOST_F_DMA) { 5991c6a0718SPierre Ossman int i; 6001c6a0718SPierre Ossman u32 channel = DMA_CHANNEL(host); 6011c6a0718SPierre Ossman 6021c6a0718SPierre Ossman au1xxx_dbdma_stop(channel); 6031c6a0718SPierre Ossman 6041c6a0718SPierre Ossman for(i = 0; i < host->dma.len; i++) { 6051c6a0718SPierre Ossman u32 ret = 0, flags = DDMA_FLAGS_NOIE; 6061c6a0718SPierre Ossman struct scatterlist *sg = &data->sg[i]; 6071c6a0718SPierre Ossman int sg_len = sg->length; 6081c6a0718SPierre Ossman 6091c6a0718SPierre Ossman int len = (datalen > sg_len) ? sg_len : datalen; 6101c6a0718SPierre Ossman 6111c6a0718SPierre Ossman if (i == host->dma.len - 1) 6121c6a0718SPierre Ossman flags = DDMA_FLAGS_IE; 6131c6a0718SPierre Ossman 6141c6a0718SPierre Ossman if (host->flags & HOST_F_XMIT){ 6151c6a0718SPierre Ossman ret = au1xxx_dbdma_put_source_flags(channel, 61645711f1aSJens Axboe (void *) sg_virt(sg), len, flags); 6171c6a0718SPierre Ossman } 6181c6a0718SPierre Ossman else { 6191c6a0718SPierre Ossman ret = au1xxx_dbdma_put_dest_flags(channel, 62045711f1aSJens Axboe (void *) sg_virt(sg), 6211c6a0718SPierre Ossman len, flags); 6221c6a0718SPierre Ossman } 6231c6a0718SPierre Ossman 6241c6a0718SPierre Ossman if (!ret) 6251c6a0718SPierre Ossman goto dataerr; 6261c6a0718SPierre Ossman 6271c6a0718SPierre Ossman datalen -= len; 6281c6a0718SPierre Ossman } 6291c6a0718SPierre Ossman } 6301c6a0718SPierre Ossman else { 6311c6a0718SPierre Ossman host->pio.index = 0; 6321c6a0718SPierre Ossman host->pio.offset = 0; 6331c6a0718SPierre Ossman host->pio.len = datalen; 6341c6a0718SPierre Ossman 6351c6a0718SPierre Ossman if (host->flags & HOST_F_XMIT) 6361c6a0718SPierre Ossman IRQ_ON(host, SD_CONFIG_TH); 6371c6a0718SPierre Ossman else 6381c6a0718SPierre Ossman IRQ_ON(host, SD_CONFIG_NE); 6391c6a0718SPierre Ossman //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); 6401c6a0718SPierre Ossman } 6411c6a0718SPierre Ossman 64217b0429dSPierre Ossman return 0; 6431c6a0718SPierre Ossman 6441c6a0718SPierre Ossman dataerr: 6451c6a0718SPierre Ossman dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); 64617b0429dSPierre Ossman return -ETIMEDOUT; 6471c6a0718SPierre Ossman } 6481c6a0718SPierre Ossman 6491c6a0718SPierre Ossman /* static void au1xmmc_request 6501c6a0718SPierre Ossman This actually starts a command or data transaction 6511c6a0718SPierre Ossman */ 6521c6a0718SPierre Ossman 6531c6a0718SPierre Ossman static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq) 6541c6a0718SPierre Ossman { 6551c6a0718SPierre Ossman 6561c6a0718SPierre Ossman struct au1xmmc_host *host = mmc_priv(mmc); 657c0f3b6c7SYoichi Yuasa unsigned int flags = 0; 65817b0429dSPierre Ossman int ret = 0; 6591c6a0718SPierre Ossman 6601c6a0718SPierre Ossman WARN_ON(irqs_disabled()); 6611c6a0718SPierre Ossman WARN_ON(host->status != HOST_S_IDLE); 6621c6a0718SPierre Ossman 6631c6a0718SPierre Ossman host->mrq = mrq; 6641c6a0718SPierre Ossman host->status = HOST_S_CMD; 6651c6a0718SPierre Ossman 6661c6a0718SPierre Ossman bcsr->disk_leds &= ~(1 << 8); 6671c6a0718SPierre Ossman 6681c6a0718SPierre Ossman if (mrq->data) { 6691c6a0718SPierre Ossman FLUSH_FIFO(host); 670c0f3b6c7SYoichi Yuasa flags = mrq->data->flags; 6711c6a0718SPierre Ossman ret = au1xmmc_prepare_data(host, mrq->data); 6721c6a0718SPierre Ossman } 6731c6a0718SPierre Ossman 67417b0429dSPierre Ossman if (!ret) 675be0192aaSPierre Ossman ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data); 6761c6a0718SPierre Ossman 67717b0429dSPierre Ossman if (ret) { 6781c6a0718SPierre Ossman mrq->cmd->error = ret; 6791c6a0718SPierre Ossman au1xmmc_finish_request(host); 6801c6a0718SPierre Ossman } 6811c6a0718SPierre Ossman } 6821c6a0718SPierre Ossman 6831c6a0718SPierre Ossman static void au1xmmc_reset_controller(struct au1xmmc_host *host) 6841c6a0718SPierre Ossman { 6851c6a0718SPierre Ossman 6861c6a0718SPierre Ossman /* Apply the clock */ 6871c6a0718SPierre Ossman au_writel(SD_ENABLE_CE, HOST_ENABLE(host)); 6881c6a0718SPierre Ossman au_sync_delay(1); 6891c6a0718SPierre Ossman 6901c6a0718SPierre Ossman au_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host)); 6911c6a0718SPierre Ossman au_sync_delay(5); 6921c6a0718SPierre Ossman 6931c6a0718SPierre Ossman au_writel(~0, HOST_STATUS(host)); 6941c6a0718SPierre Ossman au_sync(); 6951c6a0718SPierre Ossman 6961c6a0718SPierre Ossman au_writel(0, HOST_BLKSIZE(host)); 6971c6a0718SPierre Ossman au_writel(0x001fffff, HOST_TIMEOUT(host)); 6981c6a0718SPierre Ossman au_sync(); 6991c6a0718SPierre Ossman 7001c6a0718SPierre Ossman au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host)); 7011c6a0718SPierre Ossman au_sync(); 7021c6a0718SPierre Ossman 7031c6a0718SPierre Ossman au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host)); 7041c6a0718SPierre Ossman au_sync_delay(1); 7051c6a0718SPierre Ossman 7061c6a0718SPierre Ossman au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host)); 7071c6a0718SPierre Ossman au_sync(); 7081c6a0718SPierre Ossman 7091c6a0718SPierre Ossman /* Configure interrupts */ 7101c6a0718SPierre Ossman au_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host)); 7111c6a0718SPierre Ossman au_sync(); 7121c6a0718SPierre Ossman } 7131c6a0718SPierre Ossman 7141c6a0718SPierre Ossman 7151c6a0718SPierre Ossman static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) 7161c6a0718SPierre Ossman { 7171c6a0718SPierre Ossman struct au1xmmc_host *host = mmc_priv(mmc); 7181c6a0718SPierre Ossman 7191c6a0718SPierre Ossman if (ios->power_mode == MMC_POWER_OFF) 7201c6a0718SPierre Ossman au1xmmc_set_power(host, 0); 7211c6a0718SPierre Ossman else if (ios->power_mode == MMC_POWER_ON) { 7221c6a0718SPierre Ossman au1xmmc_set_power(host, 1); 7231c6a0718SPierre Ossman } 7241c6a0718SPierre Ossman 7251c6a0718SPierre Ossman if (ios->clock && ios->clock != host->clock) { 7261c6a0718SPierre Ossman au1xmmc_set_clock(host, ios->clock); 7271c6a0718SPierre Ossman host->clock = ios->clock; 7281c6a0718SPierre Ossman } 7291c6a0718SPierre Ossman } 7301c6a0718SPierre Ossman 7311c6a0718SPierre Ossman static void au1xmmc_dma_callback(int irq, void *dev_id) 7321c6a0718SPierre Ossman { 7331c6a0718SPierre Ossman struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id; 7341c6a0718SPierre Ossman 7351c6a0718SPierre Ossman /* Avoid spurious interrupts */ 7361c6a0718SPierre Ossman 7371c6a0718SPierre Ossman if (!host->mrq) 7381c6a0718SPierre Ossman return; 7391c6a0718SPierre Ossman 7401c6a0718SPierre Ossman if (host->flags & HOST_F_STOP) 7411c6a0718SPierre Ossman SEND_STOP(host); 7421c6a0718SPierre Ossman 7431c6a0718SPierre Ossman tasklet_schedule(&host->data_task); 7441c6a0718SPierre Ossman } 7451c6a0718SPierre Ossman 7461c6a0718SPierre Ossman #define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT) 7471c6a0718SPierre Ossman #define STATUS_DATA_IN (SD_STATUS_NE) 7481c6a0718SPierre Ossman #define STATUS_DATA_OUT (SD_STATUS_TH) 7491c6a0718SPierre Ossman 7501c6a0718SPierre Ossman static irqreturn_t au1xmmc_irq(int irq, void *dev_id) 7511c6a0718SPierre Ossman { 7521c6a0718SPierre Ossman 7531c6a0718SPierre Ossman u32 status; 7541c6a0718SPierre Ossman int i, ret = 0; 7551c6a0718SPierre Ossman 7561c6a0718SPierre Ossman disable_irq(AU1100_SD_IRQ); 7571c6a0718SPierre Ossman 7581c6a0718SPierre Ossman for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { 7591c6a0718SPierre Ossman struct au1xmmc_host * host = au1xmmc_hosts[i]; 7601c6a0718SPierre Ossman u32 handled = 1; 7611c6a0718SPierre Ossman 7621c6a0718SPierre Ossman status = au_readl(HOST_STATUS(host)); 7631c6a0718SPierre Ossman 7641c6a0718SPierre Ossman if (host->mrq && (status & STATUS_TIMEOUT)) { 7651c6a0718SPierre Ossman if (status & SD_STATUS_RAT) 76617b0429dSPierre Ossman host->mrq->cmd->error = -ETIMEDOUT; 7671c6a0718SPierre Ossman 7681c6a0718SPierre Ossman else if (status & SD_STATUS_DT) 76917b0429dSPierre Ossman host->mrq->data->error = -ETIMEDOUT; 7701c6a0718SPierre Ossman 7711c6a0718SPierre Ossman /* In PIO mode, interrupts might still be enabled */ 7721c6a0718SPierre Ossman IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); 7731c6a0718SPierre Ossman 7741c6a0718SPierre Ossman //IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF); 7751c6a0718SPierre Ossman tasklet_schedule(&host->finish_task); 7761c6a0718SPierre Ossman } 7771c6a0718SPierre Ossman #if 0 7781c6a0718SPierre Ossman else if (status & SD_STATUS_DD) { 7791c6a0718SPierre Ossman 7801c6a0718SPierre Ossman /* Sometimes we get a DD before a NE in PIO mode */ 7811c6a0718SPierre Ossman 7821c6a0718SPierre Ossman if (!(host->flags & HOST_F_DMA) && 7831c6a0718SPierre Ossman (status & SD_STATUS_NE)) 7841c6a0718SPierre Ossman au1xmmc_receive_pio(host); 7851c6a0718SPierre Ossman else { 7861c6a0718SPierre Ossman au1xmmc_data_complete(host, status); 7871c6a0718SPierre Ossman //tasklet_schedule(&host->data_task); 7881c6a0718SPierre Ossman } 7891c6a0718SPierre Ossman } 7901c6a0718SPierre Ossman #endif 7911c6a0718SPierre Ossman else if (status & (SD_STATUS_CR)) { 7921c6a0718SPierre Ossman if (host->status == HOST_S_CMD) 7931c6a0718SPierre Ossman au1xmmc_cmd_complete(host,status); 7941c6a0718SPierre Ossman } 7951c6a0718SPierre Ossman else if (!(host->flags & HOST_F_DMA)) { 7961c6a0718SPierre Ossman if ((host->flags & HOST_F_XMIT) && 7971c6a0718SPierre Ossman (status & STATUS_DATA_OUT)) 7981c6a0718SPierre Ossman au1xmmc_send_pio(host); 7991c6a0718SPierre Ossman else if ((host->flags & HOST_F_RECV) && 8001c6a0718SPierre Ossman (status & STATUS_DATA_IN)) 8011c6a0718SPierre Ossman au1xmmc_receive_pio(host); 8021c6a0718SPierre Ossman } 8031c6a0718SPierre Ossman else if (status & 0x203FBC70) { 8041c6a0718SPierre Ossman DBG("Unhandled status %8.8x\n", host->id, status); 8051c6a0718SPierre Ossman handled = 0; 8061c6a0718SPierre Ossman } 8071c6a0718SPierre Ossman 8081c6a0718SPierre Ossman au_writel(status, HOST_STATUS(host)); 8091c6a0718SPierre Ossman au_sync(); 8101c6a0718SPierre Ossman 8111c6a0718SPierre Ossman ret |= handled; 8121c6a0718SPierre Ossman } 8131c6a0718SPierre Ossman 8141c6a0718SPierre Ossman enable_irq(AU1100_SD_IRQ); 8151c6a0718SPierre Ossman return ret; 8161c6a0718SPierre Ossman } 8171c6a0718SPierre Ossman 8181c6a0718SPierre Ossman static void au1xmmc_poll_event(unsigned long arg) 8191c6a0718SPierre Ossman { 8201c6a0718SPierre Ossman struct au1xmmc_host *host = (struct au1xmmc_host *) arg; 8211c6a0718SPierre Ossman 8221c6a0718SPierre Ossman int card = au1xmmc_card_inserted(host); 8231c6a0718SPierre Ossman int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0; 8241c6a0718SPierre Ossman 8251c6a0718SPierre Ossman if (card != controller) { 8261c6a0718SPierre Ossman host->flags &= ~HOST_F_ACTIVE; 8271c6a0718SPierre Ossman if (card) host->flags |= HOST_F_ACTIVE; 8281c6a0718SPierre Ossman mmc_detect_change(host->mmc, 0); 8291c6a0718SPierre Ossman } 8301c6a0718SPierre Ossman 8311c6a0718SPierre Ossman if (host->mrq != NULL) { 8321c6a0718SPierre Ossman u32 status = au_readl(HOST_STATUS(host)); 8331c6a0718SPierre Ossman DBG("PENDING - %8.8x\n", host->id, status); 8341c6a0718SPierre Ossman } 8351c6a0718SPierre Ossman 8361c6a0718SPierre Ossman mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); 8371c6a0718SPierre Ossman } 8381c6a0718SPierre Ossman 8391c6a0718SPierre Ossman static dbdev_tab_t au1xmmc_mem_dbdev = 8401c6a0718SPierre Ossman { 8411c6a0718SPierre Ossman DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 8, 0x00000000, 0, 0 8421c6a0718SPierre Ossman }; 8431c6a0718SPierre Ossman 8441c6a0718SPierre Ossman static void au1xmmc_init_dma(struct au1xmmc_host *host) 8451c6a0718SPierre Ossman { 8461c6a0718SPierre Ossman 8471c6a0718SPierre Ossman u32 rxchan, txchan; 8481c6a0718SPierre Ossman 8491c6a0718SPierre Ossman int txid = au1xmmc_card_table[host->id].tx_devid; 8501c6a0718SPierre Ossman int rxid = au1xmmc_card_table[host->id].rx_devid; 8511c6a0718SPierre Ossman 8521c6a0718SPierre Ossman /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride 8531c6a0718SPierre Ossman of 8 bits. And since devices are shared, we need to create 8541c6a0718SPierre Ossman our own to avoid freaking out other devices 8551c6a0718SPierre Ossman */ 8561c6a0718SPierre Ossman 8571c6a0718SPierre Ossman int memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); 8581c6a0718SPierre Ossman 8591c6a0718SPierre Ossman txchan = au1xxx_dbdma_chan_alloc(memid, txid, 8601c6a0718SPierre Ossman au1xmmc_dma_callback, (void *) host); 8611c6a0718SPierre Ossman 8621c6a0718SPierre Ossman rxchan = au1xxx_dbdma_chan_alloc(rxid, memid, 8631c6a0718SPierre Ossman au1xmmc_dma_callback, (void *) host); 8641c6a0718SPierre Ossman 8651c6a0718SPierre Ossman au1xxx_dbdma_set_devwidth(txchan, 8); 8661c6a0718SPierre Ossman au1xxx_dbdma_set_devwidth(rxchan, 8); 8671c6a0718SPierre Ossman 8681c6a0718SPierre Ossman au1xxx_dbdma_ring_alloc(txchan, AU1XMMC_DESCRIPTOR_COUNT); 8691c6a0718SPierre Ossman au1xxx_dbdma_ring_alloc(rxchan, AU1XMMC_DESCRIPTOR_COUNT); 8701c6a0718SPierre Ossman 8711c6a0718SPierre Ossman host->tx_chan = txchan; 8721c6a0718SPierre Ossman host->rx_chan = rxchan; 8731c6a0718SPierre Ossman } 8741c6a0718SPierre Ossman 8751c6a0718SPierre Ossman static const struct mmc_host_ops au1xmmc_ops = { 8761c6a0718SPierre Ossman .request = au1xmmc_request, 8771c6a0718SPierre Ossman .set_ios = au1xmmc_set_ios, 8781c6a0718SPierre Ossman .get_ro = au1xmmc_card_readonly, 8791c6a0718SPierre Ossman }; 8801c6a0718SPierre Ossman 8811c6a0718SPierre Ossman static int __devinit au1xmmc_probe(struct platform_device *pdev) 8821c6a0718SPierre Ossman { 8831c6a0718SPierre Ossman 8841c6a0718SPierre Ossman int i, ret = 0; 8851c6a0718SPierre Ossman 8861c6a0718SPierre Ossman /* THe interrupt is shared among all controllers */ 8871c6a0718SPierre Ossman ret = request_irq(AU1100_SD_IRQ, au1xmmc_irq, IRQF_DISABLED, "MMC", 0); 8881c6a0718SPierre Ossman 8891c6a0718SPierre Ossman if (ret) { 8901c6a0718SPierre Ossman printk(DRIVER_NAME "ERROR: Couldn't get int %d: %d\n", 8911c6a0718SPierre Ossman AU1100_SD_IRQ, ret); 8921c6a0718SPierre Ossman return -ENXIO; 8931c6a0718SPierre Ossman } 8941c6a0718SPierre Ossman 8951c6a0718SPierre Ossman disable_irq(AU1100_SD_IRQ); 8961c6a0718SPierre Ossman 8971c6a0718SPierre Ossman for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { 8981c6a0718SPierre Ossman struct mmc_host *mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); 8991c6a0718SPierre Ossman struct au1xmmc_host *host = 0; 9001c6a0718SPierre Ossman 9011c6a0718SPierre Ossman if (!mmc) { 9021c6a0718SPierre Ossman printk(DRIVER_NAME "ERROR: no mem for host %d\n", i); 9031c6a0718SPierre Ossman au1xmmc_hosts[i] = 0; 9041c6a0718SPierre Ossman continue; 9051c6a0718SPierre Ossman } 9061c6a0718SPierre Ossman 9071c6a0718SPierre Ossman mmc->ops = &au1xmmc_ops; 9081c6a0718SPierre Ossman 9091c6a0718SPierre Ossman mmc->f_min = 450000; 9101c6a0718SPierre Ossman mmc->f_max = 24000000; 9111c6a0718SPierre Ossman 9121c6a0718SPierre Ossman mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; 9131c6a0718SPierre Ossman mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; 9141c6a0718SPierre Ossman 9151c6a0718SPierre Ossman mmc->max_blk_size = 2048; 9161c6a0718SPierre Ossman mmc->max_blk_count = 512; 9171c6a0718SPierre Ossman 9181c6a0718SPierre Ossman mmc->ocr_avail = AU1XMMC_OCR; 9191c6a0718SPierre Ossman 9201c6a0718SPierre Ossman host = mmc_priv(mmc); 9211c6a0718SPierre Ossman host->mmc = mmc; 9221c6a0718SPierre Ossman 9231c6a0718SPierre Ossman host->id = i; 9241c6a0718SPierre Ossman host->iobase = au1xmmc_card_table[host->id].iobase; 9251c6a0718SPierre Ossman host->clock = 0; 9261c6a0718SPierre Ossman host->power_mode = MMC_POWER_OFF; 9271c6a0718SPierre Ossman 9281c6a0718SPierre Ossman host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0; 9291c6a0718SPierre Ossman host->status = HOST_S_IDLE; 9301c6a0718SPierre Ossman 9311c6a0718SPierre Ossman init_timer(&host->timer); 9321c6a0718SPierre Ossman 9331c6a0718SPierre Ossman host->timer.function = au1xmmc_poll_event; 9341c6a0718SPierre Ossman host->timer.data = (unsigned long) host; 9351c6a0718SPierre Ossman host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT; 9361c6a0718SPierre Ossman 9371c6a0718SPierre Ossman tasklet_init(&host->data_task, au1xmmc_tasklet_data, 9381c6a0718SPierre Ossman (unsigned long) host); 9391c6a0718SPierre Ossman 9401c6a0718SPierre Ossman tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, 9411c6a0718SPierre Ossman (unsigned long) host); 9421c6a0718SPierre Ossman 9431c6a0718SPierre Ossman spin_lock_init(&host->lock); 9441c6a0718SPierre Ossman 9451c6a0718SPierre Ossman if (dma != 0) 9461c6a0718SPierre Ossman au1xmmc_init_dma(host); 9471c6a0718SPierre Ossman 9481c6a0718SPierre Ossman au1xmmc_reset_controller(host); 9491c6a0718SPierre Ossman 9501c6a0718SPierre Ossman mmc_add_host(mmc); 9511c6a0718SPierre Ossman au1xmmc_hosts[i] = host; 9521c6a0718SPierre Ossman 9531c6a0718SPierre Ossman add_timer(&host->timer); 9541c6a0718SPierre Ossman 9551c6a0718SPierre Ossman printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X (mode=%s)\n", 9561c6a0718SPierre Ossman host->id, host->iobase, dma ? "dma" : "pio"); 9571c6a0718SPierre Ossman } 9581c6a0718SPierre Ossman 9591c6a0718SPierre Ossman enable_irq(AU1100_SD_IRQ); 9601c6a0718SPierre Ossman 9611c6a0718SPierre Ossman return 0; 9621c6a0718SPierre Ossman } 9631c6a0718SPierre Ossman 9641c6a0718SPierre Ossman static int __devexit au1xmmc_remove(struct platform_device *pdev) 9651c6a0718SPierre Ossman { 9661c6a0718SPierre Ossman 9671c6a0718SPierre Ossman int i; 9681c6a0718SPierre Ossman 9691c6a0718SPierre Ossman disable_irq(AU1100_SD_IRQ); 9701c6a0718SPierre Ossman 9711c6a0718SPierre Ossman for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { 9721c6a0718SPierre Ossman struct au1xmmc_host *host = au1xmmc_hosts[i]; 9731c6a0718SPierre Ossman if (!host) continue; 9741c6a0718SPierre Ossman 9751c6a0718SPierre Ossman tasklet_kill(&host->data_task); 9761c6a0718SPierre Ossman tasklet_kill(&host->finish_task); 9771c6a0718SPierre Ossman 9781c6a0718SPierre Ossman del_timer_sync(&host->timer); 9791c6a0718SPierre Ossman au1xmmc_set_power(host, 0); 9801c6a0718SPierre Ossman 9811c6a0718SPierre Ossman mmc_remove_host(host->mmc); 9821c6a0718SPierre Ossman 9831c6a0718SPierre Ossman au1xxx_dbdma_chan_free(host->tx_chan); 9841c6a0718SPierre Ossman au1xxx_dbdma_chan_free(host->rx_chan); 9851c6a0718SPierre Ossman 9861c6a0718SPierre Ossman au_writel(0x0, HOST_ENABLE(host)); 9871c6a0718SPierre Ossman au_sync(); 9881c6a0718SPierre Ossman } 9891c6a0718SPierre Ossman 9901c6a0718SPierre Ossman free_irq(AU1100_SD_IRQ, 0); 9911c6a0718SPierre Ossman return 0; 9921c6a0718SPierre Ossman } 9931c6a0718SPierre Ossman 9941c6a0718SPierre Ossman static struct platform_driver au1xmmc_driver = { 9951c6a0718SPierre Ossman .probe = au1xmmc_probe, 9961c6a0718SPierre Ossman .remove = au1xmmc_remove, 9971c6a0718SPierre Ossman .suspend = NULL, 9981c6a0718SPierre Ossman .resume = NULL, 9991c6a0718SPierre Ossman .driver = { 10001c6a0718SPierre Ossman .name = DRIVER_NAME, 10011c6a0718SPierre Ossman }, 10021c6a0718SPierre Ossman }; 10031c6a0718SPierre Ossman 10041c6a0718SPierre Ossman static int __init au1xmmc_init(void) 10051c6a0718SPierre Ossman { 10061c6a0718SPierre Ossman return platform_driver_register(&au1xmmc_driver); 10071c6a0718SPierre Ossman } 10081c6a0718SPierre Ossman 10091c6a0718SPierre Ossman static void __exit au1xmmc_exit(void) 10101c6a0718SPierre Ossman { 10111c6a0718SPierre Ossman platform_driver_unregister(&au1xmmc_driver); 10121c6a0718SPierre Ossman } 10131c6a0718SPierre Ossman 10141c6a0718SPierre Ossman module_init(au1xmmc_init); 10151c6a0718SPierre Ossman module_exit(au1xmmc_exit); 10161c6a0718SPierre Ossman 10171c6a0718SPierre Ossman #ifdef MODULE 10181c6a0718SPierre Ossman MODULE_AUTHOR("Advanced Micro Devices, Inc"); 10191c6a0718SPierre Ossman MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX"); 10201c6a0718SPierre Ossman MODULE_LICENSE("GPL"); 10211c6a0718SPierre Ossman #endif 10221c6a0718SPierre Ossman 1023