1a45c6cb8SMadhusudhan Chikkature /* 2a45c6cb8SMadhusudhan Chikkature * drivers/mmc/host/omap_hsmmc.c 3a45c6cb8SMadhusudhan Chikkature * 4a45c6cb8SMadhusudhan Chikkature * Driver for OMAP2430/3430 MMC controller. 5a45c6cb8SMadhusudhan Chikkature * 6a45c6cb8SMadhusudhan Chikkature * Copyright (C) 2007 Texas Instruments. 7a45c6cb8SMadhusudhan Chikkature * 8a45c6cb8SMadhusudhan Chikkature * Authors: 9a45c6cb8SMadhusudhan Chikkature * Syed Mohammed Khasim <x0khasim@ti.com> 10a45c6cb8SMadhusudhan Chikkature * Madhusudhan <madhu.cr@ti.com> 11a45c6cb8SMadhusudhan Chikkature * Mohit Jalori <mjalori@ti.com> 12a45c6cb8SMadhusudhan Chikkature * 13a45c6cb8SMadhusudhan Chikkature * This file is licensed under the terms of the GNU General Public License 14a45c6cb8SMadhusudhan Chikkature * version 2. This program is licensed "as is" without any warranty of any 15a45c6cb8SMadhusudhan Chikkature * kind, whether express or implied. 16a45c6cb8SMadhusudhan Chikkature */ 17a45c6cb8SMadhusudhan Chikkature 18a45c6cb8SMadhusudhan Chikkature #include <linux/module.h> 19a45c6cb8SMadhusudhan Chikkature #include <linux/init.h> 20d900f712SDenis Karpov #include <linux/debugfs.h> 21d900f712SDenis Karpov #include <linux/seq_file.h> 22a45c6cb8SMadhusudhan Chikkature #include <linux/interrupt.h> 23a45c6cb8SMadhusudhan Chikkature #include <linux/delay.h> 24a45c6cb8SMadhusudhan Chikkature #include <linux/dma-mapping.h> 25a45c6cb8SMadhusudhan Chikkature #include <linux/platform_device.h> 26a45c6cb8SMadhusudhan Chikkature #include <linux/workqueue.h> 27a45c6cb8SMadhusudhan Chikkature #include <linux/timer.h> 28a45c6cb8SMadhusudhan Chikkature #include <linux/clk.h> 29a45c6cb8SMadhusudhan Chikkature #include <linux/mmc/host.h> 3013189e78SJarkko Lavinen #include <linux/mmc/core.h> 31a45c6cb8SMadhusudhan Chikkature #include <linux/io.h> 32a45c6cb8SMadhusudhan Chikkature #include <linux/semaphore.h> 33a45c6cb8SMadhusudhan Chikkature #include <mach/dma.h> 34a45c6cb8SMadhusudhan Chikkature #include <mach/hardware.h> 35a45c6cb8SMadhusudhan Chikkature #include <mach/board.h> 36a45c6cb8SMadhusudhan Chikkature #include <mach/mmc.h> 37a45c6cb8SMadhusudhan Chikkature #include <mach/cpu.h> 38a45c6cb8SMadhusudhan Chikkature 39a45c6cb8SMadhusudhan Chikkature /* OMAP HSMMC Host Controller Registers */ 40a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_SYSCONFIG 0x0010 4111dd62a7SDenis Karpov #define OMAP_HSMMC_SYSSTATUS 0x0014 42a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_CON 0x002C 43a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_BLK 0x0104 44a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_ARG 0x0108 45a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_CMD 0x010C 46a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_RSP10 0x0110 47a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_RSP32 0x0114 48a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_RSP54 0x0118 49a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_RSP76 0x011C 50a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_DATA 0x0120 51a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_HCTL 0x0128 52a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_SYSCTL 0x012C 53a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_STAT 0x0130 54a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_IE 0x0134 55a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_ISE 0x0138 56a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_CAPA 0x0140 57a45c6cb8SMadhusudhan Chikkature 58a45c6cb8SMadhusudhan Chikkature #define VS18 (1 << 26) 59a45c6cb8SMadhusudhan Chikkature #define VS30 (1 << 25) 60a45c6cb8SMadhusudhan Chikkature #define SDVS18 (0x5 << 9) 61a45c6cb8SMadhusudhan Chikkature #define SDVS30 (0x6 << 9) 62eb250826SDavid Brownell #define SDVS33 (0x7 << 9) 631b331e69SKim Kyuwon #define SDVS_MASK 0x00000E00 64a45c6cb8SMadhusudhan Chikkature #define SDVSCLR 0xFFFFF1FF 65a45c6cb8SMadhusudhan Chikkature #define SDVSDET 0x00000400 66a45c6cb8SMadhusudhan Chikkature #define AUTOIDLE 0x1 67a45c6cb8SMadhusudhan Chikkature #define SDBP (1 << 8) 68a45c6cb8SMadhusudhan Chikkature #define DTO 0xe 69a45c6cb8SMadhusudhan Chikkature #define ICE 0x1 70a45c6cb8SMadhusudhan Chikkature #define ICS 0x2 71a45c6cb8SMadhusudhan Chikkature #define CEN (1 << 2) 72a45c6cb8SMadhusudhan Chikkature #define CLKD_MASK 0x0000FFC0 73a45c6cb8SMadhusudhan Chikkature #define CLKD_SHIFT 6 74a45c6cb8SMadhusudhan Chikkature #define DTO_MASK 0x000F0000 75a45c6cb8SMadhusudhan Chikkature #define DTO_SHIFT 16 76a45c6cb8SMadhusudhan Chikkature #define INT_EN_MASK 0x307F0033 77ccdfe3a6SAnand Gadiyar #define BWR_ENABLE (1 << 4) 78ccdfe3a6SAnand Gadiyar #define BRR_ENABLE (1 << 5) 79a45c6cb8SMadhusudhan Chikkature #define INIT_STREAM (1 << 1) 80a45c6cb8SMadhusudhan Chikkature #define DP_SELECT (1 << 21) 81a45c6cb8SMadhusudhan Chikkature #define DDIR (1 << 4) 82a45c6cb8SMadhusudhan Chikkature #define DMA_EN 0x1 83a45c6cb8SMadhusudhan Chikkature #define MSBS (1 << 5) 84a45c6cb8SMadhusudhan Chikkature #define BCE (1 << 1) 85a45c6cb8SMadhusudhan Chikkature #define FOUR_BIT (1 << 1) 8673153010SJarkko Lavinen #define DW8 (1 << 5) 87a45c6cb8SMadhusudhan Chikkature #define CC 0x1 88a45c6cb8SMadhusudhan Chikkature #define TC 0x02 89a45c6cb8SMadhusudhan Chikkature #define OD 0x1 90a45c6cb8SMadhusudhan Chikkature #define ERR (1 << 15) 91a45c6cb8SMadhusudhan Chikkature #define CMD_TIMEOUT (1 << 16) 92a45c6cb8SMadhusudhan Chikkature #define DATA_TIMEOUT (1 << 20) 93a45c6cb8SMadhusudhan Chikkature #define CMD_CRC (1 << 17) 94a45c6cb8SMadhusudhan Chikkature #define DATA_CRC (1 << 21) 95a45c6cb8SMadhusudhan Chikkature #define CARD_ERR (1 << 28) 96a45c6cb8SMadhusudhan Chikkature #define STAT_CLEAR 0xFFFFFFFF 97a45c6cb8SMadhusudhan Chikkature #define INIT_STREAM_CMD 0x00000000 98a45c6cb8SMadhusudhan Chikkature #define DUAL_VOLT_OCR_BIT 7 99a45c6cb8SMadhusudhan Chikkature #define SRC (1 << 25) 100a45c6cb8SMadhusudhan Chikkature #define SRD (1 << 26) 10111dd62a7SDenis Karpov #define SOFTRESET (1 << 1) 10211dd62a7SDenis Karpov #define RESETDONE (1 << 0) 103a45c6cb8SMadhusudhan Chikkature 104a45c6cb8SMadhusudhan Chikkature /* 105a45c6cb8SMadhusudhan Chikkature * FIXME: Most likely all the data using these _DEVID defines should come 106a45c6cb8SMadhusudhan Chikkature * from the platform_data, or implemented in controller and slot specific 107a45c6cb8SMadhusudhan Chikkature * functions. 108a45c6cb8SMadhusudhan Chikkature */ 109a45c6cb8SMadhusudhan Chikkature #define OMAP_MMC1_DEVID 0 110a45c6cb8SMadhusudhan Chikkature #define OMAP_MMC2_DEVID 1 111f3e2f1ddSGrazvydas Ignotas #define OMAP_MMC3_DEVID 2 112a45c6cb8SMadhusudhan Chikkature 113a45c6cb8SMadhusudhan Chikkature #define MMC_TIMEOUT_MS 20 114a45c6cb8SMadhusudhan Chikkature #define OMAP_MMC_MASTER_CLOCK 96000000 115a45c6cb8SMadhusudhan Chikkature #define DRIVER_NAME "mmci-omap-hs" 116a45c6cb8SMadhusudhan Chikkature 117dd498effSDenis Karpov /* Timeouts for entering power saving states on inactivity, msec */ 118dd498effSDenis Karpov #define OMAP_MMC_DISABLED_TIMEOUT 100 11913189e78SJarkko Lavinen #define OMAP_MMC_SLEEP_TIMEOUT 1000 12013189e78SJarkko Lavinen #define OMAP_MMC_OFF_TIMEOUT 8000 121dd498effSDenis Karpov 122a45c6cb8SMadhusudhan Chikkature /* 123a45c6cb8SMadhusudhan Chikkature * One controller can have multiple slots, like on some omap boards using 124a45c6cb8SMadhusudhan Chikkature * omap.c controller driver. Luckily this is not currently done on any known 125a45c6cb8SMadhusudhan Chikkature * omap_hsmmc.c device. 126a45c6cb8SMadhusudhan Chikkature */ 127a45c6cb8SMadhusudhan Chikkature #define mmc_slot(host) (host->pdata->slots[host->slot_id]) 128a45c6cb8SMadhusudhan Chikkature 129a45c6cb8SMadhusudhan Chikkature /* 130a45c6cb8SMadhusudhan Chikkature * MMC Host controller read/write API's 131a45c6cb8SMadhusudhan Chikkature */ 132a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_READ(base, reg) \ 133a45c6cb8SMadhusudhan Chikkature __raw_readl((base) + OMAP_HSMMC_##reg) 134a45c6cb8SMadhusudhan Chikkature 135a45c6cb8SMadhusudhan Chikkature #define OMAP_HSMMC_WRITE(base, reg, val) \ 136a45c6cb8SMadhusudhan Chikkature __raw_writel((val), (base) + OMAP_HSMMC_##reg) 137a45c6cb8SMadhusudhan Chikkature 13870a3341aSDenis Karpov struct omap_hsmmc_host { 139a45c6cb8SMadhusudhan Chikkature struct device *dev; 140a45c6cb8SMadhusudhan Chikkature struct mmc_host *mmc; 141a45c6cb8SMadhusudhan Chikkature struct mmc_request *mrq; 142a45c6cb8SMadhusudhan Chikkature struct mmc_command *cmd; 143a45c6cb8SMadhusudhan Chikkature struct mmc_data *data; 144a45c6cb8SMadhusudhan Chikkature struct clk *fclk; 145a45c6cb8SMadhusudhan Chikkature struct clk *iclk; 146a45c6cb8SMadhusudhan Chikkature struct clk *dbclk; 147a45c6cb8SMadhusudhan Chikkature struct semaphore sem; 148a45c6cb8SMadhusudhan Chikkature struct work_struct mmc_carddetect_work; 149a45c6cb8SMadhusudhan Chikkature void __iomem *base; 150a45c6cb8SMadhusudhan Chikkature resource_size_t mapbase; 1514dffd7a2SAdrian Hunter spinlock_t irq_lock; /* Prevent races with irq handler */ 1524dffd7a2SAdrian Hunter unsigned long flags; 153a45c6cb8SMadhusudhan Chikkature unsigned int id; 154a45c6cb8SMadhusudhan Chikkature unsigned int dma_len; 1550ccd76d4SJuha Yrjola unsigned int dma_sg_idx; 156a45c6cb8SMadhusudhan Chikkature unsigned char bus_mode; 157a3621465SAdrian Hunter unsigned char power_mode; 158a45c6cb8SMadhusudhan Chikkature u32 *buffer; 159a45c6cb8SMadhusudhan Chikkature u32 bytesleft; 160a45c6cb8SMadhusudhan Chikkature int suspended; 161a45c6cb8SMadhusudhan Chikkature int irq; 162a45c6cb8SMadhusudhan Chikkature int use_dma, dma_ch; 163f3e2f1ddSGrazvydas Ignotas int dma_line_tx, dma_line_rx; 164a45c6cb8SMadhusudhan Chikkature int slot_id; 165a45c6cb8SMadhusudhan Chikkature int dbclk_enabled; 1664a694dc9SAdrian Hunter int response_busy; 16711dd62a7SDenis Karpov int context_loss; 168dd498effSDenis Karpov int dpm_state; 169623821f7SAdrian Hunter int vdd; 170b62f6228SAdrian Hunter int protect_card; 171b62f6228SAdrian Hunter int reqs_blocked; 17211dd62a7SDenis Karpov 173a45c6cb8SMadhusudhan Chikkature struct omap_mmc_platform_data *pdata; 174a45c6cb8SMadhusudhan Chikkature }; 175a45c6cb8SMadhusudhan Chikkature 176a45c6cb8SMadhusudhan Chikkature /* 177a45c6cb8SMadhusudhan Chikkature * Stop clock to the card 178a45c6cb8SMadhusudhan Chikkature */ 17970a3341aSDenis Karpov static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) 180a45c6cb8SMadhusudhan Chikkature { 181a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, SYSCTL, 182a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); 183a45c6cb8SMadhusudhan Chikkature if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0) 184a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); 185a45c6cb8SMadhusudhan Chikkature } 186a45c6cb8SMadhusudhan Chikkature 18711dd62a7SDenis Karpov #ifdef CONFIG_PM 18811dd62a7SDenis Karpov 18911dd62a7SDenis Karpov /* 19011dd62a7SDenis Karpov * Restore the MMC host context, if it was lost as result of a 19111dd62a7SDenis Karpov * power state change. 19211dd62a7SDenis Karpov */ 19370a3341aSDenis Karpov static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) 19411dd62a7SDenis Karpov { 19511dd62a7SDenis Karpov struct mmc_ios *ios = &host->mmc->ios; 19611dd62a7SDenis Karpov struct omap_mmc_platform_data *pdata = host->pdata; 19711dd62a7SDenis Karpov int context_loss = 0; 19811dd62a7SDenis Karpov u32 hctl, capa, con; 19911dd62a7SDenis Karpov u16 dsor = 0; 20011dd62a7SDenis Karpov unsigned long timeout; 20111dd62a7SDenis Karpov 20211dd62a7SDenis Karpov if (pdata->get_context_loss_count) { 20311dd62a7SDenis Karpov context_loss = pdata->get_context_loss_count(host->dev); 20411dd62a7SDenis Karpov if (context_loss < 0) 20511dd62a7SDenis Karpov return 1; 20611dd62a7SDenis Karpov } 20711dd62a7SDenis Karpov 20811dd62a7SDenis Karpov dev_dbg(mmc_dev(host->mmc), "context was %slost\n", 20911dd62a7SDenis Karpov context_loss == host->context_loss ? "not " : ""); 21011dd62a7SDenis Karpov if (host->context_loss == context_loss) 21111dd62a7SDenis Karpov return 1; 21211dd62a7SDenis Karpov 21311dd62a7SDenis Karpov /* Wait for hardware reset */ 21411dd62a7SDenis Karpov timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); 21511dd62a7SDenis Karpov while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE 21611dd62a7SDenis Karpov && time_before(jiffies, timeout)) 21711dd62a7SDenis Karpov ; 21811dd62a7SDenis Karpov 21911dd62a7SDenis Karpov /* Do software reset */ 22011dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET); 22111dd62a7SDenis Karpov timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); 22211dd62a7SDenis Karpov while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE 22311dd62a7SDenis Karpov && time_before(jiffies, timeout)) 22411dd62a7SDenis Karpov ; 22511dd62a7SDenis Karpov 22611dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, SYSCONFIG, 22711dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); 22811dd62a7SDenis Karpov 22911dd62a7SDenis Karpov if (host->id == OMAP_MMC1_DEVID) { 23011dd62a7SDenis Karpov if (host->power_mode != MMC_POWER_OFF && 23111dd62a7SDenis Karpov (1 << ios->vdd) <= MMC_VDD_23_24) 23211dd62a7SDenis Karpov hctl = SDVS18; 23311dd62a7SDenis Karpov else 23411dd62a7SDenis Karpov hctl = SDVS30; 23511dd62a7SDenis Karpov capa = VS30 | VS18; 23611dd62a7SDenis Karpov } else { 23711dd62a7SDenis Karpov hctl = SDVS18; 23811dd62a7SDenis Karpov capa = VS18; 23911dd62a7SDenis Karpov } 24011dd62a7SDenis Karpov 24111dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, HCTL, 24211dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, HCTL) | hctl); 24311dd62a7SDenis Karpov 24411dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, CAPA, 24511dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, CAPA) | capa); 24611dd62a7SDenis Karpov 24711dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, HCTL, 24811dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, HCTL) | SDBP); 24911dd62a7SDenis Karpov 25011dd62a7SDenis Karpov timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); 25111dd62a7SDenis Karpov while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP 25211dd62a7SDenis Karpov && time_before(jiffies, timeout)) 25311dd62a7SDenis Karpov ; 25411dd62a7SDenis Karpov 25511dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 25611dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); 25711dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); 25811dd62a7SDenis Karpov 25911dd62a7SDenis Karpov /* Do not initialize card-specific things if the power is off */ 26011dd62a7SDenis Karpov if (host->power_mode == MMC_POWER_OFF) 26111dd62a7SDenis Karpov goto out; 26211dd62a7SDenis Karpov 26311dd62a7SDenis Karpov con = OMAP_HSMMC_READ(host->base, CON); 26411dd62a7SDenis Karpov switch (ios->bus_width) { 26511dd62a7SDenis Karpov case MMC_BUS_WIDTH_8: 26611dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con | DW8); 26711dd62a7SDenis Karpov break; 26811dd62a7SDenis Karpov case MMC_BUS_WIDTH_4: 26911dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); 27011dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, HCTL, 27111dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); 27211dd62a7SDenis Karpov break; 27311dd62a7SDenis Karpov case MMC_BUS_WIDTH_1: 27411dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); 27511dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, HCTL, 27611dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); 27711dd62a7SDenis Karpov break; 27811dd62a7SDenis Karpov } 27911dd62a7SDenis Karpov 28011dd62a7SDenis Karpov if (ios->clock) { 28111dd62a7SDenis Karpov dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; 28211dd62a7SDenis Karpov if (dsor < 1) 28311dd62a7SDenis Karpov dsor = 1; 28411dd62a7SDenis Karpov 28511dd62a7SDenis Karpov if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) 28611dd62a7SDenis Karpov dsor++; 28711dd62a7SDenis Karpov 28811dd62a7SDenis Karpov if (dsor > 250) 28911dd62a7SDenis Karpov dsor = 250; 29011dd62a7SDenis Karpov } 29111dd62a7SDenis Karpov 29211dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, SYSCTL, 29311dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); 29411dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16)); 29511dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, SYSCTL, 29611dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); 29711dd62a7SDenis Karpov 29811dd62a7SDenis Karpov timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); 29911dd62a7SDenis Karpov while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS 30011dd62a7SDenis Karpov && time_before(jiffies, timeout)) 30111dd62a7SDenis Karpov ; 30211dd62a7SDenis Karpov 30311dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, SYSCTL, 30411dd62a7SDenis Karpov OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); 30511dd62a7SDenis Karpov 30611dd62a7SDenis Karpov con = OMAP_HSMMC_READ(host->base, CON); 30711dd62a7SDenis Karpov if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) 30811dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con | OD); 30911dd62a7SDenis Karpov else 31011dd62a7SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); 31111dd62a7SDenis Karpov out: 31211dd62a7SDenis Karpov host->context_loss = context_loss; 31311dd62a7SDenis Karpov 31411dd62a7SDenis Karpov dev_dbg(mmc_dev(host->mmc), "context is restored\n"); 31511dd62a7SDenis Karpov return 0; 31611dd62a7SDenis Karpov } 31711dd62a7SDenis Karpov 31811dd62a7SDenis Karpov /* 31911dd62a7SDenis Karpov * Save the MMC host context (store the number of power state changes so far). 32011dd62a7SDenis Karpov */ 32170a3341aSDenis Karpov static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) 32211dd62a7SDenis Karpov { 32311dd62a7SDenis Karpov struct omap_mmc_platform_data *pdata = host->pdata; 32411dd62a7SDenis Karpov int context_loss; 32511dd62a7SDenis Karpov 32611dd62a7SDenis Karpov if (pdata->get_context_loss_count) { 32711dd62a7SDenis Karpov context_loss = pdata->get_context_loss_count(host->dev); 32811dd62a7SDenis Karpov if (context_loss < 0) 32911dd62a7SDenis Karpov return; 33011dd62a7SDenis Karpov host->context_loss = context_loss; 33111dd62a7SDenis Karpov } 33211dd62a7SDenis Karpov } 33311dd62a7SDenis Karpov 33411dd62a7SDenis Karpov #else 33511dd62a7SDenis Karpov 33670a3341aSDenis Karpov static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) 33711dd62a7SDenis Karpov { 33811dd62a7SDenis Karpov return 0; 33911dd62a7SDenis Karpov } 34011dd62a7SDenis Karpov 34170a3341aSDenis Karpov static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) 34211dd62a7SDenis Karpov { 34311dd62a7SDenis Karpov } 34411dd62a7SDenis Karpov 34511dd62a7SDenis Karpov #endif 34611dd62a7SDenis Karpov 347a45c6cb8SMadhusudhan Chikkature /* 348a45c6cb8SMadhusudhan Chikkature * Send init stream sequence to card 349a45c6cb8SMadhusudhan Chikkature * before sending IDLE command 350a45c6cb8SMadhusudhan Chikkature */ 35170a3341aSDenis Karpov static void send_init_stream(struct omap_hsmmc_host *host) 352a45c6cb8SMadhusudhan Chikkature { 353a45c6cb8SMadhusudhan Chikkature int reg = 0; 354a45c6cb8SMadhusudhan Chikkature unsigned long timeout; 355a45c6cb8SMadhusudhan Chikkature 356b62f6228SAdrian Hunter if (host->protect_card) 357b62f6228SAdrian Hunter return; 358b62f6228SAdrian Hunter 359a45c6cb8SMadhusudhan Chikkature disable_irq(host->irq); 360a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, CON, 361a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM); 362a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD); 363a45c6cb8SMadhusudhan Chikkature 364a45c6cb8SMadhusudhan Chikkature timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); 365a45c6cb8SMadhusudhan Chikkature while ((reg != CC) && time_before(jiffies, timeout)) 366a45c6cb8SMadhusudhan Chikkature reg = OMAP_HSMMC_READ(host->base, STAT) & CC; 367a45c6cb8SMadhusudhan Chikkature 368a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, CON, 369a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM); 370c653a6d4SAdrian Hunter 371c653a6d4SAdrian Hunter OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 372c653a6d4SAdrian Hunter OMAP_HSMMC_READ(host->base, STAT); 373c653a6d4SAdrian Hunter 374a45c6cb8SMadhusudhan Chikkature enable_irq(host->irq); 375a45c6cb8SMadhusudhan Chikkature } 376a45c6cb8SMadhusudhan Chikkature 377a45c6cb8SMadhusudhan Chikkature static inline 37870a3341aSDenis Karpov int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host) 379a45c6cb8SMadhusudhan Chikkature { 380a45c6cb8SMadhusudhan Chikkature int r = 1; 381a45c6cb8SMadhusudhan Chikkature 382191d1f1dSDenis Karpov if (mmc_slot(host).get_cover_state) 383191d1f1dSDenis Karpov r = mmc_slot(host).get_cover_state(host->dev, host->slot_id); 384a45c6cb8SMadhusudhan Chikkature return r; 385a45c6cb8SMadhusudhan Chikkature } 386a45c6cb8SMadhusudhan Chikkature 387a45c6cb8SMadhusudhan Chikkature static ssize_t 38870a3341aSDenis Karpov omap_hsmmc_show_cover_switch(struct device *dev, struct device_attribute *attr, 389a45c6cb8SMadhusudhan Chikkature char *buf) 390a45c6cb8SMadhusudhan Chikkature { 391a45c6cb8SMadhusudhan Chikkature struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); 39270a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 393a45c6cb8SMadhusudhan Chikkature 39470a3341aSDenis Karpov return sprintf(buf, "%s\n", 39570a3341aSDenis Karpov omap_hsmmc_cover_is_closed(host) ? "closed" : "open"); 396a45c6cb8SMadhusudhan Chikkature } 397a45c6cb8SMadhusudhan Chikkature 39870a3341aSDenis Karpov static DEVICE_ATTR(cover_switch, S_IRUGO, omap_hsmmc_show_cover_switch, NULL); 399a45c6cb8SMadhusudhan Chikkature 400a45c6cb8SMadhusudhan Chikkature static ssize_t 40170a3341aSDenis Karpov omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr, 402a45c6cb8SMadhusudhan Chikkature char *buf) 403a45c6cb8SMadhusudhan Chikkature { 404a45c6cb8SMadhusudhan Chikkature struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); 40570a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 406a45c6cb8SMadhusudhan Chikkature 407191d1f1dSDenis Karpov return sprintf(buf, "%s\n", mmc_slot(host).name); 408a45c6cb8SMadhusudhan Chikkature } 409a45c6cb8SMadhusudhan Chikkature 41070a3341aSDenis Karpov static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); 411a45c6cb8SMadhusudhan Chikkature 412a45c6cb8SMadhusudhan Chikkature /* 413a45c6cb8SMadhusudhan Chikkature * Configure the response type and send the cmd. 414a45c6cb8SMadhusudhan Chikkature */ 415a45c6cb8SMadhusudhan Chikkature static void 41670a3341aSDenis Karpov omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, 417a45c6cb8SMadhusudhan Chikkature struct mmc_data *data) 418a45c6cb8SMadhusudhan Chikkature { 419a45c6cb8SMadhusudhan Chikkature int cmdreg = 0, resptype = 0, cmdtype = 0; 420a45c6cb8SMadhusudhan Chikkature 421a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n", 422a45c6cb8SMadhusudhan Chikkature mmc_hostname(host->mmc), cmd->opcode, cmd->arg); 423a45c6cb8SMadhusudhan Chikkature host->cmd = cmd; 424a45c6cb8SMadhusudhan Chikkature 425a45c6cb8SMadhusudhan Chikkature /* 426a45c6cb8SMadhusudhan Chikkature * Clear status bits and enable interrupts 427a45c6cb8SMadhusudhan Chikkature */ 428a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 429a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); 430ccdfe3a6SAnand Gadiyar 431ccdfe3a6SAnand Gadiyar if (host->use_dma) 432ccdfe3a6SAnand Gadiyar OMAP_HSMMC_WRITE(host->base, IE, 433ccdfe3a6SAnand Gadiyar INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE)); 434ccdfe3a6SAnand Gadiyar else 435a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); 436a45c6cb8SMadhusudhan Chikkature 4374a694dc9SAdrian Hunter host->response_busy = 0; 438a45c6cb8SMadhusudhan Chikkature if (cmd->flags & MMC_RSP_PRESENT) { 439a45c6cb8SMadhusudhan Chikkature if (cmd->flags & MMC_RSP_136) 440a45c6cb8SMadhusudhan Chikkature resptype = 1; 4414a694dc9SAdrian Hunter else if (cmd->flags & MMC_RSP_BUSY) { 4424a694dc9SAdrian Hunter resptype = 3; 4434a694dc9SAdrian Hunter host->response_busy = 1; 4444a694dc9SAdrian Hunter } else 445a45c6cb8SMadhusudhan Chikkature resptype = 2; 446a45c6cb8SMadhusudhan Chikkature } 447a45c6cb8SMadhusudhan Chikkature 448a45c6cb8SMadhusudhan Chikkature /* 449a45c6cb8SMadhusudhan Chikkature * Unlike OMAP1 controller, the cmdtype does not seem to be based on 450a45c6cb8SMadhusudhan Chikkature * ac, bc, adtc, bcr. Only commands ending an open ended transfer need 451a45c6cb8SMadhusudhan Chikkature * a val of 0x3, rest 0x0. 452a45c6cb8SMadhusudhan Chikkature */ 453a45c6cb8SMadhusudhan Chikkature if (cmd == host->mrq->stop) 454a45c6cb8SMadhusudhan Chikkature cmdtype = 0x3; 455a45c6cb8SMadhusudhan Chikkature 456a45c6cb8SMadhusudhan Chikkature cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); 457a45c6cb8SMadhusudhan Chikkature 458a45c6cb8SMadhusudhan Chikkature if (data) { 459a45c6cb8SMadhusudhan Chikkature cmdreg |= DP_SELECT | MSBS | BCE; 460a45c6cb8SMadhusudhan Chikkature if (data->flags & MMC_DATA_READ) 461a45c6cb8SMadhusudhan Chikkature cmdreg |= DDIR; 462a45c6cb8SMadhusudhan Chikkature else 463a45c6cb8SMadhusudhan Chikkature cmdreg &= ~(DDIR); 464a45c6cb8SMadhusudhan Chikkature } 465a45c6cb8SMadhusudhan Chikkature 466a45c6cb8SMadhusudhan Chikkature if (host->use_dma) 467a45c6cb8SMadhusudhan Chikkature cmdreg |= DMA_EN; 468a45c6cb8SMadhusudhan Chikkature 4694dffd7a2SAdrian Hunter /* 4704dffd7a2SAdrian Hunter * In an interrupt context (i.e. STOP command), the spinlock is unlocked 4714dffd7a2SAdrian Hunter * by the interrupt handler, otherwise (i.e. for a new request) it is 4724dffd7a2SAdrian Hunter * unlocked here. 4734dffd7a2SAdrian Hunter */ 4744dffd7a2SAdrian Hunter if (!in_interrupt()) 4754dffd7a2SAdrian Hunter spin_unlock_irqrestore(&host->irq_lock, host->flags); 4764dffd7a2SAdrian Hunter 477a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg); 478a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); 479a45c6cb8SMadhusudhan Chikkature } 480a45c6cb8SMadhusudhan Chikkature 4810ccd76d4SJuha Yrjola static int 48270a3341aSDenis Karpov omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data) 4830ccd76d4SJuha Yrjola { 4840ccd76d4SJuha Yrjola if (data->flags & MMC_DATA_WRITE) 4850ccd76d4SJuha Yrjola return DMA_TO_DEVICE; 4860ccd76d4SJuha Yrjola else 4870ccd76d4SJuha Yrjola return DMA_FROM_DEVICE; 4880ccd76d4SJuha Yrjola } 4890ccd76d4SJuha Yrjola 490a45c6cb8SMadhusudhan Chikkature /* 491a45c6cb8SMadhusudhan Chikkature * Notify the transfer complete to MMC core 492a45c6cb8SMadhusudhan Chikkature */ 493a45c6cb8SMadhusudhan Chikkature static void 49470a3341aSDenis Karpov omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) 495a45c6cb8SMadhusudhan Chikkature { 4964a694dc9SAdrian Hunter if (!data) { 4974a694dc9SAdrian Hunter struct mmc_request *mrq = host->mrq; 4984a694dc9SAdrian Hunter 49923050103SAdrian Hunter /* TC before CC from CMD6 - don't know why, but it happens */ 50023050103SAdrian Hunter if (host->cmd && host->cmd->opcode == 6 && 50123050103SAdrian Hunter host->response_busy) { 50223050103SAdrian Hunter host->response_busy = 0; 50323050103SAdrian Hunter return; 50423050103SAdrian Hunter } 50523050103SAdrian Hunter 5064a694dc9SAdrian Hunter host->mrq = NULL; 5074a694dc9SAdrian Hunter mmc_request_done(host->mmc, mrq); 5084a694dc9SAdrian Hunter return; 5094a694dc9SAdrian Hunter } 5104a694dc9SAdrian Hunter 511a45c6cb8SMadhusudhan Chikkature host->data = NULL; 512a45c6cb8SMadhusudhan Chikkature 513a45c6cb8SMadhusudhan Chikkature if (host->use_dma && host->dma_ch != -1) 514a45c6cb8SMadhusudhan Chikkature dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, 51570a3341aSDenis Karpov omap_hsmmc_get_dma_dir(host, data)); 516a45c6cb8SMadhusudhan Chikkature 517a45c6cb8SMadhusudhan Chikkature if (!data->error) 518a45c6cb8SMadhusudhan Chikkature data->bytes_xfered += data->blocks * (data->blksz); 519a45c6cb8SMadhusudhan Chikkature else 520a45c6cb8SMadhusudhan Chikkature data->bytes_xfered = 0; 521a45c6cb8SMadhusudhan Chikkature 522a45c6cb8SMadhusudhan Chikkature if (!data->stop) { 523a45c6cb8SMadhusudhan Chikkature host->mrq = NULL; 524a45c6cb8SMadhusudhan Chikkature mmc_request_done(host->mmc, data->mrq); 525a45c6cb8SMadhusudhan Chikkature return; 526a45c6cb8SMadhusudhan Chikkature } 52770a3341aSDenis Karpov omap_hsmmc_start_command(host, data->stop, NULL); 528a45c6cb8SMadhusudhan Chikkature } 529a45c6cb8SMadhusudhan Chikkature 530a45c6cb8SMadhusudhan Chikkature /* 531a45c6cb8SMadhusudhan Chikkature * Notify the core about command completion 532a45c6cb8SMadhusudhan Chikkature */ 533a45c6cb8SMadhusudhan Chikkature static void 53470a3341aSDenis Karpov omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) 535a45c6cb8SMadhusudhan Chikkature { 536a45c6cb8SMadhusudhan Chikkature host->cmd = NULL; 537a45c6cb8SMadhusudhan Chikkature 538a45c6cb8SMadhusudhan Chikkature if (cmd->flags & MMC_RSP_PRESENT) { 539a45c6cb8SMadhusudhan Chikkature if (cmd->flags & MMC_RSP_136) { 540a45c6cb8SMadhusudhan Chikkature /* response type 2 */ 541a45c6cb8SMadhusudhan Chikkature cmd->resp[3] = OMAP_HSMMC_READ(host->base, RSP10); 542a45c6cb8SMadhusudhan Chikkature cmd->resp[2] = OMAP_HSMMC_READ(host->base, RSP32); 543a45c6cb8SMadhusudhan Chikkature cmd->resp[1] = OMAP_HSMMC_READ(host->base, RSP54); 544a45c6cb8SMadhusudhan Chikkature cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP76); 545a45c6cb8SMadhusudhan Chikkature } else { 546a45c6cb8SMadhusudhan Chikkature /* response types 1, 1b, 3, 4, 5, 6 */ 547a45c6cb8SMadhusudhan Chikkature cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10); 548a45c6cb8SMadhusudhan Chikkature } 549a45c6cb8SMadhusudhan Chikkature } 5504a694dc9SAdrian Hunter if ((host->data == NULL && !host->response_busy) || cmd->error) { 551a45c6cb8SMadhusudhan Chikkature host->mrq = NULL; 552a45c6cb8SMadhusudhan Chikkature mmc_request_done(host->mmc, cmd->mrq); 553a45c6cb8SMadhusudhan Chikkature } 554a45c6cb8SMadhusudhan Chikkature } 555a45c6cb8SMadhusudhan Chikkature 556a45c6cb8SMadhusudhan Chikkature /* 557a45c6cb8SMadhusudhan Chikkature * DMA clean up for command errors 558a45c6cb8SMadhusudhan Chikkature */ 55970a3341aSDenis Karpov static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) 560a45c6cb8SMadhusudhan Chikkature { 56182788ff5SJarkko Lavinen host->data->error = errno; 562a45c6cb8SMadhusudhan Chikkature 563a45c6cb8SMadhusudhan Chikkature if (host->use_dma && host->dma_ch != -1) { 564a45c6cb8SMadhusudhan Chikkature dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, 56570a3341aSDenis Karpov omap_hsmmc_get_dma_dir(host, host->data)); 566a45c6cb8SMadhusudhan Chikkature omap_free_dma(host->dma_ch); 567a45c6cb8SMadhusudhan Chikkature host->dma_ch = -1; 568a45c6cb8SMadhusudhan Chikkature up(&host->sem); 569a45c6cb8SMadhusudhan Chikkature } 570a45c6cb8SMadhusudhan Chikkature host->data = NULL; 571a45c6cb8SMadhusudhan Chikkature } 572a45c6cb8SMadhusudhan Chikkature 573a45c6cb8SMadhusudhan Chikkature /* 574a45c6cb8SMadhusudhan Chikkature * Readable error output 575a45c6cb8SMadhusudhan Chikkature */ 576a45c6cb8SMadhusudhan Chikkature #ifdef CONFIG_MMC_DEBUG 57770a3341aSDenis Karpov static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status) 578a45c6cb8SMadhusudhan Chikkature { 579a45c6cb8SMadhusudhan Chikkature /* --- means reserved bit without definition at documentation */ 58070a3341aSDenis Karpov static const char *omap_hsmmc_status_bits[] = { 581a45c6cb8SMadhusudhan Chikkature "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ", 582a45c6cb8SMadhusudhan Chikkature "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC", 583a45c6cb8SMadhusudhan Chikkature "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---", 584a45c6cb8SMadhusudhan Chikkature "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---" 585a45c6cb8SMadhusudhan Chikkature }; 586a45c6cb8SMadhusudhan Chikkature char res[256]; 587a45c6cb8SMadhusudhan Chikkature char *buf = res; 588a45c6cb8SMadhusudhan Chikkature int len, i; 589a45c6cb8SMadhusudhan Chikkature 590a45c6cb8SMadhusudhan Chikkature len = sprintf(buf, "MMC IRQ 0x%x :", status); 591a45c6cb8SMadhusudhan Chikkature buf += len; 592a45c6cb8SMadhusudhan Chikkature 59370a3341aSDenis Karpov for (i = 0; i < ARRAY_SIZE(omap_hsmmc_status_bits); i++) 594a45c6cb8SMadhusudhan Chikkature if (status & (1 << i)) { 59570a3341aSDenis Karpov len = sprintf(buf, " %s", omap_hsmmc_status_bits[i]); 596a45c6cb8SMadhusudhan Chikkature buf += len; 597a45c6cb8SMadhusudhan Chikkature } 598a45c6cb8SMadhusudhan Chikkature 599a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "%s\n", res); 600a45c6cb8SMadhusudhan Chikkature } 601a45c6cb8SMadhusudhan Chikkature #endif /* CONFIG_MMC_DEBUG */ 602a45c6cb8SMadhusudhan Chikkature 6033ebf74b1SJean Pihet /* 6043ebf74b1SJean Pihet * MMC controller internal state machines reset 6053ebf74b1SJean Pihet * 6063ebf74b1SJean Pihet * Used to reset command or data internal state machines, using respectively 6073ebf74b1SJean Pihet * SRC or SRD bit of SYSCTL register 6083ebf74b1SJean Pihet * Can be called from interrupt context 6093ebf74b1SJean Pihet */ 61070a3341aSDenis Karpov static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, 6113ebf74b1SJean Pihet unsigned long bit) 6123ebf74b1SJean Pihet { 6133ebf74b1SJean Pihet unsigned long i = 0; 6143ebf74b1SJean Pihet unsigned long limit = (loops_per_jiffy * 6153ebf74b1SJean Pihet msecs_to_jiffies(MMC_TIMEOUT_MS)); 6163ebf74b1SJean Pihet 6173ebf74b1SJean Pihet OMAP_HSMMC_WRITE(host->base, SYSCTL, 6183ebf74b1SJean Pihet OMAP_HSMMC_READ(host->base, SYSCTL) | bit); 6193ebf74b1SJean Pihet 6203ebf74b1SJean Pihet while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && 6213ebf74b1SJean Pihet (i++ < limit)) 6223ebf74b1SJean Pihet cpu_relax(); 6233ebf74b1SJean Pihet 6243ebf74b1SJean Pihet if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) 6253ebf74b1SJean Pihet dev_err(mmc_dev(host->mmc), 6263ebf74b1SJean Pihet "Timeout waiting on controller reset in %s\n", 6273ebf74b1SJean Pihet __func__); 6283ebf74b1SJean Pihet } 629a45c6cb8SMadhusudhan Chikkature 630a45c6cb8SMadhusudhan Chikkature /* 631a45c6cb8SMadhusudhan Chikkature * MMC controller IRQ handler 632a45c6cb8SMadhusudhan Chikkature */ 63370a3341aSDenis Karpov static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) 634a45c6cb8SMadhusudhan Chikkature { 63570a3341aSDenis Karpov struct omap_hsmmc_host *host = dev_id; 636a45c6cb8SMadhusudhan Chikkature struct mmc_data *data; 637a45c6cb8SMadhusudhan Chikkature int end_cmd = 0, end_trans = 0, status; 638a45c6cb8SMadhusudhan Chikkature 6394dffd7a2SAdrian Hunter spin_lock(&host->irq_lock); 6404dffd7a2SAdrian Hunter 6414a694dc9SAdrian Hunter if (host->mrq == NULL) { 642a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, STAT, 643a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, STAT)); 64400adadc1SKevin Hilman /* Flush posted write */ 64500adadc1SKevin Hilman OMAP_HSMMC_READ(host->base, STAT); 6464dffd7a2SAdrian Hunter spin_unlock(&host->irq_lock); 647a45c6cb8SMadhusudhan Chikkature return IRQ_HANDLED; 648a45c6cb8SMadhusudhan Chikkature } 649a45c6cb8SMadhusudhan Chikkature 650a45c6cb8SMadhusudhan Chikkature data = host->data; 651a45c6cb8SMadhusudhan Chikkature status = OMAP_HSMMC_READ(host->base, STAT); 652a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); 653a45c6cb8SMadhusudhan Chikkature 654a45c6cb8SMadhusudhan Chikkature if (status & ERR) { 655a45c6cb8SMadhusudhan Chikkature #ifdef CONFIG_MMC_DEBUG 65670a3341aSDenis Karpov omap_hsmmc_report_irq(host, status); 657a45c6cb8SMadhusudhan Chikkature #endif 658a45c6cb8SMadhusudhan Chikkature if ((status & CMD_TIMEOUT) || 659a45c6cb8SMadhusudhan Chikkature (status & CMD_CRC)) { 660a45c6cb8SMadhusudhan Chikkature if (host->cmd) { 661a45c6cb8SMadhusudhan Chikkature if (status & CMD_TIMEOUT) { 66270a3341aSDenis Karpov omap_hsmmc_reset_controller_fsm(host, 663191d1f1dSDenis Karpov SRC); 664a45c6cb8SMadhusudhan Chikkature host->cmd->error = -ETIMEDOUT; 665a45c6cb8SMadhusudhan Chikkature } else { 666a45c6cb8SMadhusudhan Chikkature host->cmd->error = -EILSEQ; 667a45c6cb8SMadhusudhan Chikkature } 668a45c6cb8SMadhusudhan Chikkature end_cmd = 1; 669a45c6cb8SMadhusudhan Chikkature } 6704a694dc9SAdrian Hunter if (host->data || host->response_busy) { 6714a694dc9SAdrian Hunter if (host->data) 67270a3341aSDenis Karpov omap_hsmmc_dma_cleanup(host, 67370a3341aSDenis Karpov -ETIMEDOUT); 6744a694dc9SAdrian Hunter host->response_busy = 0; 67570a3341aSDenis Karpov omap_hsmmc_reset_controller_fsm(host, SRD); 676c232f457SJean Pihet } 677a45c6cb8SMadhusudhan Chikkature } 678a45c6cb8SMadhusudhan Chikkature if ((status & DATA_TIMEOUT) || 679a45c6cb8SMadhusudhan Chikkature (status & DATA_CRC)) { 6804a694dc9SAdrian Hunter if (host->data || host->response_busy) { 6814a694dc9SAdrian Hunter int err = (status & DATA_TIMEOUT) ? 6824a694dc9SAdrian Hunter -ETIMEDOUT : -EILSEQ; 6834a694dc9SAdrian Hunter 6844a694dc9SAdrian Hunter if (host->data) 68570a3341aSDenis Karpov omap_hsmmc_dma_cleanup(host, err); 686a45c6cb8SMadhusudhan Chikkature else 6874a694dc9SAdrian Hunter host->mrq->cmd->error = err; 6884a694dc9SAdrian Hunter host->response_busy = 0; 68970a3341aSDenis Karpov omap_hsmmc_reset_controller_fsm(host, SRD); 690a45c6cb8SMadhusudhan Chikkature end_trans = 1; 691a45c6cb8SMadhusudhan Chikkature } 692a45c6cb8SMadhusudhan Chikkature } 693a45c6cb8SMadhusudhan Chikkature if (status & CARD_ERR) { 694a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 695a45c6cb8SMadhusudhan Chikkature "Ignoring card err CMD%d\n", host->cmd->opcode); 696a45c6cb8SMadhusudhan Chikkature if (host->cmd) 697a45c6cb8SMadhusudhan Chikkature end_cmd = 1; 698a45c6cb8SMadhusudhan Chikkature if (host->data) 699a45c6cb8SMadhusudhan Chikkature end_trans = 1; 700a45c6cb8SMadhusudhan Chikkature } 701a45c6cb8SMadhusudhan Chikkature } 702a45c6cb8SMadhusudhan Chikkature 703a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, STAT, status); 70400adadc1SKevin Hilman /* Flush posted write */ 70500adadc1SKevin Hilman OMAP_HSMMC_READ(host->base, STAT); 706a45c6cb8SMadhusudhan Chikkature 707a8fe29d8SJarkko Lavinen if (end_cmd || ((status & CC) && host->cmd)) 70870a3341aSDenis Karpov omap_hsmmc_cmd_done(host, host->cmd); 7090a40e647SJarkko Lavinen if ((end_trans || (status & TC)) && host->mrq) 71070a3341aSDenis Karpov omap_hsmmc_xfer_done(host, data); 711a45c6cb8SMadhusudhan Chikkature 7124dffd7a2SAdrian Hunter spin_unlock(&host->irq_lock); 7134dffd7a2SAdrian Hunter 714a45c6cb8SMadhusudhan Chikkature return IRQ_HANDLED; 715a45c6cb8SMadhusudhan Chikkature } 716a45c6cb8SMadhusudhan Chikkature 71770a3341aSDenis Karpov static void set_sd_bus_power(struct omap_hsmmc_host *host) 718e13bb300SAdrian Hunter { 719e13bb300SAdrian Hunter unsigned long i; 720e13bb300SAdrian Hunter 721e13bb300SAdrian Hunter OMAP_HSMMC_WRITE(host->base, HCTL, 722e13bb300SAdrian Hunter OMAP_HSMMC_READ(host->base, HCTL) | SDBP); 723e13bb300SAdrian Hunter for (i = 0; i < loops_per_jiffy; i++) { 724e13bb300SAdrian Hunter if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP) 725e13bb300SAdrian Hunter break; 726e13bb300SAdrian Hunter cpu_relax(); 727e13bb300SAdrian Hunter } 728e13bb300SAdrian Hunter } 729e13bb300SAdrian Hunter 730a45c6cb8SMadhusudhan Chikkature /* 731eb250826SDavid Brownell * Switch MMC interface voltage ... only relevant for MMC1. 732eb250826SDavid Brownell * 733eb250826SDavid Brownell * MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver. 734eb250826SDavid Brownell * The MMC2 transceiver controls are used instead of DAT4..DAT7. 735eb250826SDavid Brownell * Some chips, like eMMC ones, use internal transceivers. 736a45c6cb8SMadhusudhan Chikkature */ 73770a3341aSDenis Karpov static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) 738a45c6cb8SMadhusudhan Chikkature { 739a45c6cb8SMadhusudhan Chikkature u32 reg_val = 0; 740a45c6cb8SMadhusudhan Chikkature int ret; 741a45c6cb8SMadhusudhan Chikkature 742a45c6cb8SMadhusudhan Chikkature /* Disable the clocks */ 743a45c6cb8SMadhusudhan Chikkature clk_disable(host->fclk); 744a45c6cb8SMadhusudhan Chikkature clk_disable(host->iclk); 745a45c6cb8SMadhusudhan Chikkature clk_disable(host->dbclk); 746a45c6cb8SMadhusudhan Chikkature 747a45c6cb8SMadhusudhan Chikkature /* Turn the power off */ 748a45c6cb8SMadhusudhan Chikkature ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); 749a45c6cb8SMadhusudhan Chikkature if (ret != 0) 750a45c6cb8SMadhusudhan Chikkature goto err; 751a45c6cb8SMadhusudhan Chikkature 752a45c6cb8SMadhusudhan Chikkature /* Turn the power ON with given VDD 1.8 or 3.0v */ 753a45c6cb8SMadhusudhan Chikkature ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); 754a45c6cb8SMadhusudhan Chikkature if (ret != 0) 755a45c6cb8SMadhusudhan Chikkature goto err; 756a45c6cb8SMadhusudhan Chikkature 757a45c6cb8SMadhusudhan Chikkature clk_enable(host->fclk); 758a45c6cb8SMadhusudhan Chikkature clk_enable(host->iclk); 759a45c6cb8SMadhusudhan Chikkature clk_enable(host->dbclk); 760a45c6cb8SMadhusudhan Chikkature 761a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, HCTL, 762a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); 763a45c6cb8SMadhusudhan Chikkature reg_val = OMAP_HSMMC_READ(host->base, HCTL); 764eb250826SDavid Brownell 765a45c6cb8SMadhusudhan Chikkature /* 766a45c6cb8SMadhusudhan Chikkature * If a MMC dual voltage card is detected, the set_ios fn calls 767a45c6cb8SMadhusudhan Chikkature * this fn with VDD bit set for 1.8V. Upon card removal from the 76870a3341aSDenis Karpov * slot, omap_hsmmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. 769a45c6cb8SMadhusudhan Chikkature * 770eb250826SDavid Brownell * Cope with a bit of slop in the range ... per data sheets: 771eb250826SDavid Brownell * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max, 772eb250826SDavid Brownell * but recommended values are 1.71V to 1.89V 773eb250826SDavid Brownell * - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max, 774eb250826SDavid Brownell * but recommended values are 2.7V to 3.3V 775eb250826SDavid Brownell * 776eb250826SDavid Brownell * Board setup code shouldn't permit anything very out-of-range. 777eb250826SDavid Brownell * TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the 778eb250826SDavid Brownell * middle range) but VSIM can't power DAT4..DAT7 at more than 3V. 779a45c6cb8SMadhusudhan Chikkature */ 780eb250826SDavid Brownell if ((1 << vdd) <= MMC_VDD_23_24) 781a45c6cb8SMadhusudhan Chikkature reg_val |= SDVS18; 782eb250826SDavid Brownell else 783eb250826SDavid Brownell reg_val |= SDVS30; 784a45c6cb8SMadhusudhan Chikkature 785a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); 786e13bb300SAdrian Hunter set_sd_bus_power(host); 787a45c6cb8SMadhusudhan Chikkature 788a45c6cb8SMadhusudhan Chikkature return 0; 789a45c6cb8SMadhusudhan Chikkature err: 790a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); 791a45c6cb8SMadhusudhan Chikkature return ret; 792a45c6cb8SMadhusudhan Chikkature } 793a45c6cb8SMadhusudhan Chikkature 794b62f6228SAdrian Hunter /* Protect the card while the cover is open */ 795b62f6228SAdrian Hunter static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) 796b62f6228SAdrian Hunter { 797b62f6228SAdrian Hunter if (!mmc_slot(host).get_cover_state) 798b62f6228SAdrian Hunter return; 799b62f6228SAdrian Hunter 800b62f6228SAdrian Hunter host->reqs_blocked = 0; 801b62f6228SAdrian Hunter if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { 802b62f6228SAdrian Hunter if (host->protect_card) { 803b62f6228SAdrian Hunter printk(KERN_INFO "%s: cover is closed, " 804b62f6228SAdrian Hunter "card is now accessible\n", 805b62f6228SAdrian Hunter mmc_hostname(host->mmc)); 806b62f6228SAdrian Hunter host->protect_card = 0; 807b62f6228SAdrian Hunter } 808b62f6228SAdrian Hunter } else { 809b62f6228SAdrian Hunter if (!host->protect_card) { 810b62f6228SAdrian Hunter printk(KERN_INFO "%s: cover is open, " 811b62f6228SAdrian Hunter "card is now inaccessible\n", 812b62f6228SAdrian Hunter mmc_hostname(host->mmc)); 813b62f6228SAdrian Hunter host->protect_card = 1; 814b62f6228SAdrian Hunter } 815b62f6228SAdrian Hunter } 816b62f6228SAdrian Hunter } 817b62f6228SAdrian Hunter 818a45c6cb8SMadhusudhan Chikkature /* 819a45c6cb8SMadhusudhan Chikkature * Work Item to notify the core about card insertion/removal 820a45c6cb8SMadhusudhan Chikkature */ 82170a3341aSDenis Karpov static void omap_hsmmc_detect(struct work_struct *work) 822a45c6cb8SMadhusudhan Chikkature { 82370a3341aSDenis Karpov struct omap_hsmmc_host *host = 82470a3341aSDenis Karpov container_of(work, struct omap_hsmmc_host, mmc_carddetect_work); 825249d0fa9SDavid Brownell struct omap_mmc_slot_data *slot = &mmc_slot(host); 826a6b2240dSAdrian Hunter int carddetect; 827249d0fa9SDavid Brownell 828a6b2240dSAdrian Hunter if (host->suspended) 829a6b2240dSAdrian Hunter return; 830a45c6cb8SMadhusudhan Chikkature 831a45c6cb8SMadhusudhan Chikkature sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); 832a6b2240dSAdrian Hunter 833191d1f1dSDenis Karpov if (slot->card_detect) 834a6b2240dSAdrian Hunter carddetect = slot->card_detect(slot->card_detect_irq); 835b62f6228SAdrian Hunter else { 836b62f6228SAdrian Hunter omap_hsmmc_protect_card(host); 837a6b2240dSAdrian Hunter carddetect = -ENOSYS; 838b62f6228SAdrian Hunter } 839a6b2240dSAdrian Hunter 840a6b2240dSAdrian Hunter if (carddetect) { 841a45c6cb8SMadhusudhan Chikkature mmc_detect_change(host->mmc, (HZ * 200) / 1000); 842a45c6cb8SMadhusudhan Chikkature } else { 8435e2ea617SAdrian Hunter mmc_host_enable(host->mmc); 84470a3341aSDenis Karpov omap_hsmmc_reset_controller_fsm(host, SRD); 8455e2ea617SAdrian Hunter mmc_host_lazy_disable(host->mmc); 84670a3341aSDenis Karpov 847a45c6cb8SMadhusudhan Chikkature mmc_detect_change(host->mmc, (HZ * 50) / 1000); 848a45c6cb8SMadhusudhan Chikkature } 849a45c6cb8SMadhusudhan Chikkature } 850a45c6cb8SMadhusudhan Chikkature 851a45c6cb8SMadhusudhan Chikkature /* 852a45c6cb8SMadhusudhan Chikkature * ISR for handling card insertion and removal 853a45c6cb8SMadhusudhan Chikkature */ 85470a3341aSDenis Karpov static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id) 855a45c6cb8SMadhusudhan Chikkature { 85670a3341aSDenis Karpov struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id; 857a45c6cb8SMadhusudhan Chikkature 858a6b2240dSAdrian Hunter if (host->suspended) 859a6b2240dSAdrian Hunter return IRQ_HANDLED; 860a45c6cb8SMadhusudhan Chikkature schedule_work(&host->mmc_carddetect_work); 861a45c6cb8SMadhusudhan Chikkature 862a45c6cb8SMadhusudhan Chikkature return IRQ_HANDLED; 863a45c6cb8SMadhusudhan Chikkature } 864a45c6cb8SMadhusudhan Chikkature 86570a3341aSDenis Karpov static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host, 8660ccd76d4SJuha Yrjola struct mmc_data *data) 8670ccd76d4SJuha Yrjola { 8680ccd76d4SJuha Yrjola int sync_dev; 8690ccd76d4SJuha Yrjola 870f3e2f1ddSGrazvydas Ignotas if (data->flags & MMC_DATA_WRITE) 871f3e2f1ddSGrazvydas Ignotas sync_dev = host->dma_line_tx; 8720ccd76d4SJuha Yrjola else 873f3e2f1ddSGrazvydas Ignotas sync_dev = host->dma_line_rx; 8740ccd76d4SJuha Yrjola return sync_dev; 8750ccd76d4SJuha Yrjola } 8760ccd76d4SJuha Yrjola 87770a3341aSDenis Karpov static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, 8780ccd76d4SJuha Yrjola struct mmc_data *data, 8790ccd76d4SJuha Yrjola struct scatterlist *sgl) 8800ccd76d4SJuha Yrjola { 8810ccd76d4SJuha Yrjola int blksz, nblk, dma_ch; 8820ccd76d4SJuha Yrjola 8830ccd76d4SJuha Yrjola dma_ch = host->dma_ch; 8840ccd76d4SJuha Yrjola if (data->flags & MMC_DATA_WRITE) { 8850ccd76d4SJuha Yrjola omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, 8860ccd76d4SJuha Yrjola (host->mapbase + OMAP_HSMMC_DATA), 0, 0); 8870ccd76d4SJuha Yrjola omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, 8880ccd76d4SJuha Yrjola sg_dma_address(sgl), 0, 0); 8890ccd76d4SJuha Yrjola } else { 8900ccd76d4SJuha Yrjola omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, 8910ccd76d4SJuha Yrjola (host->mapbase + OMAP_HSMMC_DATA), 0, 0); 8920ccd76d4SJuha Yrjola omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, 8930ccd76d4SJuha Yrjola sg_dma_address(sgl), 0, 0); 8940ccd76d4SJuha Yrjola } 8950ccd76d4SJuha Yrjola 8960ccd76d4SJuha Yrjola blksz = host->data->blksz; 8970ccd76d4SJuha Yrjola nblk = sg_dma_len(sgl) / blksz; 8980ccd76d4SJuha Yrjola 8990ccd76d4SJuha Yrjola omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, 9000ccd76d4SJuha Yrjola blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, 90170a3341aSDenis Karpov omap_hsmmc_get_dma_sync_dev(host, data), 9020ccd76d4SJuha Yrjola !(data->flags & MMC_DATA_WRITE)); 9030ccd76d4SJuha Yrjola 9040ccd76d4SJuha Yrjola omap_start_dma(dma_ch); 9050ccd76d4SJuha Yrjola } 9060ccd76d4SJuha Yrjola 907a45c6cb8SMadhusudhan Chikkature /* 908a45c6cb8SMadhusudhan Chikkature * DMA call back function 909a45c6cb8SMadhusudhan Chikkature */ 91070a3341aSDenis Karpov static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data) 911a45c6cb8SMadhusudhan Chikkature { 91270a3341aSDenis Karpov struct omap_hsmmc_host *host = data; 913a45c6cb8SMadhusudhan Chikkature 914a45c6cb8SMadhusudhan Chikkature if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ) 915a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n"); 916a45c6cb8SMadhusudhan Chikkature 917a45c6cb8SMadhusudhan Chikkature if (host->dma_ch < 0) 918a45c6cb8SMadhusudhan Chikkature return; 919a45c6cb8SMadhusudhan Chikkature 9200ccd76d4SJuha Yrjola host->dma_sg_idx++; 9210ccd76d4SJuha Yrjola if (host->dma_sg_idx < host->dma_len) { 9220ccd76d4SJuha Yrjola /* Fire up the next transfer. */ 92370a3341aSDenis Karpov omap_hsmmc_config_dma_params(host, host->data, 9240ccd76d4SJuha Yrjola host->data->sg + host->dma_sg_idx); 9250ccd76d4SJuha Yrjola return; 9260ccd76d4SJuha Yrjola } 9270ccd76d4SJuha Yrjola 928a45c6cb8SMadhusudhan Chikkature omap_free_dma(host->dma_ch); 929a45c6cb8SMadhusudhan Chikkature host->dma_ch = -1; 930a45c6cb8SMadhusudhan Chikkature /* 931a45c6cb8SMadhusudhan Chikkature * DMA Callback: run in interrupt context. 93285b84322SAnand Gadiyar * mutex_unlock will throw a kernel warning if used. 933a45c6cb8SMadhusudhan Chikkature */ 934a45c6cb8SMadhusudhan Chikkature up(&host->sem); 935a45c6cb8SMadhusudhan Chikkature } 936a45c6cb8SMadhusudhan Chikkature 937a45c6cb8SMadhusudhan Chikkature /* 938a45c6cb8SMadhusudhan Chikkature * Routine to configure and start DMA for the MMC card 939a45c6cb8SMadhusudhan Chikkature */ 94070a3341aSDenis Karpov static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, 94170a3341aSDenis Karpov struct mmc_request *req) 942a45c6cb8SMadhusudhan Chikkature { 9430ccd76d4SJuha Yrjola int dma_ch = 0, ret = 0, err = 1, i; 944a45c6cb8SMadhusudhan Chikkature struct mmc_data *data = req->data; 945a45c6cb8SMadhusudhan Chikkature 9460ccd76d4SJuha Yrjola /* Sanity check: all the SG entries must be aligned by block size. */ 947a3f406f8SJarkko Lavinen for (i = 0; i < data->sg_len; i++) { 9480ccd76d4SJuha Yrjola struct scatterlist *sgl; 9490ccd76d4SJuha Yrjola 9500ccd76d4SJuha Yrjola sgl = data->sg + i; 9510ccd76d4SJuha Yrjola if (sgl->length % data->blksz) 9520ccd76d4SJuha Yrjola return -EINVAL; 9530ccd76d4SJuha Yrjola } 9540ccd76d4SJuha Yrjola if ((data->blksz % 4) != 0) 9550ccd76d4SJuha Yrjola /* REVISIT: The MMC buffer increments only when MSB is written. 9560ccd76d4SJuha Yrjola * Return error for blksz which is non multiple of four. 9570ccd76d4SJuha Yrjola */ 9580ccd76d4SJuha Yrjola return -EINVAL; 9590ccd76d4SJuha Yrjola 960a45c6cb8SMadhusudhan Chikkature /* 961a45c6cb8SMadhusudhan Chikkature * If for some reason the DMA transfer is still active, 962a45c6cb8SMadhusudhan Chikkature * we wait for timeout period and free the dma 963a45c6cb8SMadhusudhan Chikkature */ 964a45c6cb8SMadhusudhan Chikkature if (host->dma_ch != -1) { 965a45c6cb8SMadhusudhan Chikkature set_current_state(TASK_UNINTERRUPTIBLE); 966a45c6cb8SMadhusudhan Chikkature schedule_timeout(100); 967a45c6cb8SMadhusudhan Chikkature if (down_trylock(&host->sem)) { 968a45c6cb8SMadhusudhan Chikkature omap_free_dma(host->dma_ch); 969a45c6cb8SMadhusudhan Chikkature host->dma_ch = -1; 970a45c6cb8SMadhusudhan Chikkature up(&host->sem); 971a45c6cb8SMadhusudhan Chikkature return err; 972a45c6cb8SMadhusudhan Chikkature } 973a45c6cb8SMadhusudhan Chikkature } else { 974a45c6cb8SMadhusudhan Chikkature if (down_trylock(&host->sem)) 975a45c6cb8SMadhusudhan Chikkature return err; 976a45c6cb8SMadhusudhan Chikkature } 977a45c6cb8SMadhusudhan Chikkature 97870a3341aSDenis Karpov ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), 97970a3341aSDenis Karpov "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); 980a45c6cb8SMadhusudhan Chikkature if (ret != 0) { 9810ccd76d4SJuha Yrjola dev_err(mmc_dev(host->mmc), 982a45c6cb8SMadhusudhan Chikkature "%s: omap_request_dma() failed with %d\n", 983a45c6cb8SMadhusudhan Chikkature mmc_hostname(host->mmc), ret); 984a45c6cb8SMadhusudhan Chikkature return ret; 985a45c6cb8SMadhusudhan Chikkature } 986a45c6cb8SMadhusudhan Chikkature 987a45c6cb8SMadhusudhan Chikkature host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, 98870a3341aSDenis Karpov data->sg_len, omap_hsmmc_get_dma_dir(host, data)); 989a45c6cb8SMadhusudhan Chikkature host->dma_ch = dma_ch; 9900ccd76d4SJuha Yrjola host->dma_sg_idx = 0; 991a45c6cb8SMadhusudhan Chikkature 99270a3341aSDenis Karpov omap_hsmmc_config_dma_params(host, data, data->sg); 993a45c6cb8SMadhusudhan Chikkature 994a45c6cb8SMadhusudhan Chikkature return 0; 995a45c6cb8SMadhusudhan Chikkature } 996a45c6cb8SMadhusudhan Chikkature 99770a3341aSDenis Karpov static void set_data_timeout(struct omap_hsmmc_host *host, 998a45c6cb8SMadhusudhan Chikkature struct mmc_request *req) 999a45c6cb8SMadhusudhan Chikkature { 1000a45c6cb8SMadhusudhan Chikkature unsigned int timeout, cycle_ns; 1001a45c6cb8SMadhusudhan Chikkature uint32_t reg, clkd, dto = 0; 1002a45c6cb8SMadhusudhan Chikkature 1003a45c6cb8SMadhusudhan Chikkature reg = OMAP_HSMMC_READ(host->base, SYSCTL); 1004a45c6cb8SMadhusudhan Chikkature clkd = (reg & CLKD_MASK) >> CLKD_SHIFT; 1005a45c6cb8SMadhusudhan Chikkature if (clkd == 0) 1006a45c6cb8SMadhusudhan Chikkature clkd = 1; 1007a45c6cb8SMadhusudhan Chikkature 1008a45c6cb8SMadhusudhan Chikkature cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); 1009a45c6cb8SMadhusudhan Chikkature timeout = req->data->timeout_ns / cycle_ns; 1010a45c6cb8SMadhusudhan Chikkature timeout += req->data->timeout_clks; 1011a45c6cb8SMadhusudhan Chikkature if (timeout) { 1012a45c6cb8SMadhusudhan Chikkature while ((timeout & 0x80000000) == 0) { 1013a45c6cb8SMadhusudhan Chikkature dto += 1; 1014a45c6cb8SMadhusudhan Chikkature timeout <<= 1; 1015a45c6cb8SMadhusudhan Chikkature } 1016a45c6cb8SMadhusudhan Chikkature dto = 31 - dto; 1017a45c6cb8SMadhusudhan Chikkature timeout <<= 1; 1018a45c6cb8SMadhusudhan Chikkature if (timeout && dto) 1019a45c6cb8SMadhusudhan Chikkature dto += 1; 1020a45c6cb8SMadhusudhan Chikkature if (dto >= 13) 1021a45c6cb8SMadhusudhan Chikkature dto -= 13; 1022a45c6cb8SMadhusudhan Chikkature else 1023a45c6cb8SMadhusudhan Chikkature dto = 0; 1024a45c6cb8SMadhusudhan Chikkature if (dto > 14) 1025a45c6cb8SMadhusudhan Chikkature dto = 14; 1026a45c6cb8SMadhusudhan Chikkature } 1027a45c6cb8SMadhusudhan Chikkature 1028a45c6cb8SMadhusudhan Chikkature reg &= ~DTO_MASK; 1029a45c6cb8SMadhusudhan Chikkature reg |= dto << DTO_SHIFT; 1030a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, SYSCTL, reg); 1031a45c6cb8SMadhusudhan Chikkature } 1032a45c6cb8SMadhusudhan Chikkature 1033a45c6cb8SMadhusudhan Chikkature /* 1034a45c6cb8SMadhusudhan Chikkature * Configure block length for MMC/SD cards and initiate the transfer. 1035a45c6cb8SMadhusudhan Chikkature */ 1036a45c6cb8SMadhusudhan Chikkature static int 103770a3341aSDenis Karpov omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) 1038a45c6cb8SMadhusudhan Chikkature { 1039a45c6cb8SMadhusudhan Chikkature int ret; 1040a45c6cb8SMadhusudhan Chikkature host->data = req->data; 1041a45c6cb8SMadhusudhan Chikkature 1042a45c6cb8SMadhusudhan Chikkature if (req->data == NULL) { 1043a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, BLK, 0); 1044a45c6cb8SMadhusudhan Chikkature return 0; 1045a45c6cb8SMadhusudhan Chikkature } 1046a45c6cb8SMadhusudhan Chikkature 1047a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) 1048a45c6cb8SMadhusudhan Chikkature | (req->data->blocks << 16)); 1049a45c6cb8SMadhusudhan Chikkature set_data_timeout(host, req); 1050a45c6cb8SMadhusudhan Chikkature 1051a45c6cb8SMadhusudhan Chikkature if (host->use_dma) { 105270a3341aSDenis Karpov ret = omap_hsmmc_start_dma_transfer(host, req); 1053a45c6cb8SMadhusudhan Chikkature if (ret != 0) { 1054a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n"); 1055a45c6cb8SMadhusudhan Chikkature return ret; 1056a45c6cb8SMadhusudhan Chikkature } 1057a45c6cb8SMadhusudhan Chikkature } 1058a45c6cb8SMadhusudhan Chikkature return 0; 1059a45c6cb8SMadhusudhan Chikkature } 1060a45c6cb8SMadhusudhan Chikkature 1061a45c6cb8SMadhusudhan Chikkature /* 1062a45c6cb8SMadhusudhan Chikkature * Request function. for read/write operation 1063a45c6cb8SMadhusudhan Chikkature */ 106470a3341aSDenis Karpov static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) 1065a45c6cb8SMadhusudhan Chikkature { 106670a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1067a3f406f8SJarkko Lavinen int err; 1068a45c6cb8SMadhusudhan Chikkature 10694dffd7a2SAdrian Hunter /* 10704dffd7a2SAdrian Hunter * Prevent races with the interrupt handler because of unexpected 10714dffd7a2SAdrian Hunter * interrupts, but not if we are already in interrupt context i.e. 10724dffd7a2SAdrian Hunter * retries. 10734dffd7a2SAdrian Hunter */ 1074b62f6228SAdrian Hunter if (!in_interrupt()) { 10754dffd7a2SAdrian Hunter spin_lock_irqsave(&host->irq_lock, host->flags); 1076b62f6228SAdrian Hunter /* 1077b62f6228SAdrian Hunter * Protect the card from I/O if there is a possibility 1078b62f6228SAdrian Hunter * it can be removed. 1079b62f6228SAdrian Hunter */ 1080b62f6228SAdrian Hunter if (host->protect_card) { 1081b62f6228SAdrian Hunter if (host->reqs_blocked < 3) { 1082b62f6228SAdrian Hunter /* 1083b62f6228SAdrian Hunter * Ensure the controller is left in a consistent 1084b62f6228SAdrian Hunter * state by resetting the command and data state 1085b62f6228SAdrian Hunter * machines. 1086b62f6228SAdrian Hunter */ 1087b62f6228SAdrian Hunter omap_hsmmc_reset_controller_fsm(host, SRD); 1088b62f6228SAdrian Hunter omap_hsmmc_reset_controller_fsm(host, SRC); 1089b62f6228SAdrian Hunter host->reqs_blocked += 1; 1090b62f6228SAdrian Hunter } 1091b62f6228SAdrian Hunter req->cmd->error = -EBADF; 1092b62f6228SAdrian Hunter if (req->data) 1093b62f6228SAdrian Hunter req->data->error = -EBADF; 1094b62f6228SAdrian Hunter spin_unlock_irqrestore(&host->irq_lock, host->flags); 1095b62f6228SAdrian Hunter mmc_request_done(mmc, req); 1096b62f6228SAdrian Hunter return; 1097b62f6228SAdrian Hunter } else if (host->reqs_blocked) 1098b62f6228SAdrian Hunter host->reqs_blocked = 0; 1099b62f6228SAdrian Hunter } 1100a45c6cb8SMadhusudhan Chikkature WARN_ON(host->mrq != NULL); 1101a45c6cb8SMadhusudhan Chikkature host->mrq = req; 110270a3341aSDenis Karpov err = omap_hsmmc_prepare_data(host, req); 1103a3f406f8SJarkko Lavinen if (err) { 1104a3f406f8SJarkko Lavinen req->cmd->error = err; 1105a3f406f8SJarkko Lavinen if (req->data) 1106a3f406f8SJarkko Lavinen req->data->error = err; 1107a3f406f8SJarkko Lavinen host->mrq = NULL; 11084dffd7a2SAdrian Hunter if (!in_interrupt()) 11094dffd7a2SAdrian Hunter spin_unlock_irqrestore(&host->irq_lock, host->flags); 1110a3f406f8SJarkko Lavinen mmc_request_done(mmc, req); 1111a3f406f8SJarkko Lavinen return; 1112a3f406f8SJarkko Lavinen } 1113a3f406f8SJarkko Lavinen 111470a3341aSDenis Karpov omap_hsmmc_start_command(host, req->cmd, req->data); 1115a45c6cb8SMadhusudhan Chikkature } 1116a45c6cb8SMadhusudhan Chikkature 1117a45c6cb8SMadhusudhan Chikkature /* Routine to configure clock values. Exposed API to core */ 111870a3341aSDenis Karpov static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 1119a45c6cb8SMadhusudhan Chikkature { 112070a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1121a45c6cb8SMadhusudhan Chikkature u16 dsor = 0; 1122a45c6cb8SMadhusudhan Chikkature unsigned long regval; 1123a45c6cb8SMadhusudhan Chikkature unsigned long timeout; 112473153010SJarkko Lavinen u32 con; 1125a3621465SAdrian Hunter int do_send_init_stream = 0; 1126a45c6cb8SMadhusudhan Chikkature 11275e2ea617SAdrian Hunter mmc_host_enable(host->mmc); 11285e2ea617SAdrian Hunter 1129a3621465SAdrian Hunter if (ios->power_mode != host->power_mode) { 1130a45c6cb8SMadhusudhan Chikkature switch (ios->power_mode) { 1131a45c6cb8SMadhusudhan Chikkature case MMC_POWER_OFF: 1132a3621465SAdrian Hunter mmc_slot(host).set_power(host->dev, host->slot_id, 1133a3621465SAdrian Hunter 0, 0); 1134623821f7SAdrian Hunter host->vdd = 0; 1135a45c6cb8SMadhusudhan Chikkature break; 1136a45c6cb8SMadhusudhan Chikkature case MMC_POWER_UP: 1137a3621465SAdrian Hunter mmc_slot(host).set_power(host->dev, host->slot_id, 1138a3621465SAdrian Hunter 1, ios->vdd); 1139623821f7SAdrian Hunter host->vdd = ios->vdd; 1140a45c6cb8SMadhusudhan Chikkature break; 1141a3621465SAdrian Hunter case MMC_POWER_ON: 1142a3621465SAdrian Hunter do_send_init_stream = 1; 1143a3621465SAdrian Hunter break; 1144a3621465SAdrian Hunter } 1145a3621465SAdrian Hunter host->power_mode = ios->power_mode; 1146a45c6cb8SMadhusudhan Chikkature } 1147a45c6cb8SMadhusudhan Chikkature 1148dd498effSDenis Karpov /* FIXME: set registers based only on changes to ios */ 1149dd498effSDenis Karpov 115073153010SJarkko Lavinen con = OMAP_HSMMC_READ(host->base, CON); 1151a45c6cb8SMadhusudhan Chikkature switch (mmc->ios.bus_width) { 115273153010SJarkko Lavinen case MMC_BUS_WIDTH_8: 115373153010SJarkko Lavinen OMAP_HSMMC_WRITE(host->base, CON, con | DW8); 115473153010SJarkko Lavinen break; 1155a45c6cb8SMadhusudhan Chikkature case MMC_BUS_WIDTH_4: 115673153010SJarkko Lavinen OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); 1157a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, HCTL, 1158a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); 1159a45c6cb8SMadhusudhan Chikkature break; 1160a45c6cb8SMadhusudhan Chikkature case MMC_BUS_WIDTH_1: 116173153010SJarkko Lavinen OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); 1162a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, HCTL, 1163a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); 1164a45c6cb8SMadhusudhan Chikkature break; 1165a45c6cb8SMadhusudhan Chikkature } 1166a45c6cb8SMadhusudhan Chikkature 1167a45c6cb8SMadhusudhan Chikkature if (host->id == OMAP_MMC1_DEVID) { 1168eb250826SDavid Brownell /* Only MMC1 can interface at 3V without some flavor 1169eb250826SDavid Brownell * of external transceiver; but they all handle 1.8V. 1170eb250826SDavid Brownell */ 1171a45c6cb8SMadhusudhan Chikkature if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && 1172a45c6cb8SMadhusudhan Chikkature (ios->vdd == DUAL_VOLT_OCR_BIT)) { 1173a45c6cb8SMadhusudhan Chikkature /* 1174a45c6cb8SMadhusudhan Chikkature * The mmc_select_voltage fn of the core does 1175a45c6cb8SMadhusudhan Chikkature * not seem to set the power_mode to 1176a45c6cb8SMadhusudhan Chikkature * MMC_POWER_UP upon recalculating the voltage. 1177a45c6cb8SMadhusudhan Chikkature * vdd 1.8v. 1178a45c6cb8SMadhusudhan Chikkature */ 117970a3341aSDenis Karpov if (omap_hsmmc_switch_opcond(host, ios->vdd) != 0) 1180a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 1181a45c6cb8SMadhusudhan Chikkature "Switch operation failed\n"); 1182a45c6cb8SMadhusudhan Chikkature } 1183a45c6cb8SMadhusudhan Chikkature } 1184a45c6cb8SMadhusudhan Chikkature 1185a45c6cb8SMadhusudhan Chikkature if (ios->clock) { 1186a45c6cb8SMadhusudhan Chikkature dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; 1187a45c6cb8SMadhusudhan Chikkature if (dsor < 1) 1188a45c6cb8SMadhusudhan Chikkature dsor = 1; 1189a45c6cb8SMadhusudhan Chikkature 1190a45c6cb8SMadhusudhan Chikkature if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) 1191a45c6cb8SMadhusudhan Chikkature dsor++; 1192a45c6cb8SMadhusudhan Chikkature 1193a45c6cb8SMadhusudhan Chikkature if (dsor > 250) 1194a45c6cb8SMadhusudhan Chikkature dsor = 250; 1195a45c6cb8SMadhusudhan Chikkature } 119670a3341aSDenis Karpov omap_hsmmc_stop_clock(host); 1197a45c6cb8SMadhusudhan Chikkature regval = OMAP_HSMMC_READ(host->base, SYSCTL); 1198a45c6cb8SMadhusudhan Chikkature regval = regval & ~(CLKD_MASK); 1199a45c6cb8SMadhusudhan Chikkature regval = regval | (dsor << 6) | (DTO << 16); 1200a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); 1201a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, SYSCTL, 1202a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); 1203a45c6cb8SMadhusudhan Chikkature 1204a45c6cb8SMadhusudhan Chikkature /* Wait till the ICS bit is set */ 1205a45c6cb8SMadhusudhan Chikkature timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); 120611dd62a7SDenis Karpov while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS 1207a45c6cb8SMadhusudhan Chikkature && time_before(jiffies, timeout)) 1208a45c6cb8SMadhusudhan Chikkature msleep(1); 1209a45c6cb8SMadhusudhan Chikkature 1210a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, SYSCTL, 1211a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); 1212a45c6cb8SMadhusudhan Chikkature 1213a3621465SAdrian Hunter if (do_send_init_stream) 1214a45c6cb8SMadhusudhan Chikkature send_init_stream(host); 1215a45c6cb8SMadhusudhan Chikkature 1216abb28e73SDenis Karpov con = OMAP_HSMMC_READ(host->base, CON); 1217a45c6cb8SMadhusudhan Chikkature if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) 1218abb28e73SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con | OD); 1219abb28e73SDenis Karpov else 1220abb28e73SDenis Karpov OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); 12215e2ea617SAdrian Hunter 1222dd498effSDenis Karpov if (host->power_mode == MMC_POWER_OFF) 1223dd498effSDenis Karpov mmc_host_disable(host->mmc); 1224dd498effSDenis Karpov else 12255e2ea617SAdrian Hunter mmc_host_lazy_disable(host->mmc); 1226a45c6cb8SMadhusudhan Chikkature } 1227a45c6cb8SMadhusudhan Chikkature 1228a45c6cb8SMadhusudhan Chikkature static int omap_hsmmc_get_cd(struct mmc_host *mmc) 1229a45c6cb8SMadhusudhan Chikkature { 123070a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1231a45c6cb8SMadhusudhan Chikkature 1232191d1f1dSDenis Karpov if (!mmc_slot(host).card_detect) 1233a45c6cb8SMadhusudhan Chikkature return -ENOSYS; 1234191d1f1dSDenis Karpov return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq); 1235a45c6cb8SMadhusudhan Chikkature } 1236a45c6cb8SMadhusudhan Chikkature 1237a45c6cb8SMadhusudhan Chikkature static int omap_hsmmc_get_ro(struct mmc_host *mmc) 1238a45c6cb8SMadhusudhan Chikkature { 123970a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1240a45c6cb8SMadhusudhan Chikkature 1241191d1f1dSDenis Karpov if (!mmc_slot(host).get_ro) 1242a45c6cb8SMadhusudhan Chikkature return -ENOSYS; 1243191d1f1dSDenis Karpov return mmc_slot(host).get_ro(host->dev, 0); 1244a45c6cb8SMadhusudhan Chikkature } 1245a45c6cb8SMadhusudhan Chikkature 124670a3341aSDenis Karpov static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) 12471b331e69SKim Kyuwon { 12481b331e69SKim Kyuwon u32 hctl, capa, value; 12491b331e69SKim Kyuwon 12501b331e69SKim Kyuwon /* Only MMC1 supports 3.0V */ 12511b331e69SKim Kyuwon if (host->id == OMAP_MMC1_DEVID) { 12521b331e69SKim Kyuwon hctl = SDVS30; 12531b331e69SKim Kyuwon capa = VS30 | VS18; 12541b331e69SKim Kyuwon } else { 12551b331e69SKim Kyuwon hctl = SDVS18; 12561b331e69SKim Kyuwon capa = VS18; 12571b331e69SKim Kyuwon } 12581b331e69SKim Kyuwon 12591b331e69SKim Kyuwon value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK; 12601b331e69SKim Kyuwon OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl); 12611b331e69SKim Kyuwon 12621b331e69SKim Kyuwon value = OMAP_HSMMC_READ(host->base, CAPA); 12631b331e69SKim Kyuwon OMAP_HSMMC_WRITE(host->base, CAPA, value | capa); 12641b331e69SKim Kyuwon 12651b331e69SKim Kyuwon /* Set the controller to AUTO IDLE mode */ 12661b331e69SKim Kyuwon value = OMAP_HSMMC_READ(host->base, SYSCONFIG); 12671b331e69SKim Kyuwon OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE); 12681b331e69SKim Kyuwon 12691b331e69SKim Kyuwon /* Set SD bus power bit */ 1270e13bb300SAdrian Hunter set_sd_bus_power(host); 12711b331e69SKim Kyuwon } 12721b331e69SKim Kyuwon 1273dd498effSDenis Karpov /* 1274dd498effSDenis Karpov * Dynamic power saving handling, FSM: 127513189e78SJarkko Lavinen * ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF 127613189e78SJarkko Lavinen * ^___________| | | 127713189e78SJarkko Lavinen * |______________________|______________________| 1278dd498effSDenis Karpov * 1279dd498effSDenis Karpov * ENABLED: mmc host is fully functional 1280dd498effSDenis Karpov * DISABLED: fclk is off 128113189e78SJarkko Lavinen * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep 1282623821f7SAdrian Hunter * REGSLEEP: fclk is off, voltage regulator is asleep 128313189e78SJarkko Lavinen * OFF: fclk is off, voltage regulator is off 1284dd498effSDenis Karpov * 1285dd498effSDenis Karpov * Transition handlers return the timeout for the next state transition 1286dd498effSDenis Karpov * or negative error. 1287dd498effSDenis Karpov */ 1288dd498effSDenis Karpov 128913189e78SJarkko Lavinen enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF}; 1290dd498effSDenis Karpov 1291dd498effSDenis Karpov /* Handler for [ENABLED -> DISABLED] transition */ 129270a3341aSDenis Karpov static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host) 1293dd498effSDenis Karpov { 129470a3341aSDenis Karpov omap_hsmmc_context_save(host); 1295dd498effSDenis Karpov clk_disable(host->fclk); 1296dd498effSDenis Karpov host->dpm_state = DISABLED; 1297dd498effSDenis Karpov 1298dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n"); 1299dd498effSDenis Karpov 1300dd498effSDenis Karpov if (host->power_mode == MMC_POWER_OFF) 1301dd498effSDenis Karpov return 0; 1302dd498effSDenis Karpov 130313189e78SJarkko Lavinen return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT); 1304dd498effSDenis Karpov } 1305dd498effSDenis Karpov 130613189e78SJarkko Lavinen /* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */ 130770a3341aSDenis Karpov static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host) 1308dd498effSDenis Karpov { 130913189e78SJarkko Lavinen int err, new_state; 1310dd498effSDenis Karpov 1311dd498effSDenis Karpov if (!mmc_try_claim_host(host->mmc)) 1312dd498effSDenis Karpov return 0; 1313dd498effSDenis Karpov 1314dd498effSDenis Karpov clk_enable(host->fclk); 131570a3341aSDenis Karpov omap_hsmmc_context_restore(host); 131613189e78SJarkko Lavinen if (mmc_card_can_sleep(host->mmc)) { 131713189e78SJarkko Lavinen err = mmc_card_sleep(host->mmc); 131813189e78SJarkko Lavinen if (err < 0) { 131913189e78SJarkko Lavinen clk_disable(host->fclk); 132013189e78SJarkko Lavinen mmc_release_host(host->mmc); 132113189e78SJarkko Lavinen return err; 132213189e78SJarkko Lavinen } 132313189e78SJarkko Lavinen new_state = CARDSLEEP; 132470a3341aSDenis Karpov } else { 132513189e78SJarkko Lavinen new_state = REGSLEEP; 132670a3341aSDenis Karpov } 132713189e78SJarkko Lavinen if (mmc_slot(host).set_sleep) 132813189e78SJarkko Lavinen mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0, 132913189e78SJarkko Lavinen new_state == CARDSLEEP); 133013189e78SJarkko Lavinen /* FIXME: turn off bus power and perhaps interrupts too */ 133113189e78SJarkko Lavinen clk_disable(host->fclk); 133213189e78SJarkko Lavinen host->dpm_state = new_state; 133313189e78SJarkko Lavinen 133413189e78SJarkko Lavinen mmc_release_host(host->mmc); 133513189e78SJarkko Lavinen 133613189e78SJarkko Lavinen dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n", 133713189e78SJarkko Lavinen host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); 1338dd498effSDenis Karpov 1339dd498effSDenis Karpov if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || 1340dd498effSDenis Karpov mmc_slot(host).card_detect || 1341dd498effSDenis Karpov (mmc_slot(host).get_cover_state && 134213189e78SJarkko Lavinen mmc_slot(host).get_cover_state(host->dev, host->slot_id))) 134313189e78SJarkko Lavinen return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT); 134413189e78SJarkko Lavinen 134513189e78SJarkko Lavinen return 0; 1346623821f7SAdrian Hunter } 1347dd498effSDenis Karpov 134813189e78SJarkko Lavinen /* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */ 134970a3341aSDenis Karpov static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host) 135013189e78SJarkko Lavinen { 135113189e78SJarkko Lavinen if (!mmc_try_claim_host(host->mmc)) 135213189e78SJarkko Lavinen return 0; 1353dd498effSDenis Karpov 135413189e78SJarkko Lavinen if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) || 135513189e78SJarkko Lavinen mmc_slot(host).card_detect || 135613189e78SJarkko Lavinen (mmc_slot(host).get_cover_state && 135713189e78SJarkko Lavinen mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) { 135813189e78SJarkko Lavinen mmc_release_host(host->mmc); 135913189e78SJarkko Lavinen return 0; 136013189e78SJarkko Lavinen } 1361dd498effSDenis Karpov 136213189e78SJarkko Lavinen mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); 136313189e78SJarkko Lavinen host->vdd = 0; 136413189e78SJarkko Lavinen host->power_mode = MMC_POWER_OFF; 136513189e78SJarkko Lavinen 136613189e78SJarkko Lavinen dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n", 136713189e78SJarkko Lavinen host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); 136813189e78SJarkko Lavinen 136913189e78SJarkko Lavinen host->dpm_state = OFF; 1370dd498effSDenis Karpov 1371dd498effSDenis Karpov mmc_release_host(host->mmc); 1372dd498effSDenis Karpov 1373dd498effSDenis Karpov return 0; 1374dd498effSDenis Karpov } 1375dd498effSDenis Karpov 1376dd498effSDenis Karpov /* Handler for [DISABLED -> ENABLED] transition */ 137770a3341aSDenis Karpov static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host) 1378dd498effSDenis Karpov { 1379dd498effSDenis Karpov int err; 1380dd498effSDenis Karpov 1381dd498effSDenis Karpov err = clk_enable(host->fclk); 1382dd498effSDenis Karpov if (err < 0) 1383dd498effSDenis Karpov return err; 1384dd498effSDenis Karpov 138570a3341aSDenis Karpov omap_hsmmc_context_restore(host); 1386dd498effSDenis Karpov host->dpm_state = ENABLED; 1387dd498effSDenis Karpov 1388dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n"); 1389dd498effSDenis Karpov 1390dd498effSDenis Karpov return 0; 1391dd498effSDenis Karpov } 1392dd498effSDenis Karpov 139313189e78SJarkko Lavinen /* Handler for [SLEEP -> ENABLED] transition */ 139470a3341aSDenis Karpov static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host) 139513189e78SJarkko Lavinen { 139613189e78SJarkko Lavinen if (!mmc_try_claim_host(host->mmc)) 139713189e78SJarkko Lavinen return 0; 139813189e78SJarkko Lavinen 139913189e78SJarkko Lavinen clk_enable(host->fclk); 140070a3341aSDenis Karpov omap_hsmmc_context_restore(host); 140113189e78SJarkko Lavinen if (mmc_slot(host).set_sleep) 140213189e78SJarkko Lavinen mmc_slot(host).set_sleep(host->dev, host->slot_id, 0, 140313189e78SJarkko Lavinen host->vdd, host->dpm_state == CARDSLEEP); 140413189e78SJarkko Lavinen if (mmc_card_can_sleep(host->mmc)) 140513189e78SJarkko Lavinen mmc_card_awake(host->mmc); 140613189e78SJarkko Lavinen 140713189e78SJarkko Lavinen dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n", 140813189e78SJarkko Lavinen host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); 140913189e78SJarkko Lavinen 141013189e78SJarkko Lavinen host->dpm_state = ENABLED; 141113189e78SJarkko Lavinen 141213189e78SJarkko Lavinen mmc_release_host(host->mmc); 141313189e78SJarkko Lavinen 141413189e78SJarkko Lavinen return 0; 141513189e78SJarkko Lavinen } 141613189e78SJarkko Lavinen 1417dd498effSDenis Karpov /* Handler for [OFF -> ENABLED] transition */ 141870a3341aSDenis Karpov static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host) 1419dd498effSDenis Karpov { 1420dd498effSDenis Karpov clk_enable(host->fclk); 1421dd498effSDenis Karpov 142270a3341aSDenis Karpov omap_hsmmc_context_restore(host); 142370a3341aSDenis Karpov omap_hsmmc_conf_bus_power(host); 1424dd498effSDenis Karpov mmc_power_restore_host(host->mmc); 1425dd498effSDenis Karpov 1426dd498effSDenis Karpov host->dpm_state = ENABLED; 1427dd498effSDenis Karpov 1428dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n"); 1429dd498effSDenis Karpov 1430dd498effSDenis Karpov return 0; 1431dd498effSDenis Karpov } 1432dd498effSDenis Karpov 1433dd498effSDenis Karpov /* 1434dd498effSDenis Karpov * Bring MMC host to ENABLED from any other PM state. 1435dd498effSDenis Karpov */ 143670a3341aSDenis Karpov static int omap_hsmmc_enable(struct mmc_host *mmc) 1437dd498effSDenis Karpov { 143870a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1439dd498effSDenis Karpov 1440dd498effSDenis Karpov switch (host->dpm_state) { 1441dd498effSDenis Karpov case DISABLED: 144270a3341aSDenis Karpov return omap_hsmmc_disabled_to_enabled(host); 144313189e78SJarkko Lavinen case CARDSLEEP: 1444623821f7SAdrian Hunter case REGSLEEP: 144570a3341aSDenis Karpov return omap_hsmmc_sleep_to_enabled(host); 1446dd498effSDenis Karpov case OFF: 144770a3341aSDenis Karpov return omap_hsmmc_off_to_enabled(host); 1448dd498effSDenis Karpov default: 1449dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); 1450dd498effSDenis Karpov return -EINVAL; 1451dd498effSDenis Karpov } 1452dd498effSDenis Karpov } 1453dd498effSDenis Karpov 1454dd498effSDenis Karpov /* 1455dd498effSDenis Karpov * Bring MMC host in PM state (one level deeper). 1456dd498effSDenis Karpov */ 145770a3341aSDenis Karpov static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy) 1458dd498effSDenis Karpov { 145970a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1460dd498effSDenis Karpov 1461dd498effSDenis Karpov switch (host->dpm_state) { 1462dd498effSDenis Karpov case ENABLED: { 1463dd498effSDenis Karpov int delay; 1464dd498effSDenis Karpov 146570a3341aSDenis Karpov delay = omap_hsmmc_enabled_to_disabled(host); 1466dd498effSDenis Karpov if (lazy || delay < 0) 1467dd498effSDenis Karpov return delay; 1468dd498effSDenis Karpov return 0; 1469dd498effSDenis Karpov } 1470dd498effSDenis Karpov case DISABLED: 147170a3341aSDenis Karpov return omap_hsmmc_disabled_to_sleep(host); 147213189e78SJarkko Lavinen case CARDSLEEP: 147313189e78SJarkko Lavinen case REGSLEEP: 147470a3341aSDenis Karpov return omap_hsmmc_sleep_to_off(host); 1475dd498effSDenis Karpov default: 1476dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); 1477dd498effSDenis Karpov return -EINVAL; 1478dd498effSDenis Karpov } 1479dd498effSDenis Karpov } 1480dd498effSDenis Karpov 148170a3341aSDenis Karpov static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) 1482dd498effSDenis Karpov { 148370a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1484dd498effSDenis Karpov int err; 1485dd498effSDenis Karpov 1486dd498effSDenis Karpov err = clk_enable(host->fclk); 1487dd498effSDenis Karpov if (err) 1488dd498effSDenis Karpov return err; 1489dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); 149070a3341aSDenis Karpov omap_hsmmc_context_restore(host); 1491dd498effSDenis Karpov return 0; 1492dd498effSDenis Karpov } 1493dd498effSDenis Karpov 149470a3341aSDenis Karpov static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy) 1495dd498effSDenis Karpov { 149670a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 1497dd498effSDenis Karpov 149870a3341aSDenis Karpov omap_hsmmc_context_save(host); 1499dd498effSDenis Karpov clk_disable(host->fclk); 1500dd498effSDenis Karpov dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n"); 1501dd498effSDenis Karpov return 0; 1502dd498effSDenis Karpov } 1503dd498effSDenis Karpov 150470a3341aSDenis Karpov static const struct mmc_host_ops omap_hsmmc_ops = { 150570a3341aSDenis Karpov .enable = omap_hsmmc_enable_fclk, 150670a3341aSDenis Karpov .disable = omap_hsmmc_disable_fclk, 150770a3341aSDenis Karpov .request = omap_hsmmc_request, 150870a3341aSDenis Karpov .set_ios = omap_hsmmc_set_ios, 1509dd498effSDenis Karpov .get_cd = omap_hsmmc_get_cd, 1510dd498effSDenis Karpov .get_ro = omap_hsmmc_get_ro, 1511dd498effSDenis Karpov /* NYET -- enable_sdio_irq */ 1512dd498effSDenis Karpov }; 1513dd498effSDenis Karpov 151470a3341aSDenis Karpov static const struct mmc_host_ops omap_hsmmc_ps_ops = { 151570a3341aSDenis Karpov .enable = omap_hsmmc_enable, 151670a3341aSDenis Karpov .disable = omap_hsmmc_disable, 151770a3341aSDenis Karpov .request = omap_hsmmc_request, 151870a3341aSDenis Karpov .set_ios = omap_hsmmc_set_ios, 1519a45c6cb8SMadhusudhan Chikkature .get_cd = omap_hsmmc_get_cd, 1520a45c6cb8SMadhusudhan Chikkature .get_ro = omap_hsmmc_get_ro, 1521a45c6cb8SMadhusudhan Chikkature /* NYET -- enable_sdio_irq */ 1522a45c6cb8SMadhusudhan Chikkature }; 1523a45c6cb8SMadhusudhan Chikkature 1524d900f712SDenis Karpov #ifdef CONFIG_DEBUG_FS 1525d900f712SDenis Karpov 152670a3341aSDenis Karpov static int omap_hsmmc_regs_show(struct seq_file *s, void *data) 1527d900f712SDenis Karpov { 1528d900f712SDenis Karpov struct mmc_host *mmc = s->private; 152970a3341aSDenis Karpov struct omap_hsmmc_host *host = mmc_priv(mmc); 153011dd62a7SDenis Karpov int context_loss = 0; 153111dd62a7SDenis Karpov 153270a3341aSDenis Karpov if (host->pdata->get_context_loss_count) 153370a3341aSDenis Karpov context_loss = host->pdata->get_context_loss_count(host->dev); 1534d900f712SDenis Karpov 15355e2ea617SAdrian Hunter seq_printf(s, "mmc%d:\n" 15365e2ea617SAdrian Hunter " enabled:\t%d\n" 1537dd498effSDenis Karpov " dpm_state:\t%d\n" 15385e2ea617SAdrian Hunter " nesting_cnt:\t%d\n" 153911dd62a7SDenis Karpov " ctx_loss:\t%d:%d\n" 15405e2ea617SAdrian Hunter "\nregs:\n", 1541dd498effSDenis Karpov mmc->index, mmc->enabled ? 1 : 0, 1542dd498effSDenis Karpov host->dpm_state, mmc->nesting_cnt, 154311dd62a7SDenis Karpov host->context_loss, context_loss); 15445e2ea617SAdrian Hunter 154513189e78SJarkko Lavinen if (host->suspended || host->dpm_state == OFF) { 1546dd498effSDenis Karpov seq_printf(s, "host suspended, can't read registers\n"); 1547dd498effSDenis Karpov return 0; 1548dd498effSDenis Karpov } 1549dd498effSDenis Karpov 15505e2ea617SAdrian Hunter if (clk_enable(host->fclk) != 0) { 15515e2ea617SAdrian Hunter seq_printf(s, "can't read the regs\n"); 1552dd498effSDenis Karpov return 0; 15535e2ea617SAdrian Hunter } 1554d900f712SDenis Karpov 1555d900f712SDenis Karpov seq_printf(s, "SYSCONFIG:\t0x%08x\n", 1556d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, SYSCONFIG)); 1557d900f712SDenis Karpov seq_printf(s, "CON:\t\t0x%08x\n", 1558d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, CON)); 1559d900f712SDenis Karpov seq_printf(s, "HCTL:\t\t0x%08x\n", 1560d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, HCTL)); 1561d900f712SDenis Karpov seq_printf(s, "SYSCTL:\t\t0x%08x\n", 1562d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, SYSCTL)); 1563d900f712SDenis Karpov seq_printf(s, "IE:\t\t0x%08x\n", 1564d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, IE)); 1565d900f712SDenis Karpov seq_printf(s, "ISE:\t\t0x%08x\n", 1566d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, ISE)); 1567d900f712SDenis Karpov seq_printf(s, "CAPA:\t\t0x%08x\n", 1568d900f712SDenis Karpov OMAP_HSMMC_READ(host->base, CAPA)); 15695e2ea617SAdrian Hunter 15705e2ea617SAdrian Hunter clk_disable(host->fclk); 1571dd498effSDenis Karpov 1572d900f712SDenis Karpov return 0; 1573d900f712SDenis Karpov } 1574d900f712SDenis Karpov 157570a3341aSDenis Karpov static int omap_hsmmc_regs_open(struct inode *inode, struct file *file) 1576d900f712SDenis Karpov { 157770a3341aSDenis Karpov return single_open(file, omap_hsmmc_regs_show, inode->i_private); 1578d900f712SDenis Karpov } 1579d900f712SDenis Karpov 1580d900f712SDenis Karpov static const struct file_operations mmc_regs_fops = { 158170a3341aSDenis Karpov .open = omap_hsmmc_regs_open, 1582d900f712SDenis Karpov .read = seq_read, 1583d900f712SDenis Karpov .llseek = seq_lseek, 1584d900f712SDenis Karpov .release = single_release, 1585d900f712SDenis Karpov }; 1586d900f712SDenis Karpov 158770a3341aSDenis Karpov static void omap_hsmmc_debugfs(struct mmc_host *mmc) 1588d900f712SDenis Karpov { 1589d900f712SDenis Karpov if (mmc->debugfs_root) 1590d900f712SDenis Karpov debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root, 1591d900f712SDenis Karpov mmc, &mmc_regs_fops); 1592d900f712SDenis Karpov } 1593d900f712SDenis Karpov 1594d900f712SDenis Karpov #else 1595d900f712SDenis Karpov 159670a3341aSDenis Karpov static void omap_hsmmc_debugfs(struct mmc_host *mmc) 1597d900f712SDenis Karpov { 1598d900f712SDenis Karpov } 1599d900f712SDenis Karpov 1600d900f712SDenis Karpov #endif 1601d900f712SDenis Karpov 160270a3341aSDenis Karpov static int __init omap_hsmmc_probe(struct platform_device *pdev) 1603a45c6cb8SMadhusudhan Chikkature { 1604a45c6cb8SMadhusudhan Chikkature struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; 1605a45c6cb8SMadhusudhan Chikkature struct mmc_host *mmc; 160670a3341aSDenis Karpov struct omap_hsmmc_host *host = NULL; 1607a45c6cb8SMadhusudhan Chikkature struct resource *res; 1608a45c6cb8SMadhusudhan Chikkature int ret = 0, irq; 1609a45c6cb8SMadhusudhan Chikkature 1610a45c6cb8SMadhusudhan Chikkature if (pdata == NULL) { 1611a45c6cb8SMadhusudhan Chikkature dev_err(&pdev->dev, "Platform Data is missing\n"); 1612a45c6cb8SMadhusudhan Chikkature return -ENXIO; 1613a45c6cb8SMadhusudhan Chikkature } 1614a45c6cb8SMadhusudhan Chikkature 1615a45c6cb8SMadhusudhan Chikkature if (pdata->nr_slots == 0) { 1616a45c6cb8SMadhusudhan Chikkature dev_err(&pdev->dev, "No Slots\n"); 1617a45c6cb8SMadhusudhan Chikkature return -ENXIO; 1618a45c6cb8SMadhusudhan Chikkature } 1619a45c6cb8SMadhusudhan Chikkature 1620a45c6cb8SMadhusudhan Chikkature res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1621a45c6cb8SMadhusudhan Chikkature irq = platform_get_irq(pdev, 0); 1622a45c6cb8SMadhusudhan Chikkature if (res == NULL || irq < 0) 1623a45c6cb8SMadhusudhan Chikkature return -ENXIO; 1624a45c6cb8SMadhusudhan Chikkature 1625a45c6cb8SMadhusudhan Chikkature res = request_mem_region(res->start, res->end - res->start + 1, 1626a45c6cb8SMadhusudhan Chikkature pdev->name); 1627a45c6cb8SMadhusudhan Chikkature if (res == NULL) 1628a45c6cb8SMadhusudhan Chikkature return -EBUSY; 1629a45c6cb8SMadhusudhan Chikkature 163070a3341aSDenis Karpov mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); 1631a45c6cb8SMadhusudhan Chikkature if (!mmc) { 1632a45c6cb8SMadhusudhan Chikkature ret = -ENOMEM; 1633a45c6cb8SMadhusudhan Chikkature goto err; 1634a45c6cb8SMadhusudhan Chikkature } 1635a45c6cb8SMadhusudhan Chikkature 1636a45c6cb8SMadhusudhan Chikkature host = mmc_priv(mmc); 1637a45c6cb8SMadhusudhan Chikkature host->mmc = mmc; 1638a45c6cb8SMadhusudhan Chikkature host->pdata = pdata; 1639a45c6cb8SMadhusudhan Chikkature host->dev = &pdev->dev; 1640a45c6cb8SMadhusudhan Chikkature host->use_dma = 1; 1641a45c6cb8SMadhusudhan Chikkature host->dev->dma_mask = &pdata->dma_mask; 1642a45c6cb8SMadhusudhan Chikkature host->dma_ch = -1; 1643a45c6cb8SMadhusudhan Chikkature host->irq = irq; 1644a45c6cb8SMadhusudhan Chikkature host->id = pdev->id; 1645a45c6cb8SMadhusudhan Chikkature host->slot_id = 0; 1646a45c6cb8SMadhusudhan Chikkature host->mapbase = res->start; 1647a45c6cb8SMadhusudhan Chikkature host->base = ioremap(host->mapbase, SZ_4K); 1648a3621465SAdrian Hunter host->power_mode = -1; 1649a45c6cb8SMadhusudhan Chikkature 1650a45c6cb8SMadhusudhan Chikkature platform_set_drvdata(pdev, host); 165170a3341aSDenis Karpov INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect); 1652a45c6cb8SMadhusudhan Chikkature 1653191d1f1dSDenis Karpov if (mmc_slot(host).power_saving) 165470a3341aSDenis Karpov mmc->ops = &omap_hsmmc_ps_ops; 1655dd498effSDenis Karpov else 165670a3341aSDenis Karpov mmc->ops = &omap_hsmmc_ops; 1657dd498effSDenis Karpov 1658a45c6cb8SMadhusudhan Chikkature mmc->f_min = 400000; 1659a45c6cb8SMadhusudhan Chikkature mmc->f_max = 52000000; 1660a45c6cb8SMadhusudhan Chikkature 1661a45c6cb8SMadhusudhan Chikkature sema_init(&host->sem, 1); 16624dffd7a2SAdrian Hunter spin_lock_init(&host->irq_lock); 1663a45c6cb8SMadhusudhan Chikkature 16646f7607ccSRussell King host->iclk = clk_get(&pdev->dev, "ick"); 1665a45c6cb8SMadhusudhan Chikkature if (IS_ERR(host->iclk)) { 1666a45c6cb8SMadhusudhan Chikkature ret = PTR_ERR(host->iclk); 1667a45c6cb8SMadhusudhan Chikkature host->iclk = NULL; 1668a45c6cb8SMadhusudhan Chikkature goto err1; 1669a45c6cb8SMadhusudhan Chikkature } 16706f7607ccSRussell King host->fclk = clk_get(&pdev->dev, "fck"); 1671a45c6cb8SMadhusudhan Chikkature if (IS_ERR(host->fclk)) { 1672a45c6cb8SMadhusudhan Chikkature ret = PTR_ERR(host->fclk); 1673a45c6cb8SMadhusudhan Chikkature host->fclk = NULL; 1674a45c6cb8SMadhusudhan Chikkature clk_put(host->iclk); 1675a45c6cb8SMadhusudhan Chikkature goto err1; 1676a45c6cb8SMadhusudhan Chikkature } 1677a45c6cb8SMadhusudhan Chikkature 167870a3341aSDenis Karpov omap_hsmmc_context_save(host); 167911dd62a7SDenis Karpov 16805e2ea617SAdrian Hunter mmc->caps |= MMC_CAP_DISABLE; 1681dd498effSDenis Karpov mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT); 1682dd498effSDenis Karpov /* we start off in DISABLED state */ 1683dd498effSDenis Karpov host->dpm_state = DISABLED; 1684dd498effSDenis Karpov 16855e2ea617SAdrian Hunter if (mmc_host_enable(host->mmc) != 0) { 1686a45c6cb8SMadhusudhan Chikkature clk_put(host->iclk); 1687a45c6cb8SMadhusudhan Chikkature clk_put(host->fclk); 1688a45c6cb8SMadhusudhan Chikkature goto err1; 1689a45c6cb8SMadhusudhan Chikkature } 1690a45c6cb8SMadhusudhan Chikkature 1691a45c6cb8SMadhusudhan Chikkature if (clk_enable(host->iclk) != 0) { 16925e2ea617SAdrian Hunter mmc_host_disable(host->mmc); 1693a45c6cb8SMadhusudhan Chikkature clk_put(host->iclk); 1694a45c6cb8SMadhusudhan Chikkature clk_put(host->fclk); 1695a45c6cb8SMadhusudhan Chikkature goto err1; 1696a45c6cb8SMadhusudhan Chikkature } 1697a45c6cb8SMadhusudhan Chikkature 1698a45c6cb8SMadhusudhan Chikkature host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); 1699a45c6cb8SMadhusudhan Chikkature /* 1700a45c6cb8SMadhusudhan Chikkature * MMC can still work without debounce clock. 1701a45c6cb8SMadhusudhan Chikkature */ 1702a45c6cb8SMadhusudhan Chikkature if (IS_ERR(host->dbclk)) 1703a45c6cb8SMadhusudhan Chikkature dev_warn(mmc_dev(host->mmc), "Failed to get debounce clock\n"); 1704a45c6cb8SMadhusudhan Chikkature else 1705a45c6cb8SMadhusudhan Chikkature if (clk_enable(host->dbclk) != 0) 1706a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "Enabling debounce" 1707a45c6cb8SMadhusudhan Chikkature " clk failed\n"); 1708a45c6cb8SMadhusudhan Chikkature else 1709a45c6cb8SMadhusudhan Chikkature host->dbclk_enabled = 1; 1710a45c6cb8SMadhusudhan Chikkature 17110ccd76d4SJuha Yrjola /* Since we do only SG emulation, we can have as many segs 17120ccd76d4SJuha Yrjola * as we want. */ 17130ccd76d4SJuha Yrjola mmc->max_phys_segs = 1024; 17140ccd76d4SJuha Yrjola mmc->max_hw_segs = 1024; 17150ccd76d4SJuha Yrjola 1716a45c6cb8SMadhusudhan Chikkature mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ 1717a45c6cb8SMadhusudhan Chikkature mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ 1718a45c6cb8SMadhusudhan Chikkature mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 1719a45c6cb8SMadhusudhan Chikkature mmc->max_seg_size = mmc->max_req_size; 1720a45c6cb8SMadhusudhan Chikkature 172113189e78SJarkko Lavinen mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | 172213189e78SJarkko Lavinen MMC_CAP_WAIT_WHILE_BUSY; 1723a45c6cb8SMadhusudhan Chikkature 1724191d1f1dSDenis Karpov if (mmc_slot(host).wires >= 8) 172573153010SJarkko Lavinen mmc->caps |= MMC_CAP_8_BIT_DATA; 1726191d1f1dSDenis Karpov else if (mmc_slot(host).wires >= 4) 1727a45c6cb8SMadhusudhan Chikkature mmc->caps |= MMC_CAP_4_BIT_DATA; 1728a45c6cb8SMadhusudhan Chikkature 1729191d1f1dSDenis Karpov if (mmc_slot(host).nonremovable) 173023d99bb9SAdrian Hunter mmc->caps |= MMC_CAP_NONREMOVABLE; 173123d99bb9SAdrian Hunter 173270a3341aSDenis Karpov omap_hsmmc_conf_bus_power(host); 1733a45c6cb8SMadhusudhan Chikkature 1734f3e2f1ddSGrazvydas Ignotas /* Select DMA lines */ 1735f3e2f1ddSGrazvydas Ignotas switch (host->id) { 1736f3e2f1ddSGrazvydas Ignotas case OMAP_MMC1_DEVID: 1737f3e2f1ddSGrazvydas Ignotas host->dma_line_tx = OMAP24XX_DMA_MMC1_TX; 1738f3e2f1ddSGrazvydas Ignotas host->dma_line_rx = OMAP24XX_DMA_MMC1_RX; 1739f3e2f1ddSGrazvydas Ignotas break; 1740f3e2f1ddSGrazvydas Ignotas case OMAP_MMC2_DEVID: 1741f3e2f1ddSGrazvydas Ignotas host->dma_line_tx = OMAP24XX_DMA_MMC2_TX; 1742f3e2f1ddSGrazvydas Ignotas host->dma_line_rx = OMAP24XX_DMA_MMC2_RX; 1743f3e2f1ddSGrazvydas Ignotas break; 1744f3e2f1ddSGrazvydas Ignotas case OMAP_MMC3_DEVID: 1745f3e2f1ddSGrazvydas Ignotas host->dma_line_tx = OMAP34XX_DMA_MMC3_TX; 1746f3e2f1ddSGrazvydas Ignotas host->dma_line_rx = OMAP34XX_DMA_MMC3_RX; 1747f3e2f1ddSGrazvydas Ignotas break; 1748f3e2f1ddSGrazvydas Ignotas default: 1749f3e2f1ddSGrazvydas Ignotas dev_err(mmc_dev(host->mmc), "Invalid MMC id\n"); 1750f3e2f1ddSGrazvydas Ignotas goto err_irq; 1751a45c6cb8SMadhusudhan Chikkature } 1752a45c6cb8SMadhusudhan Chikkature 1753a45c6cb8SMadhusudhan Chikkature /* Request IRQ for MMC operations */ 175470a3341aSDenis Karpov ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED, 1755a45c6cb8SMadhusudhan Chikkature mmc_hostname(mmc), host); 1756a45c6cb8SMadhusudhan Chikkature if (ret) { 1757a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); 1758a45c6cb8SMadhusudhan Chikkature goto err_irq; 1759a45c6cb8SMadhusudhan Chikkature } 1760a45c6cb8SMadhusudhan Chikkature 1761b583f26dSDavid Brownell /* initialize power supplies, gpios, etc */ 1762a45c6cb8SMadhusudhan Chikkature if (pdata->init != NULL) { 1763a45c6cb8SMadhusudhan Chikkature if (pdata->init(&pdev->dev) != 0) { 176470a3341aSDenis Karpov dev_dbg(mmc_dev(host->mmc), 176570a3341aSDenis Karpov "Unable to configure MMC IRQs\n"); 1766a45c6cb8SMadhusudhan Chikkature goto err_irq_cd_init; 1767a45c6cb8SMadhusudhan Chikkature } 1768a45c6cb8SMadhusudhan Chikkature } 1769b583f26dSDavid Brownell mmc->ocr_avail = mmc_slot(host).ocr_mask; 1770a45c6cb8SMadhusudhan Chikkature 1771a45c6cb8SMadhusudhan Chikkature /* Request IRQ for card detect */ 1772e1a55f5eSAdrian Hunter if ((mmc_slot(host).card_detect_irq)) { 1773a45c6cb8SMadhusudhan Chikkature ret = request_irq(mmc_slot(host).card_detect_irq, 177470a3341aSDenis Karpov omap_hsmmc_cd_handler, 1775a45c6cb8SMadhusudhan Chikkature IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING 1776a45c6cb8SMadhusudhan Chikkature | IRQF_DISABLED, 1777a45c6cb8SMadhusudhan Chikkature mmc_hostname(mmc), host); 1778a45c6cb8SMadhusudhan Chikkature if (ret) { 1779a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 1780a45c6cb8SMadhusudhan Chikkature "Unable to grab MMC CD IRQ\n"); 1781a45c6cb8SMadhusudhan Chikkature goto err_irq_cd; 1782a45c6cb8SMadhusudhan Chikkature } 1783a45c6cb8SMadhusudhan Chikkature } 1784a45c6cb8SMadhusudhan Chikkature 1785a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); 1786a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); 1787a45c6cb8SMadhusudhan Chikkature 17885e2ea617SAdrian Hunter mmc_host_lazy_disable(host->mmc); 17895e2ea617SAdrian Hunter 1790b62f6228SAdrian Hunter omap_hsmmc_protect_card(host); 1791b62f6228SAdrian Hunter 1792a45c6cb8SMadhusudhan Chikkature mmc_add_host(mmc); 1793a45c6cb8SMadhusudhan Chikkature 1794191d1f1dSDenis Karpov if (mmc_slot(host).name != NULL) { 1795a45c6cb8SMadhusudhan Chikkature ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); 1796a45c6cb8SMadhusudhan Chikkature if (ret < 0) 1797a45c6cb8SMadhusudhan Chikkature goto err_slot_name; 1798a45c6cb8SMadhusudhan Chikkature } 1799191d1f1dSDenis Karpov if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) { 1800a45c6cb8SMadhusudhan Chikkature ret = device_create_file(&mmc->class_dev, 1801a45c6cb8SMadhusudhan Chikkature &dev_attr_cover_switch); 1802a45c6cb8SMadhusudhan Chikkature if (ret < 0) 1803a45c6cb8SMadhusudhan Chikkature goto err_cover_switch; 1804a45c6cb8SMadhusudhan Chikkature } 1805a45c6cb8SMadhusudhan Chikkature 180670a3341aSDenis Karpov omap_hsmmc_debugfs(mmc); 1807d900f712SDenis Karpov 1808a45c6cb8SMadhusudhan Chikkature return 0; 1809a45c6cb8SMadhusudhan Chikkature 1810a45c6cb8SMadhusudhan Chikkature err_cover_switch: 1811a45c6cb8SMadhusudhan Chikkature device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); 1812a45c6cb8SMadhusudhan Chikkature err_slot_name: 1813a45c6cb8SMadhusudhan Chikkature mmc_remove_host(mmc); 1814a45c6cb8SMadhusudhan Chikkature err_irq_cd: 1815a45c6cb8SMadhusudhan Chikkature free_irq(mmc_slot(host).card_detect_irq, host); 1816a45c6cb8SMadhusudhan Chikkature err_irq_cd_init: 1817a45c6cb8SMadhusudhan Chikkature free_irq(host->irq, host); 1818a45c6cb8SMadhusudhan Chikkature err_irq: 18195e2ea617SAdrian Hunter mmc_host_disable(host->mmc); 1820a45c6cb8SMadhusudhan Chikkature clk_disable(host->iclk); 1821a45c6cb8SMadhusudhan Chikkature clk_put(host->fclk); 1822a45c6cb8SMadhusudhan Chikkature clk_put(host->iclk); 1823a45c6cb8SMadhusudhan Chikkature if (host->dbclk_enabled) { 1824a45c6cb8SMadhusudhan Chikkature clk_disable(host->dbclk); 1825a45c6cb8SMadhusudhan Chikkature clk_put(host->dbclk); 1826a45c6cb8SMadhusudhan Chikkature } 1827a45c6cb8SMadhusudhan Chikkature 1828a45c6cb8SMadhusudhan Chikkature err1: 1829a45c6cb8SMadhusudhan Chikkature iounmap(host->base); 1830a45c6cb8SMadhusudhan Chikkature err: 1831a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), "Probe Failed\n"); 1832a45c6cb8SMadhusudhan Chikkature release_mem_region(res->start, res->end - res->start + 1); 1833a45c6cb8SMadhusudhan Chikkature if (host) 1834a45c6cb8SMadhusudhan Chikkature mmc_free_host(mmc); 1835a45c6cb8SMadhusudhan Chikkature return ret; 1836a45c6cb8SMadhusudhan Chikkature } 1837a45c6cb8SMadhusudhan Chikkature 183870a3341aSDenis Karpov static int omap_hsmmc_remove(struct platform_device *pdev) 1839a45c6cb8SMadhusudhan Chikkature { 184070a3341aSDenis Karpov struct omap_hsmmc_host *host = platform_get_drvdata(pdev); 1841a45c6cb8SMadhusudhan Chikkature struct resource *res; 1842a45c6cb8SMadhusudhan Chikkature 1843a45c6cb8SMadhusudhan Chikkature if (host) { 18445e2ea617SAdrian Hunter mmc_host_enable(host->mmc); 1845a45c6cb8SMadhusudhan Chikkature mmc_remove_host(host->mmc); 1846a45c6cb8SMadhusudhan Chikkature if (host->pdata->cleanup) 1847a45c6cb8SMadhusudhan Chikkature host->pdata->cleanup(&pdev->dev); 1848a45c6cb8SMadhusudhan Chikkature free_irq(host->irq, host); 1849a45c6cb8SMadhusudhan Chikkature if (mmc_slot(host).card_detect_irq) 1850a45c6cb8SMadhusudhan Chikkature free_irq(mmc_slot(host).card_detect_irq, host); 1851a45c6cb8SMadhusudhan Chikkature flush_scheduled_work(); 1852a45c6cb8SMadhusudhan Chikkature 18535e2ea617SAdrian Hunter mmc_host_disable(host->mmc); 1854a45c6cb8SMadhusudhan Chikkature clk_disable(host->iclk); 1855a45c6cb8SMadhusudhan Chikkature clk_put(host->fclk); 1856a45c6cb8SMadhusudhan Chikkature clk_put(host->iclk); 1857a45c6cb8SMadhusudhan Chikkature if (host->dbclk_enabled) { 1858a45c6cb8SMadhusudhan Chikkature clk_disable(host->dbclk); 1859a45c6cb8SMadhusudhan Chikkature clk_put(host->dbclk); 1860a45c6cb8SMadhusudhan Chikkature } 1861a45c6cb8SMadhusudhan Chikkature 1862a45c6cb8SMadhusudhan Chikkature mmc_free_host(host->mmc); 1863a45c6cb8SMadhusudhan Chikkature iounmap(host->base); 1864a45c6cb8SMadhusudhan Chikkature } 1865a45c6cb8SMadhusudhan Chikkature 1866a45c6cb8SMadhusudhan Chikkature res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1867a45c6cb8SMadhusudhan Chikkature if (res) 1868a45c6cb8SMadhusudhan Chikkature release_mem_region(res->start, res->end - res->start + 1); 1869a45c6cb8SMadhusudhan Chikkature platform_set_drvdata(pdev, NULL); 1870a45c6cb8SMadhusudhan Chikkature 1871a45c6cb8SMadhusudhan Chikkature return 0; 1872a45c6cb8SMadhusudhan Chikkature } 1873a45c6cb8SMadhusudhan Chikkature 1874a45c6cb8SMadhusudhan Chikkature #ifdef CONFIG_PM 187570a3341aSDenis Karpov static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state) 1876a45c6cb8SMadhusudhan Chikkature { 1877a45c6cb8SMadhusudhan Chikkature int ret = 0; 187870a3341aSDenis Karpov struct omap_hsmmc_host *host = platform_get_drvdata(pdev); 1879a45c6cb8SMadhusudhan Chikkature 1880a45c6cb8SMadhusudhan Chikkature if (host && host->suspended) 1881a45c6cb8SMadhusudhan Chikkature return 0; 1882a45c6cb8SMadhusudhan Chikkature 1883a45c6cb8SMadhusudhan Chikkature if (host) { 1884a45c6cb8SMadhusudhan Chikkature host->suspended = 1; 1885a45c6cb8SMadhusudhan Chikkature if (host->pdata->suspend) { 1886a45c6cb8SMadhusudhan Chikkature ret = host->pdata->suspend(&pdev->dev, 1887a45c6cb8SMadhusudhan Chikkature host->slot_id); 1888a6b2240dSAdrian Hunter if (ret) { 1889a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 1890a45c6cb8SMadhusudhan Chikkature "Unable to handle MMC board" 1891a45c6cb8SMadhusudhan Chikkature " level suspend\n"); 1892a6b2240dSAdrian Hunter host->suspended = 0; 1893a6b2240dSAdrian Hunter return ret; 1894a45c6cb8SMadhusudhan Chikkature } 1895a6b2240dSAdrian Hunter } 1896a6b2240dSAdrian Hunter cancel_work_sync(&host->mmc_carddetect_work); 1897a6b2240dSAdrian Hunter mmc_host_enable(host->mmc); 1898a6b2240dSAdrian Hunter ret = mmc_suspend_host(host->mmc, state); 1899a6b2240dSAdrian Hunter if (ret == 0) { 1900a6b2240dSAdrian Hunter OMAP_HSMMC_WRITE(host->base, ISE, 0); 1901a6b2240dSAdrian Hunter OMAP_HSMMC_WRITE(host->base, IE, 0); 1902a6b2240dSAdrian Hunter 1903a45c6cb8SMadhusudhan Chikkature 1904a45c6cb8SMadhusudhan Chikkature OMAP_HSMMC_WRITE(host->base, HCTL, 19050683af48SJarkko Lavinen OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); 19065e2ea617SAdrian Hunter mmc_host_disable(host->mmc); 1907a45c6cb8SMadhusudhan Chikkature clk_disable(host->iclk); 1908a45c6cb8SMadhusudhan Chikkature clk_disable(host->dbclk); 1909a6b2240dSAdrian Hunter } else { 1910a6b2240dSAdrian Hunter host->suspended = 0; 1911a6b2240dSAdrian Hunter if (host->pdata->resume) { 1912a6b2240dSAdrian Hunter ret = host->pdata->resume(&pdev->dev, 1913a6b2240dSAdrian Hunter host->slot_id); 1914a6b2240dSAdrian Hunter if (ret) 1915a6b2240dSAdrian Hunter dev_dbg(mmc_dev(host->mmc), 1916a6b2240dSAdrian Hunter "Unmask interrupt failed\n"); 1917a6b2240dSAdrian Hunter } 19185e2ea617SAdrian Hunter mmc_host_disable(host->mmc); 1919a6b2240dSAdrian Hunter } 1920a45c6cb8SMadhusudhan Chikkature 1921a45c6cb8SMadhusudhan Chikkature } 1922a45c6cb8SMadhusudhan Chikkature return ret; 1923a45c6cb8SMadhusudhan Chikkature } 1924a45c6cb8SMadhusudhan Chikkature 1925a45c6cb8SMadhusudhan Chikkature /* Routine to resume the MMC device */ 192670a3341aSDenis Karpov static int omap_hsmmc_resume(struct platform_device *pdev) 1927a45c6cb8SMadhusudhan Chikkature { 1928a45c6cb8SMadhusudhan Chikkature int ret = 0; 192970a3341aSDenis Karpov struct omap_hsmmc_host *host = platform_get_drvdata(pdev); 1930a45c6cb8SMadhusudhan Chikkature 1931a45c6cb8SMadhusudhan Chikkature if (host && !host->suspended) 1932a45c6cb8SMadhusudhan Chikkature return 0; 1933a45c6cb8SMadhusudhan Chikkature 1934a45c6cb8SMadhusudhan Chikkature if (host) { 1935a45c6cb8SMadhusudhan Chikkature ret = clk_enable(host->iclk); 193611dd62a7SDenis Karpov if (ret) 1937a45c6cb8SMadhusudhan Chikkature goto clk_en_err; 1938a45c6cb8SMadhusudhan Chikkature 1939a45c6cb8SMadhusudhan Chikkature if (clk_enable(host->dbclk) != 0) 1940a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 1941a45c6cb8SMadhusudhan Chikkature "Enabling debounce clk failed\n"); 1942a45c6cb8SMadhusudhan Chikkature 194311dd62a7SDenis Karpov if (mmc_host_enable(host->mmc) != 0) { 194411dd62a7SDenis Karpov clk_disable(host->iclk); 194511dd62a7SDenis Karpov goto clk_en_err; 194611dd62a7SDenis Karpov } 194711dd62a7SDenis Karpov 194870a3341aSDenis Karpov omap_hsmmc_conf_bus_power(host); 19491b331e69SKim Kyuwon 1950a45c6cb8SMadhusudhan Chikkature if (host->pdata->resume) { 1951a45c6cb8SMadhusudhan Chikkature ret = host->pdata->resume(&pdev->dev, host->slot_id); 1952a45c6cb8SMadhusudhan Chikkature if (ret) 1953a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 1954a45c6cb8SMadhusudhan Chikkature "Unmask interrupt failed\n"); 1955a45c6cb8SMadhusudhan Chikkature } 1956a45c6cb8SMadhusudhan Chikkature 1957b62f6228SAdrian Hunter omap_hsmmc_protect_card(host); 1958b62f6228SAdrian Hunter 1959a45c6cb8SMadhusudhan Chikkature /* Notify the core to resume the host */ 1960a45c6cb8SMadhusudhan Chikkature ret = mmc_resume_host(host->mmc); 1961a45c6cb8SMadhusudhan Chikkature if (ret == 0) 1962a45c6cb8SMadhusudhan Chikkature host->suspended = 0; 196370a3341aSDenis Karpov 19645e2ea617SAdrian Hunter mmc_host_lazy_disable(host->mmc); 1965a45c6cb8SMadhusudhan Chikkature } 1966a45c6cb8SMadhusudhan Chikkature 1967a45c6cb8SMadhusudhan Chikkature return ret; 1968a45c6cb8SMadhusudhan Chikkature 1969a45c6cb8SMadhusudhan Chikkature clk_en_err: 1970a45c6cb8SMadhusudhan Chikkature dev_dbg(mmc_dev(host->mmc), 1971a45c6cb8SMadhusudhan Chikkature "Failed to enable MMC clocks during resume\n"); 1972a45c6cb8SMadhusudhan Chikkature return ret; 1973a45c6cb8SMadhusudhan Chikkature } 1974a45c6cb8SMadhusudhan Chikkature 1975a45c6cb8SMadhusudhan Chikkature #else 197670a3341aSDenis Karpov #define omap_hsmmc_suspend NULL 197770a3341aSDenis Karpov #define omap_hsmmc_resume NULL 1978a45c6cb8SMadhusudhan Chikkature #endif 1979a45c6cb8SMadhusudhan Chikkature 198070a3341aSDenis Karpov static struct platform_driver omap_hsmmc_driver = { 198170a3341aSDenis Karpov .remove = omap_hsmmc_remove, 198270a3341aSDenis Karpov .suspend = omap_hsmmc_suspend, 198370a3341aSDenis Karpov .resume = omap_hsmmc_resume, 1984a45c6cb8SMadhusudhan Chikkature .driver = { 1985a45c6cb8SMadhusudhan Chikkature .name = DRIVER_NAME, 1986a45c6cb8SMadhusudhan Chikkature .owner = THIS_MODULE, 1987a45c6cb8SMadhusudhan Chikkature }, 1988a45c6cb8SMadhusudhan Chikkature }; 1989a45c6cb8SMadhusudhan Chikkature 199070a3341aSDenis Karpov static int __init omap_hsmmc_init(void) 1991a45c6cb8SMadhusudhan Chikkature { 1992a45c6cb8SMadhusudhan Chikkature /* Register the MMC driver */ 199370a3341aSDenis Karpov return platform_driver_register(&omap_hsmmc_driver); 1994a45c6cb8SMadhusudhan Chikkature } 1995a45c6cb8SMadhusudhan Chikkature 199670a3341aSDenis Karpov static void __exit omap_hsmmc_cleanup(void) 1997a45c6cb8SMadhusudhan Chikkature { 1998a45c6cb8SMadhusudhan Chikkature /* Unregister MMC driver */ 199970a3341aSDenis Karpov platform_driver_unregister(&omap_hsmmc_driver); 2000a45c6cb8SMadhusudhan Chikkature } 2001a45c6cb8SMadhusudhan Chikkature 200270a3341aSDenis Karpov module_init(omap_hsmmc_init); 200370a3341aSDenis Karpov module_exit(omap_hsmmc_cleanup); 2004a45c6cb8SMadhusudhan Chikkature 2005a45c6cb8SMadhusudhan Chikkature MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver"); 2006a45c6cb8SMadhusudhan Chikkature MODULE_LICENSE("GPL"); 2007a45c6cb8SMadhusudhan Chikkature MODULE_ALIAS("platform:" DRIVER_NAME); 2008a45c6cb8SMadhusudhan Chikkature MODULE_AUTHOR("Texas Instruments Inc"); 2009