/* * linux/arch/arm/mach-omap2/board-sdp-hsmmc.c * * Copyright (C) 2007 Texas Instruments * Author: Texas Instruments * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MMC_OMAP_HS #define VMMC1_DEV_GRP 0x27 #define P1_DEV_GRP 0x20 #define VMMC1_DEDICATED 0x2A #define VSEL_3V 0x02 #define VSEL_18V 0x00 #define TWL_GPIO_PUPDCTR1 0x13 #define TWL_GPIO_IMR1A 0x1C #define TWL_GPIO_ISR1A 0x19 #define LDO_CLR 0x00 #define VSEL_S2_CLR 0x40 #define GPIO_0_BIT_POS 1 << 0 #define MMC1_CD_IRQ 0 #define MMC2_CD_IRQ 1 static irqreturn_t mmc_omap_cd_handler(int irq, void *dev_id) { int detect; detect = twl4030_get_gpio_datain(MMC1_CD_IRQ); omap_mmc_notify_card_detect(dev_id, 0, detect); return IRQ_HANDLED; } /* * MMC Slot Initialization. */ static int sdp_mmc_late_init(struct device *dev) { int ret = 0; /* * Configure TWL4030 GPIO parameters for MMC hotplug irq */ ret = twl4030_request_gpio(MMC1_CD_IRQ); if (ret != 0) goto err; ret = twl4030_set_gpio_edge_ctrl(MMC1_CD_IRQ, TWL4030_GPIO_EDGE_RISING | TWL4030_GPIO_EDGE_FALLING); if (ret != 0) goto err; ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0x02, TWL_GPIO_PUPDCTR1); if (ret != 0) goto err; ret = twl4030_set_gpio_debounce(MMC1_CD_IRQ, TWL4030_GPIO_IS_ENABLE); if (ret != 0) goto err; ret = request_irq(TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ), mmc_omap_cd_handler, IRQF_DISABLED, "MMC1_CD_IRQ", dev); if (ret < 0) goto err; return ret; err: dev_err(dev, "Failed to configure TWL4030 GPIO IRQ\n"); return ret; } static void sdp_mmc_cleanup(struct device *dev) { int ret = 0; ret = twl4030_free_gpio(MMC1_CD_IRQ); free_irq(TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ), dev); if (ret != 0) dev_err(dev, "Failed to configure TWL4030 GPIO IRQ\n"); } #ifdef CONFIG_PM /* * To mask and unmask MMC Card Detect Interrupt * mask : 1 * unmask : 0 */ static int mask_cd_interrupt(int mask) { u8 reg = 0, ret = 0; ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, ®, TWL_GPIO_IMR1A); if (ret != 0) goto err; reg = (mask == 1) ? (reg | GPIO_0_BIT_POS) : (reg & ~GPIO_0_BIT_POS); ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, reg, TWL_GPIO_IMR1A); if (ret != 0) goto err; ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, ®, TWL_GPIO_ISR1A); if (ret != 0) goto err; reg = (mask == 1) ? (reg | GPIO_0_BIT_POS) : (reg & ~GPIO_0_BIT_POS); ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, reg, TWL_GPIO_ISR1A); if (ret != 0) goto err; err: return ret; } static int sdp_mmc_suspend(struct device *dev, int slot) { int ret = 0; disable_irq(TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ)); ret = mask_cd_interrupt(1); return ret; } static int sdp_mmc_resume(struct device *dev, int slot) { int ret = 0; enable_irq(TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ)); ret = mask_cd_interrupt(0); return ret; } #endif static int sdp_mmc_set_power(struct device *dev, int slot, int power_on, int vdd) { u32 vdd_sel = 0, devconf = 0, reg = 0; int ret = 0; /* REVISIT: Using address directly till the control.h defines * are settled. */ #if defined(CONFIG_ARCH_OMAP2430) #define OMAP2_CONTROL_PBIAS 0x490024A0 #else #define OMAP2_CONTROL_PBIAS 0x48002520 #endif if (power_on == 1) { if (cpu_is_omap24xx()) devconf = omap_readl(0x490022E8); else devconf = omap_readl(0x48002274); switch (1 << vdd) { case MMC_VDD_33_34: case MMC_VDD_32_33: vdd_sel = VSEL_3V; if (cpu_is_omap24xx()) devconf = (reg | (1 << 31)); break; case MMC_VDD_165_195: vdd_sel = VSEL_18V; if (cpu_is_omap24xx()) devconf = (devconf & ~(1 << 31)); } if (cpu_is_omap24xx()) omap_writel(devconf, 0x490022E8); else omap_writel(devconf | 1 << 24, 0x48002274); omap_writel(omap_readl(OMAP2_CONTROL_PBIAS) | 1 << 2, OMAP2_CONTROL_PBIAS); omap_writel(omap_readl(OMAP2_CONTROL_PBIAS) & ~(1 << 1), OMAP2_CONTROL_PBIAS); ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, P1_DEV_GRP, VMMC1_DEV_GRP); if (ret != 0) goto err; ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, vdd_sel, VMMC1_DEDICATED); if (ret != 0) goto err; msleep(100); reg = omap_readl(OMAP2_CONTROL_PBIAS); reg = (vdd_sel == VSEL_18V) ? ((reg | 0x6) & ~0x1) : (reg | 0x7); omap_writel(reg, OMAP2_CONTROL_PBIAS); return ret; } else if (power_on == 0) { /* Power OFF */ /* For MMC1, Toggle PBIAS before every power up sequence */ omap_writel(omap_readl(OMAP2_CONTROL_PBIAS) & ~(1 << 1), OMAP2_CONTROL_PBIAS); ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, LDO_CLR, VMMC1_DEV_GRP); if (ret != 0) goto err; ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, VSEL_S2_CLR, VMMC1_DEDICATED); if (ret != 0) goto err; /* 100ms delay required for PBIAS configuration */ msleep(100); omap_writel(omap_readl(OMAP2_CONTROL_PBIAS) | 0x7, OMAP2_CONTROL_PBIAS); } else { ret = -1; goto err; } return 0; err: return 1; } static struct omap_mmc_platform_data sdp_mmc_data = { .nr_slots = 1, .switch_slot = NULL, .init = sdp_mmc_late_init, .cleanup = sdp_mmc_cleanup, #ifdef CONFIG_PM .suspend = sdp_mmc_suspend, .resume = sdp_mmc_resume, #endif .slots[0] = { .set_power = sdp_mmc_set_power, .set_bus_mode = NULL, .get_ro = NULL, .get_cover_state = NULL, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195, .name = "first slot", }, }; void __init sdp_mmc_init(void) { omap_set_mmc_info(1, &sdp_mmc_data); } #else void __init sdp_mmc_init(void) { } #endif