From patchwork Tue May 4 16:01:37 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: kishore kadiyala X-Patchwork-Id: 96820 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o44G2GH5014327 for ; Tue, 4 May 2010 16:02:16 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932436Ab0EDQBw (ORCPT ); Tue, 4 May 2010 12:01:52 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:53354 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932084Ab0EDQBu (ORCPT ); Tue, 4 May 2010 12:01:50 -0400 Received: from dlep36.itg.ti.com ([157.170.170.91]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id o44G1ela022393 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 4 May 2010 11:01:40 -0500 Received: from dbdmail.itg.ti.com (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id o44G1Yl9011437; Tue, 4 May 2010 11:01:35 -0500 (CDT) Received: from 10.24.255.17 (SquirrelMail authenticated user x0099945); by dbdmail.itg.ti.com with HTTP; Tue, 4 May 2010 21:31:37 +0530 (IST) Message-ID: <35156.10.24.255.17.1272988897.squirrel@dbdmail.itg.ti.com> Date: Tue, 4 May 2010 21:31:37 +0530 (IST) Subject: [PATCH v2 3/5] OMAP4-HSMMC: Adding MMC-TWL regulator changes From: "kishore kadiyala" To: linux-mmc@vger.kernel.org, linux-omap@vger.kernel.org Cc: tony@atomide.com, madhu.cr@ti.com, jarkko.lavinen@nokia.com, rmk@arm.linux.org.uk, paul@pwsan.com User-Agent: SquirrelMail/1.4.3a X-Mailer: SquirrelMail/1.4.3a MIME-Version: 1.0 X-Priority: 3 (Normal) Importance: Normal Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 04 May 2010 16:02:22 +0000 (UTC) diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index f5ca16c..a73f011 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ static u16 control_pbias_offset; static u16 control_devconf1_offset; +static u16 control_mmc1; #define HSMMC_NAME_LEN 9 @@ -43,7 +45,7 @@ static int hsmmc_get_context_loss(struct device *dev) #define hsmmc_get_context_loss NULL #endif -static void hsmmc1_before_set_reg(struct device *dev, int slot, +static void omap_hsmmc1_before_set_reg(struct device *dev, int slot, int power_on, int vdd) { u32 reg, prog_io; @@ -96,7 +98,7 @@ static void hsmmc1_before_set_reg(struct device *dev, int slot, } } -static void hsmmc1_after_set_reg(struct device *dev, int slot, +static void omap_hsmmc1_after_set_reg(struct device *dev, int slot, int power_on, int vdd) { u32 reg; @@ -120,6 +122,61 @@ static void hsmmc1_after_set_reg(struct device *dev, int slot, } } +static void omap4_hsmmc1_before_set_reg(struct device *dev, int slot, + int power_on, int vdd) +{ + u32 reg; + + /* + * Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the + * card with Vcc regulator (from twl4030 or whatever). OMAP has both + * 1.8V and 3.0V modes, controlled by the PBIAS register. + * + * In 8-bit modes, OMAP VMMC1A (for DAT4..7) needs a supply, which + * is most naturally TWL VSIM; those pins also use PBIAS. + * + * FIXME handle VMMC1A as needed ... + */ + if (power_on) { + reg = omap_ctrl_readl(control_pbias_offset); + reg &= ~(OMAP4_MMC1_PBIASLITE_PWRDNZ | OMAP4_MMC1_PWRDNZ); + omap_ctrl_writel(reg, control_pbias_offset); + } else { + reg = omap_ctrl_readl(control_pbias_offset); + reg &= ~(OMAP4_MMC1_PBIASLITE_PWRDNZ | OMAP4_MMC1_PWRDNZ); + omap_ctrl_writel(reg, control_pbias_offset); + } +} + +static void omap4_hsmmc1_after_set_reg(struct device *dev, int slot, + int power_on, int vdd) +{ + u32 reg; + + /* 100ms delay required for PBIAS configuration */ + msleep(100); + + if (power_on) { + reg = omap_ctrl_readl(control_pbias_offset); + reg |= OMAP4_MMC1_PBIASLITE_PWRDNZ; + if ((1 << vdd) <= MMC_VDD_165_195) { + reg &= ~OMAP4_MMC1_PBIASLITE_VMODE; + reg |= (OMAP4_MMC1_PBIASLITE_PWRDNZ | + OMAP4_MMC1_PWRDNZ); + } else { + reg |= (OMAP4_MMC1_PBIASLITE_VMODE | + OMAP4_MMC1_PBIASLITE_PWRDNZ | + OMAP4_MMC1_PWRDNZ); + } + omap_ctrl_writel(reg, control_pbias_offset); + } else { + reg = omap_ctrl_readl(control_pbias_offset); + reg |= (OMAP4_MMC1_PBIASLITE_PWRDNZ | + OMAP4_MMC1_PBIASLITE_VMODE | OMAP4_MMC1_PWRDNZ); + omap_ctrl_writel(reg, control_pbias_offset); + } +} + static void hsmmc23_before_set_reg(struct device *dev, int slot, int power_on, int vdd) { @@ -140,6 +197,24 @@ static void hsmmc23_before_set_reg(struct device *dev, int slot, } } +static int mmc_twl_late_init(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + + if (cpu_is_omap44xx()) { + /* MMC1 Card detect Configuration */ + if (pdev->id == 0) { + ret = omap4_hsmmc1_card_detect_config(); + if (ret < 0) + pr_err("Unable to configure" + "Card detect for MMC1\n"); + } + } + return ret; +} + static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata; void __init omap2_hsmmc_init(struct omap2_hsmmc_info *controllers) @@ -147,13 +222,28 @@ void __init omap2_hsmmc_init(struct omap2_hsmmc_info struct omap2_hsmmc_info *c; int nr_hsmmc = ARRAY_SIZE(hsmmc_data); int i; + u32 reg; - if (cpu_is_omap2430()) { - control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE; - control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1; + if (!cpu_is_omap44xx()) { + if (cpu_is_omap2430()) { + control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE; + control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1; + } else { + control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE; + control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1; + } } else { - control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE; - control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1; + control_pbias_offset = OMAP44XX_CONTROL_PBIAS_LITE; + control_mmc1 = OMAP44XX_CONTROL_MMC1; + reg = omap_ctrl_readl(control_mmc1); + reg |= (OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP0 | + OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP1); + reg &= ~(OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP2 | + OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP3); + reg |= (OMAP4_CONTROL_SDMMC1_DR0_SPEEDCTRL | + OMAP4_CONTROL_SDMMC1_DR1_SPEEDCTRL | + OMAP4_CONTROL_SDMMC1_DR2_SPEEDCTRL); + omap_ctrl_writel(reg, control_mmc1); } for (c = controllers; c->mmc; c++) { @@ -186,6 +276,7 @@ void __init omap2_hsmmc_init(struct omap2_hsmmc_info *controllers) mmc->slots[0].wires = c->wires; mmc->slots[0].internal_clock = !c->ext_clock; mmc->dma_mask = 0xffffffff; + mmc->init = mmc_twl_late_init; mmc->get_context_loss_count = hsmmc_get_context_loss; @@ -220,10 +311,18 @@ void __init omap2_hsmmc_init(struct omap2_hsmmc_info switch (c->mmc) { case 1: - /* on-chip level shifting via PBIAS0/PBIAS1 */ - mmc->slots[0].before_set_reg = hsmmc1_before_set_reg; - mmc->slots[0].after_set_reg = hsmmc1_after_set_reg; - + if (cpu_is_omap44xx()) { + /* on-chip level shifting via PBIAS0/PBIAS1 */ + mmc->slots[0].before_set_reg = + omap4_hsmmc1_before_set_reg; + mmc->slots[0].after_set_reg = + omap4_hsmmc1_after_set_reg; + } else { + mmc->slots[0].before_set_reg = + omap_hsmmc1_before_set_reg; + mmc->slots[0].after_set_reg = + omap_hsmmc1_after_set_reg; + } /* Omap3630 HSMMC1 supports only 4-bit */ if (cpu_is_omap3630() && c->wires > 4) { c->wires = 4; diff --git a/arch/arm/plat-omap/include/plat/control.h b/arch/arm/plat-omap/include/plat/control.h index a56deee..6d17a61 100644 --- a/arch/arm/plat-omap/include/plat/control.h +++ b/arch/arm/plat-omap/include/plat/control.h @@ -207,6 +207,9 @@ /* 44xx control status register offset */ #define OMAP44XX_CONTROL_STATUS 0x2c4 +/* 44xx-only CONTROL_GENERAL register offsets */ +#define OMAP44XX_CONTROL_MMC1 0x628 +#define OMAP44XX_CONTROL_PBIAS_LITE 0x600 /* * REVISIT: This list of registers is not comprehensive - there are more * that should be added. @@ -252,6 +255,20 @@ #define OMAP2_PBIASLITEPWRDNZ0 (1 << 1) #define OMAP2_PBIASLITEVMODE0 (1 << 0) +/* CONTROL_PBIAS_LITE bits for OMAP4 */ +#define OMAP4_MMC1_PWRDNZ (1 << 26) +#define OMAP4_MMC1_PBIASLITE_HIZ_MODE (1 << 25) +#define OMAP4_MMC1_PBIASLITE_PWRDNZ (1 << 22) +#define OMAP4_MMC1_PBIASLITE_VMODE (1 << 21) + +#define OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP0 (1 << 31) +#define OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP1 (1 << 30) +#define OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP2 (1 << 29) +#define OMAP4_CONTROL_SDMMC1_PUSTRENGTHGRP3 (1 << 28) +#define OMAP4_CONTROL_SDMMC1_DR0_SPEEDCTRL (1 << 27) +#define OMAP4_CONTROL_SDMMC1_DR1_SPEEDCTRL (1 << 26) +#define OMAP4_CONTROL_SDMMC1_DR2_SPEEDCTRL (1 << 25) + /* CONTROL_PROG_IO1 bits */ #define OMAP3630_PRG_SDMMC1_SPEEDCTRL (1 << 20) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index eb198db..423a355 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -142,6 +142,13 @@ #define TWL6030_CHARGER_FAULT_INT_MASK 0x60 #define TWL6030_MMCCTRL 0xEE +#define VMMC_AUTO_OFF (0x1 << 3) +#define SW_FC (0x1 << 2) + +#define TWL6030_CFG_INPUT_PUPD3 0xF2 +#define MMC_PU (0x1 << 3) +#define MMC_PD (0x1 << 2) + #define TWL4030_CLASS_ID 0x4030 #define TWL6030_CLASS_ID 0x6030 @@ -174,11 +181,40 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned int twl6030_interrupt_unmask(u8 bit_mask, u8 offset); int twl6030_interrupt_mask(u8 bit_mask, u8 offset); -/* - * MMC1 Controller on OMAP4 uses Phoenix Irq for Card detect. - */ +/* MMC1 Controller on OMAP4 uses Phoenix Irq for Card detect */ int twl6030_mmc_card_detect(struct device *dev, int slot); +/* Configuring Card Detect for MMC1 */ +static inline int omap4_hsmmc1_card_detect_config(void) +{ + int res = -1; + u8 reg_val = 0; + + /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */ + if (twl_class_is_6030()) { + twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, + REG_INT_MSK_LINE_B); + twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, + REG_INT_MSK_STS_B); + } + + /* + * Intially Configuring MMC_CTRL for receving interrupts & + * Card status on TWL6030 for MMC1 + */ + reg_val |= (SW_FC & ~VMMC_AUTO_OFF); + twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL); + res = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, + TWL6030_CFG_INPUT_PUPD3); + if (res < 0) + return -EINVAL; + reg_val = 0; + reg_val &= ~(MMC_PU | MMC_PD); + twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, + TWL6030_CFG_INPUT_PUPD3); + return 0; +} + /*----------------------------------------------------------------------*/ /*