Patchwork [U-Boot,v2,3/6] Tegra30: MMC: Add SD bus power-rail and SDMMC pad init routines

login
register
mail settings
Submitter Tom Warren
Date March 5, 2013, 4:29 p.m.
Message ID <1362500985-13196-4-git-send-email-twarren@nvidia.com>
Download mbox | patch
Permalink /patch/225080/
State Superseded
Delegated to: Tom Warren
Headers show

Comments

Tom Warren - March 5, 2013, 4:29 p.m.
T30 requires specific SDMMC pad programming, and bus power-rail bringup.

Signed-off-by: Tom Warren <twarren@nvidia.com>
---
v2:
- rewrite pad_init_mmc to use mmc_id instead of SDMMC base address
- add PMU-specific comments to board_sdmmc_voltage_init sequence

 board/nvidia/cardhu/cardhu.c |   51 ++++++++++++++++++++++++++++++++++++++
 board/nvidia/common/board.c  |   55 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 105 insertions(+), 1 deletions(-)
Stephen Warren - March 5, 2013, 5:55 p.m.
On 03/05/2013 09:29 AM, Tom Warren wrote:
> T30 requires specific SDMMC pad programming, and bus power-rail bringup.

> diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c

> +void pad_init_mmc(struct mmc_host *host)

> +	if (id == PERIPH_ID_SDMMC1) {
> +		val = readl(&gpc->sdio1cfg);
> +		val &= padmask;
> +		val |= padcfg;
> +		writel(val, &gpc->sdio1cfg);
> +		debug(" wrote 0x%08X to %p\n", val, &gpc->sdio1cfg);
> +	} else {
> +		val = readl(&gpc->sdio3cfg);
> +		val &= padmask;
> +		val |= padcfg;
> +		writel(val, &gpc->sdio3cfg);
> +		debug(" wrote 0x%08X to %p\n", val, &gpc->sdio3cfg);
> +	}

This isn't generic enough, although the problems may not show up for the
SDMMC1/3 controllers since it looks like those controllers always use a
specific set of pins; the pinmux HW doesn't allow those controllers to
be routed to different places.

However, SDMMC1 and SDMMC4 would also need entries in the above if
statement for it to be complete. Those controllers can definitely be
switched between different sets of pins. This function would then
somehow have to know which set of pins to apply the pinmux configuration
to, which would imply searching through the pinmux registers to find the
pins which have their mux function set to point at the controller in
question.

This implies that configuring the pinmux here isn't the right way to do
this. As I wrote in my immediately previous email, I think the pingroup
drive registers (named "*cfg") should be initialized based on a table
provided by the board file (and later by DT; the values are already part
of the DT pinctrl bindings). The table or DT content will be
board-specific, and hence able to describe which pingroup should be
configured based on the board design.
Tom Warren - March 5, 2013, 6:23 p.m.
On Tue, Mar 5, 2013 at 10:55 AM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 03/05/2013 09:29 AM, Tom Warren wrote:
>> T30 requires specific SDMMC pad programming, and bus power-rail bringup.
>
>> diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
>
>> +void pad_init_mmc(struct mmc_host *host)
>
>> +     if (id == PERIPH_ID_SDMMC1) {
>> +             val = readl(&gpc->sdio1cfg);
>> +             val &= padmask;
>> +             val |= padcfg;
>> +             writel(val, &gpc->sdio1cfg);
>> +             debug(" wrote 0x%08X to %p\n", val, &gpc->sdio1cfg);
>> +     } else {
>> +             val = readl(&gpc->sdio3cfg);
>> +             val &= padmask;
>> +             val |= padcfg;
>> +             writel(val, &gpc->sdio3cfg);
>> +             debug(" wrote 0x%08X to %p\n", val, &gpc->sdio3cfg);
>> +     }
>
> This isn't generic enough, although the problems may not show up for the
> SDMMC1/3 controllers since it looks like those controllers always use a
> specific set of pins; the pinmux HW doesn't allow those controllers to
> be routed to different places.
>
> However, SDMMC1 and SDMMC4 would also need entries in the above if
SDMMC1 is already there. I assume you mean SDMMC2.
> statement for it to be complete. Those controllers can definitely be
> switched between different sets of pins. This function would then
> somehow have to know which set of pins to apply the pinmux configuration
> to, which would imply searching through the pinmux registers to find the
> pins which have their mux function set to point at the controller in
> question.
Read the SDMMC section of the T30 TRM (24.6.1.2 - 5), and you'll see
that there is no config info given for SDMMC2 or SDMMC4 GP regs.
SDIO1CFG covers SDMMC1, SDIO3CFG covers SDMMC3 thru DAT3, and SDIO2CFG
covers the other 4 DAT pins, which are unused for SDIO on Cardhu.  So
there will never be a need to write to any GP SDIOxCFG registers for
SDMMC2 or SDMMC4 on Tegra30, at least as per this section of the TRM.
Note that it seems SDMMC2 doesn't even have a 'cfg' pingroup register
called out in Table 29, and SDMMC4 is split between 3 different
gm[abcd]cfg groups.  So the above code covers all SDIO pad cfg
settings as per the TRM.

>
> This implies that configuring the pinmux here isn't the right way to do
> this.
Disagree, see above.

> As I wrote in my immediately previous email, I think the pingroup
> drive registers (named "*cfg") should be initialized based on a table
> provided by the board file (and later by DT; the values are already part
> of the DT pinctrl bindings). The table or DT content will be
> board-specific, and hence able to describe which pingroup should be
> configured based on the board design.
That's certainly doable, and probably a good idea if there were any
other code that requires pad cfg to be tuned, but I don't see that in
the TRM anywhere. This patchset is specifically to enable MMC on
Tegra30. Adding pad_cfg_ctrl tables/macros, and code to handle them is
something I see happening in a separate patchset, if at all, since I
don't see any other pad cfg registers that ever need writing (beyond
SDMMC1/3).
Stephen Warren - March 5, 2013, 7:10 p.m.
On 03/05/2013 11:23 AM, Tom Warren wrote:
> On Tue, Mar 5, 2013 at 10:55 AM, Stephen Warren <swarren@wwwdotorg.org> wrote:
>> On 03/05/2013 09:29 AM, Tom Warren wrote:
>>> T30 requires specific SDMMC pad programming, and bus power-rail bringup.
>>
>>> diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
>>
>>> +void pad_init_mmc(struct mmc_host *host)
>>
>>> +     if (id == PERIPH_ID_SDMMC1) {
>>> +             val = readl(&gpc->sdio1cfg);
>>> +             val &= padmask;
>>> +             val |= padcfg;
>>> +             writel(val, &gpc->sdio1cfg);
>>> +             debug(" wrote 0x%08X to %p\n", val, &gpc->sdio1cfg);
>>> +     } else {
>>> +             val = readl(&gpc->sdio3cfg);
>>> +             val &= padmask;
>>> +             val |= padcfg;
>>> +             writel(val, &gpc->sdio3cfg);
>>> +             debug(" wrote 0x%08X to %p\n", val, &gpc->sdio3cfg);
>>> +     }
>>
>> This isn't generic enough, although the problems may not show up for the
>> SDMMC1/3 controllers since it looks like those controllers always use a
>> specific set of pins; the pinmux HW doesn't allow those controllers to
>> be routed to different places.
>>
>> However, SDMMC1 and SDMMC4 would also need entries in the above if
>
> SDMMC1 is already there. I assume you mean SDMMC2.

Yes.

>> statement for it to be complete. Those controllers can definitely be
>> switched between different sets of pins. This function would then
>> somehow have to know which set of pins to apply the pinmux configuration
>> to, which would imply searching through the pinmux registers to find the
>> pins which have their mux function set to point at the controller in
>> question.
>
> Read the SDMMC section of the T30 TRM (24.6.1.2 - 5), and you'll see
> that there is no config info given for SDMMC2 or SDMMC4 GP regs.

The SDMMC section doesn't mandate a specific configuration. However,
almost all pins that are affected by the pinmux are included in some
group that has a "cfg" register with those exact same set of fields.
Hence, it is almost always possible to set some value in those fields.
We just didn't document what those values were for SDMMC2 and SDMMC4.

> SDIO1CFG covers SDMMC1, SDIO3CFG covers SDMMC3 thru DAT3, and SDIO2CFG
> covers the other 4 DAT pins, which are unused for SDIO on Cardhu.  So
> there will never be a need to write to any GP SDIOxCFG registers for
> SDMMC2 or SDMMC4 on Tegra30, at least as per this section of the TRM.

That doesn't follow.

First off, Cardhu is not the only Tegra30 board, so you can't generalize
based on which SDMMC controllers are used on Cardhu.

Second, Cardhu does actually use all of SDMMC1, SDMMC2, SDMMC4; the
other controller is for SDIO for WiFi. Admittedly it's fairly unlikely
we'll support SDIO-based WiFi in U-Boot, but we likely do want to at
least not preclude support for that due to the way the pinmux register
logic is written. Besides, we might also want to assume that U-Boot sets
up 100% of the pinmux registers; that's certainly the approach already
taken for the mux registers.

> Note that it seems SDMMC2 doesn't even have a 'cfg' pingroup register
> called out in Table 29, and SDMMC4 is split between 3 different
> gm[abcd]cfg groups.  So the above code covers all SDIO pad cfg
> settings as per the TRM.

That's exactly my point. The pinmux can route the signals from the other
two SDMMC controllers to different sets of pins. Those different sets of
pins are in different pin groups for the "cfg" registers. Hence, if you
extend this code in question to support all SDMMC controllers, as we
will almost certainly have to do for complete board support, we can't
hard-code what "cfg" registers we choose to write to here, since the
register is board-specific due to the pinmux configuration being
board-specific.

>> As I wrote in my immediately previous email, I think the pingroup
>> drive registers (named "*cfg") should be initialized based on a table
>> provided by the board file (and later by DT; the values are already part
>> of the DT pinctrl bindings). The table or DT content will be
>> board-specific, and hence able to describe which pingroup should be
>> configured based on the board design.
>
> That's certainly doable, and probably a good idea if there were any
> other code that requires pad cfg to be tuned, but I don't see that in
> the TRM anywhere.

The TRM is notoriously incomplete.

> This patchset is specifically to enable MMC on
> Tegra30. Adding pad_cfg_ctrl tables/macros, and code to handle them is
> something I see happening in a separate patchset, if at all, since I
> don't see any other pad cfg registers that ever need writing (beyond
> SDMMC1/3).

Again, the "cfg" registers are part of the pinmux HW, not MMC. If
there's a need to write to them, the pinmux driver needs to do it. We
shouldn't have the MMC driver randomly trample on registers owned by
other code.

Patch

diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c
index df4cb6b..1889998 100644
--- a/board/nvidia/cardhu/cardhu.c
+++ b/board/nvidia/cardhu/cardhu.c
@@ -24,6 +24,10 @@ 
 #include <common.h>
 #include <asm/arch/pinmux.h>
 #include "pinmux-config-cardhu.h"
+#include <i2c.h>
+
+#define PMU_I2C_ADDRESS		0x2D
+#define MAX_I2C_RETRY		3
 
 /*
  * Routine: pinmux_init
@@ -37,3 +41,50 @@  void pinmux_init(void)
 	pinmux_config_table(unused_pins_lowpower,
 		ARRAY_SIZE(unused_pins_lowpower));
 }
+
+#if defined(CONFIG_TEGRA_MMC)
+/*
+ * Do I2C/PMU writes to bring up SD card bus power
+ *
+ */
+void board_sdmmc_voltage_init(void)
+{
+	uchar reg, data_buffer[1];
+	int i;
+
+	i2c_set_bus_num(0);	/* PMU is on bus 0 */
+
+	/* TPS659110: LDO5_REG = 3.3v, ACTIVE to SDMMC1 */
+	data_buffer[0] = 0x65;
+	reg = 0x32;
+
+	for (i = 0; i < MAX_I2C_RETRY; ++i) {
+		if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1))
+			udelay(100);
+	}
+
+	/* TPS659110: GPIO7_REG = PDEN, output a 1 to EN_3V3_SYS */
+	data_buffer[0] = 0x09;
+	reg = 0x67;
+
+	for (i = 0; i < MAX_I2C_RETRY; ++i) {
+		if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1))
+			udelay(100);
+	}
+}
+
+/*
+ * Routine: pin_mux_mmc
+ * Description: setup the MMC muxes, power rails, etc.
+ */
+void pin_mux_mmc(void)
+{
+	/*
+	 * NOTE: We don't do mmc-specific pin muxes here.
+	 * They were done globally in pinmux_init().
+	 */
+
+	/* Bring up the SDIO1 power rail */
+	board_sdmmc_voltage_init();
+}
+#endif	/* MMC */
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index babbe08..8c9040d 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -49,6 +49,8 @@ 
 #include <asm/arch-tegra/usb.h>
 #endif
 #ifdef CONFIG_TEGRA_MMC
+#include <asm/arch/gp_padctrl.h>
+#include <asm/arch-tegra/tegra_mmc.h>
 #include <asm/arch-tegra/mmc.h>
 #endif
 #include <i2c.h>
@@ -245,4 +247,55 @@  int board_mmc_init(bd_t *bd)
 
 	return 0;
 }
-#endif
+
+void pad_init_mmc(struct mmc_host *host)
+{
+#if defined(CONFIG_TEGRA30)
+	struct apb_misc_gp_ctlr *const gpc =
+		(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE;
+	enum periph_id id;
+	u32 val, padcfg, padmask;
+
+	id = host->mmc_id;
+
+	debug("%s: sdmmc address = %08x, id = %d\n", __func__,
+		(unsigned int)host->reg, id);
+
+	/* Set the pad drive strength for SDMMC1 or 3 only */
+	if (id != PERIPH_ID_SDMMC1 && id != PERIPH_ID_SDMMC3) {
+		debug("%s: settings are only valid for SDMMC1/SDMMC3!\n",
+			__func__);
+		return;
+	}
+
+	/* Set pads as per T30 TRM, section 24.6.1.2 */
+	padcfg = (GP_SDIOCFG_DRVUP_SLWF | GP_SDIOCFG_DRVDN_SLWR | \
+		GP_SDIOCFG_DRVUP | GP_SDIOCFG_DRVDN);
+	padmask = 0x00000FFF;
+
+	if (id == PERIPH_ID_SDMMC1) {
+		val = readl(&gpc->sdio1cfg);
+		val &= padmask;
+		val |= padcfg;
+		writel(val, &gpc->sdio1cfg);
+		debug(" wrote 0x%08X to %p\n", val, &gpc->sdio1cfg);
+	} else {
+		val = readl(&gpc->sdio3cfg);
+		val &= padmask;
+		val |= padcfg;
+		writel(val, &gpc->sdio3cfg);
+		debug(" wrote 0x%08X to %p\n", val, &gpc->sdio3cfg);
+	}
+
+	val = readl(&host->reg->sdmemcmppadctl);
+	val &= 0xFFFFFFF0;
+	val |= MEMCOMP_PADCTRL_VREF;
+	writel(val, &host->reg->sdmemcmppadctl);
+
+	val = readl(&host->reg->autocalcfg);
+	val &= 0xFFFF0000;
+	val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED;
+	writel(val, &host->reg->autocalcfg);
+#endif	/* T30 */
+}
+#endif	/* MMC */