nyan*: Enable SD card power in initialization.

After a warm reboot, Tegra SD card reader must be re-initialized by cycling its
power GPIO and VDD_SDMMC.  Previous loader (ex, Coreboot) should clear the power
settings, and depthcharge MMC module will set GPIO and VDD starting to access SD
card.

BUG=chrome-os-partner:27053
BRANCH=nyan
TEST=emerge-nyan coreboot depthcharge chromeos-bootimage
     On a Nyan/Norrin DEV mode, insert Sandisk class 10 SD card,
     first boot eMMC system, mount SD card, run "reboot" command,
     press Ctrl-U in DEV screen to boot SD card (success).
     Run "crossystem recovery_request=1" then "reboot" (success).

Change-Id: I40b251d3400cafac810494ba3627142f10de5baa
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/196783
Reviewed-by: Gabe Black <gabeblack@chromium.org>
Tested-by: Andrew Bresticker <abrestic@chromium.org>
diff --git a/src/board/nyan/board.c b/src/board/nyan/board.c
index fba3110..60e54ef 100644
--- a/src/board/nyan/board.c
+++ b/src/board/nyan/board.c
@@ -20,6 +20,7 @@
  * MA 02111-1307 USA
  */
 
+#include <assert.h>
 #include <libpayload.h>
 
 #include "base/init_funcs.h"
@@ -84,6 +85,44 @@
 	CLK_H_I2C5 = 0x1 << 15
 };
 
+typedef struct VirtualMmcPowerGpio
+{
+	GpioOps ops;
+
+	GpioOps *gpio;
+	As3722Pmic *as3722;
+	uint8_t reg, enable_val, disable_val;  // Params for as3722.
+} VirtualMmcPowerGpio;
+
+static int virtual_mmc_power_set(GpioOps *me, unsigned value)
+{
+	VirtualMmcPowerGpio *power = container_of(me, VirtualMmcPowerGpio, ops);
+	assert(power->gpio && power->as3722);
+	if (power->gpio->set(power->gpio, value) ||
+	    power->as3722->set_reg(power->as3722, power->reg, value ?
+				   power->enable_val : power->disable_val)) {
+		printf("Failed to enable SD/MMC power.\n");
+		return -1;
+	}
+	return 0;
+}
+
+static VirtualMmcPowerGpio *new_virtual_mmc_power(GpioOps *gpio,
+						  As3722Pmic *as3722,
+						  uint8_t reg,
+						  uint8_t enable_val,
+						  uint8_t disable_val)
+{
+	VirtualMmcPowerGpio *power = xzalloc(sizeof(*power));
+	power->gpio = gpio;
+	power->as3722 = as3722;
+	power->reg = reg;
+	power->enable_val = enable_val;
+	power->disable_val = disable_val;
+	power->ops.set = &virtual_mmc_power_set;
+	return power;
+}
+
 static int board_setup(void)
 {
 	uint8_t id = board_id();
@@ -155,20 +194,6 @@
 
 	cros_ec_set_bus(&new_cros_ec_spi_bus(&spi1->ops)->ops);
 
-	// sdmmc4
-	TegraMmcHost *emmc = new_tegra_mmc_host(0x700b0600, 8, 0, NULL);
-	// sdmmc3
-	TegraGpio *card_detect = new_tegra_gpio_input(GPIO(V, 2));
-	GpioOps *card_detect_ops = &card_detect->ops;
-	if (id != BOARD_ID_REV0)
-		card_detect_ops = new_gpio_not(card_detect_ops);
-	TegraMmcHost *sd_card = new_tegra_mmc_host(0x700b0400, 4, 1,
-						   card_detect_ops);
-	list_insert_after(&emmc->mmc.ctrlr.list_node,
-			  &fixed_block_dev_controllers);
-	list_insert_after(&sd_card->mmc.ctrlr.list_node,
-			  &removable_block_dev_controllers);
-
 	TegraI2c *pwr_i2c = new_tegra_i2c((void *)0x7000d000, 5,
 					  (void *)CLK_RST_H_RST_SET,
 					  (void *)CLK_RST_H_RST_CLR,
@@ -179,6 +204,25 @@
 						 0);
 	power_set_ops(&power->ops);
 
+	// sdmmc4
+	TegraMmcHost *emmc = new_tegra_mmc_host(0x700b0600, 8, 0, NULL, NULL);
+	// sdmmc3
+	TegraGpio *enable_vdd_sd = new_tegra_gpio_output(GPIO(R, 0));
+	// The params in mmc_power set AS3722_LDO6 to 3.3V.
+	VirtualMmcPowerGpio *mmc_power = new_virtual_mmc_power(
+			&enable_vdd_sd->ops, pmic, 0x16, 0x3F, 0);
+	TegraGpio *card_detect = new_tegra_gpio_input(GPIO(V, 2));
+	GpioOps *card_detect_ops = &card_detect->ops;
+	if (id != BOARD_ID_REV0)
+		card_detect_ops = new_gpio_not(card_detect_ops);
+	TegraMmcHost *sd_card = new_tegra_mmc_host(0x700b0400, 4, 1,
+						   card_detect_ops,
+						   &mmc_power->ops);
+	list_insert_after(&emmc->mmc.ctrlr.list_node,
+			  &fixed_block_dev_controllers);
+	list_insert_after(&sd_card->mmc.ctrlr.list_node,
+			  &removable_block_dev_controllers);
+
 	/* Careful: the EHCI base is at offset 0x100 from the SoC's IP base */
 	UsbHostController *usbd = new_usb_hc(EHCI, 0x7d000100);
 	/* USB2 is connected to the camera, not needed in firmware */
diff --git a/src/board/nyan_big/board.c b/src/board/nyan_big/board.c
index 03b9063..bf6ebca 100644
--- a/src/board/nyan_big/board.c
+++ b/src/board/nyan_big/board.c
@@ -20,6 +20,7 @@
  * MA 02111-1307 USA
  */
 
+#include <assert.h>
 #include <libpayload.h>
 #include <stdlib.h>
 
@@ -97,6 +98,44 @@
 	CLK_H_I2C5 = 0x1 << 15
 };
 
+typedef struct VirtualMmcPowerGpio
+{
+	GpioOps ops;
+
+	GpioOps *gpio;
+	As3722Pmic *as3722;
+	uint8_t reg, enable_val, disable_val;  // Params for as3722.
+} VirtualMmcPowerGpio;
+
+static int virtual_mmc_power_set(GpioOps *me, unsigned value)
+{
+	VirtualMmcPowerGpio *power = container_of(me, VirtualMmcPowerGpio, ops);
+	assert(power->gpio && power->as3722);
+	if (power->gpio->set(power->gpio, value) ||
+	    power->as3722->set_reg(power->as3722, power->reg, value ?
+				   power->enable_val : power->disable_val)) {
+		printf("Failed to enable SD/MMC power.\n");
+		return -1;
+	}
+	return 0;
+}
+
+static VirtualMmcPowerGpio *new_virtual_mmc_power(GpioOps *gpio,
+						  As3722Pmic *as3722,
+						  uint8_t reg,
+						  uint8_t enable_val,
+						  uint8_t disable_val)
+{
+	VirtualMmcPowerGpio *power = xzalloc(sizeof(*power));
+	power->gpio = gpio;
+	power->as3722 = as3722;
+	power->reg = reg;
+	power->enable_val = enable_val;
+	power->disable_val = disable_val;
+	power->ops.set = &virtual_mmc_power_set;
+	return power;
+}
+
 static int board_setup(void)
 {
 	uint8_t id = board_id();
@@ -180,21 +219,6 @@
 
 	cros_ec_set_bus(&new_cros_ec_spi_bus(&spi1->ops)->ops);
 
-	// sdmmc4
-	TegraMmcHost *emmc = new_tegra_mmc_host(0x700b0600, 8, 0, NULL);
-	// sdmmc3
-	TegraGpio *card_detect = new_tegra_gpio_input(GPIO(V, 2));
-	GpioOps *card_detect_ops = &card_detect->ops;
-	// invert SD-card CD polarity
-	card_detect_ops = new_gpio_not(card_detect_ops);
-
-	TegraMmcHost *sd_card = new_tegra_mmc_host(0x700b0400, 4, 1,
-						   card_detect_ops);
-	list_insert_after(&emmc->mmc.ctrlr.list_node,
-			  &fixed_block_dev_controllers);
-	list_insert_after(&sd_card->mmc.ctrlr.list_node,
-			  &removable_block_dev_controllers);
-
 	TegraI2c *pwr_i2c = new_tegra_i2c((void *)0x7000d000, 5,
 					  (void *)CLK_RST_H_RST_SET,
 					  (void *)CLK_RST_H_RST_CLR,
@@ -205,6 +229,26 @@
 						 0);
 	power_set_ops(&power->ops);
 
+	// sdmmc4
+	TegraMmcHost *emmc = new_tegra_mmc_host(0x700b0600, 8, 0, NULL, NULL);
+	// sdmmc3
+	TegraGpio *enable_vdd_sd = new_tegra_gpio_output(GPIO(R, 0));
+	// The params in mmc_power set AS3722_LDO6 to 3.3V.
+	VirtualMmcPowerGpio *mmc_power = new_virtual_mmc_power(
+			&enable_vdd_sd->ops, pmic, 0x16, 0x3F, 0);
+	TegraGpio *card_detect = new_tegra_gpio_input(GPIO(V, 2));
+	GpioOps *card_detect_ops = &card_detect->ops;
+	// invert SD-card CD polarity
+	card_detect_ops = new_gpio_not(card_detect_ops);
+
+	TegraMmcHost *sd_card = new_tegra_mmc_host(0x700b0400, 4, 1,
+						   card_detect_ops,
+						   &mmc_power->ops);
+	list_insert_after(&emmc->mmc.ctrlr.list_node,
+			  &fixed_block_dev_controllers);
+	list_insert_after(&sd_card->mmc.ctrlr.list_node,
+			  &removable_block_dev_controllers);
+
 	/* Careful: the EHCI base is at offset 0x100 from the SoC's IP base */
 	UsbHostController *usbd = new_usb_hc(EHCI, 0x7d000100);
 	/* USB2 is connected to the camera, not needed in firmware */
diff --git a/src/board/nyan_blaze/board.c b/src/board/nyan_blaze/board.c
index 09d4b97..7ff5a36 100644
--- a/src/board/nyan_blaze/board.c
+++ b/src/board/nyan_blaze/board.c
@@ -20,6 +20,7 @@
  * MA 02111-1307 USA
  */
 
+#include <assert.h>
 #include <libpayload.h>
 #include <stdlib.h>
 
@@ -97,6 +98,44 @@
 	CLK_H_I2C5 = 0x1 << 15
 };
 
+typedef struct VirtualMmcPowerGpio
+{
+	GpioOps ops;
+
+	GpioOps *gpio;
+	As3722Pmic *as3722;
+	uint8_t reg, enable_val, disable_val;  // Params for as3722.
+} VirtualMmcPowerGpio;
+
+static int virtual_mmc_power_set(GpioOps *me, unsigned value)
+{
+	VirtualMmcPowerGpio *power = container_of(me, VirtualMmcPowerGpio, ops);
+	assert(power->gpio && power->as3722);
+	if (power->gpio->set(power->gpio, value) ||
+	    power->as3722->set_reg(power->as3722, power->reg, value ?
+				   power->enable_val : power->disable_val)) {
+		printf("Failed to enable SD/MMC power.\n");
+		return -1;
+	}
+	return 0;
+}
+
+static VirtualMmcPowerGpio *new_virtual_mmc_power(GpioOps *gpio,
+						  As3722Pmic *as3722,
+						  uint8_t reg,
+						  uint8_t enable_val,
+						  uint8_t disable_val)
+{
+	VirtualMmcPowerGpio *power = xzalloc(sizeof(*power));
+	power->gpio = gpio;
+	power->as3722 = as3722;
+	power->reg = reg;
+	power->enable_val = enable_val;
+	power->disable_val = disable_val;
+	power->ops.set = &virtual_mmc_power_set;
+	return power;
+}
+
 static int board_setup(void)
 {
 	uint8_t id = board_id();
@@ -180,21 +219,6 @@
 
 	cros_ec_set_bus(&new_cros_ec_spi_bus(&spi1->ops)->ops);
 
-	// sdmmc4
-	TegraMmcHost *emmc = new_tegra_mmc_host(0x700b0600, 8, 0, NULL);
-	// sdmmc3
-	TegraGpio *card_detect = new_tegra_gpio_input(GPIO(V, 2));
-	GpioOps *card_detect_ops = &card_detect->ops;
-	// invert SD-card CD polarity
-	card_detect_ops = new_gpio_not(card_detect_ops);
-
-	TegraMmcHost *sd_card = new_tegra_mmc_host(0x700b0400, 4, 1,
-						   card_detect_ops);
-	list_insert_after(&emmc->mmc.ctrlr.list_node,
-			  &fixed_block_dev_controllers);
-	list_insert_after(&sd_card->mmc.ctrlr.list_node,
-			  &removable_block_dev_controllers);
-
 	TegraI2c *pwr_i2c = new_tegra_i2c((void *)0x7000d000, 5,
 					  (void *)CLK_RST_H_RST_SET,
 					  (void *)CLK_RST_H_RST_CLR,
@@ -205,6 +229,26 @@
 						 0);
 	power_set_ops(&power->ops);
 
+	// sdmmc4
+	TegraMmcHost *emmc = new_tegra_mmc_host(0x700b0600, 8, 0, NULL, NULL);
+	// sdmmc3
+	TegraGpio *enable_vdd_sd = new_tegra_gpio_output(GPIO(R, 0));
+	// The params in mmc_power set AS3722_LDO6 to 3.3V.
+	VirtualMmcPowerGpio *mmc_power = new_virtual_mmc_power(
+			&enable_vdd_sd->ops, pmic, 0x16, 0x3F, 0);
+	TegraGpio *card_detect = new_tegra_gpio_input(GPIO(V, 2));
+	GpioOps *card_detect_ops = &card_detect->ops;
+	// invert SD-card CD polarity
+	card_detect_ops = new_gpio_not(card_detect_ops);
+
+	TegraMmcHost *sd_card = new_tegra_mmc_host(0x700b0400, 4, 1,
+						   card_detect_ops,
+						   &mmc_power->ops);
+	list_insert_after(&emmc->mmc.ctrlr.list_node,
+			  &fixed_block_dev_controllers);
+	list_insert_after(&sd_card->mmc.ctrlr.list_node,
+			  &removable_block_dev_controllers);
+
 	/* Careful: the EHCI base is at offset 0x100 from the SoC's IP base */
 	UsbHostController *usbd = new_usb_hc(EHCI, 0x7d000100);
 	/* USB2 is connected to the camera, not needed in firmware */
diff --git a/src/drivers/storage/tegra_mmc.c b/src/drivers/storage/tegra_mmc.c
index bc4e98b..0ddc82c 100644
--- a/src/drivers/storage/tegra_mmc.c
+++ b/src/drivers/storage/tegra_mmc.c
@@ -486,6 +486,10 @@
 	TegraMmcHost *host = container_of(me, TegraMmcHost, mmc.ctrlr.ops);
 	unsigned int mask;
 	mmc_debug("%s called\n", __func__);
+	if (host->power_gpio) {
+		host->power_gpio->set(host->power_gpio, 1);
+		udelay(2000);
+	}
 
 	tegra_mmc_reset(host);
 
@@ -566,7 +570,8 @@
 }
 
 TegraMmcHost *new_tegra_mmc_host(uintptr_t ioaddr, int bus_width,
-				 int removable, GpioOps *card_detect)
+				 int removable, GpioOps *card_detect,
+				 GpioOps *enable_power)
 {
 	TegraMmcHost *ctrlr = xzalloc(sizeof(*ctrlr));
 
@@ -596,6 +601,7 @@
 	ctrlr->removable = removable;
 
 	ctrlr->cd_gpio = card_detect;
+	ctrlr->power_gpio = enable_power;
 
 	return ctrlr;
 }
diff --git a/src/drivers/storage/tegra_mmc.h b/src/drivers/storage/tegra_mmc.h
index cacbfbb..264318f 100644
--- a/src/drivers/storage/tegra_mmc.h
+++ b/src/drivers/storage/tegra_mmc.h
@@ -153,12 +153,14 @@
 	uint32_t src_hz;	// Source clock (hz)
 
 	GpioOps *cd_gpio;	// Change Detect GPIO
+	GpioOps *power_gpio;	// Enable Power GPIO
 
 	int initialized;
 	int removable;
 } TegraMmcHost;
 
 TegraMmcHost *new_tegra_mmc_host(uintptr_t ioaddr, int bus_width,
-				 int removable,	GpioOps *card_detect);
+				 int removable, GpioOps *card_detect,
+				 GpioOps *enable_power);
 
 #endif // __DRIVERS_STORAGE_TEGRA_MMC_H_