Fix for Intel Baytrail-T errata regarding the non-functioning mmc_pwr pin
RFC PATCH 1/1 Fix for Intel Baytrail-T errata regarding the non-functioning mmc_pwr pin
This bug effects Asus T100 as well as other Baytrail-T devices. Erratum in question can be found at
http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/atom-Z36xxx-Z37xxx-spec-update.pdf
---
drivers/mmc/host/sdhci-acpi.c +1 -1
@@ -123,7 +123,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { .quirks =SDHCI_QUIRK_BROKEN_CARD_DETECTION , - .quirks2 =SDHCI_QUIRK2_HOST_OFF_CARD_ON , + .quirks2 =SDHCI_QUIRK2_HOST_OFF_CARD_ON |SDHCI_QUIRK2_BROKEN_POWER_ENABLE , .caps =MMC_CAP_NONREMOVABLE |MMC_CAP_POWER_OFF_CARD , .flags =SDHCI_ACPI_RUNTIME_PM , .pm_caps =MMC_PM_KEEP_POWER ,
drivers/mmc/host/sdhci.c +30 -0
@@ -22,6 +22,7 @@#include <linux/scatterlist.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> +#include <linux/gpio.h> #include <linux/leds.h>
@@ -1261,6 +1262,10 @@ static inline void sdhci_update_clock(struct sdhci_host *host)sdhci_set_clock (host, clock); } +static inline void sdhci_set_power_quirk (unsigned short power) { +gpio_set_value (SDHCI_POWER_QUIRK_GPIO , power); +} +static int sdhci_set_power (struct sdhci_host *host,unsigned short power) {u8 pwr =0 ;
@@ -1292,6 +1297,9 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)sdhci_writeb (host,0 ,SDHCI_POWER_CONTROL );if (host->quirks2 &SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON )sdhci_runtime_pm_bus_off (host); +/* The intel Bay Trail SoCs need to assert a GPIO as a work around */ +if (host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE ) +sdhci_set_power_quirk (0 );return 0 ; }
@@ -1311,8 +1319,14 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) pwr |=SDHCI_POWER_ON ; +sdhci_writeb (host, pwr,SDHCI_POWER_CONTROL ); +/* The intel Bay Trail SoCs need to assert a GPIO as a work around */ +if (host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE ) +sdhci_set_power_quirk (1 ); + +if (host->quirks2 &SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON )sdhci_runtime_pm_bus_on (host);
@@ -2820,6 +2841,18 @@ int sdhci_add_host(struct sdhci_host *host) host->flags &= ~SDHCI_USE_SDMA ; } +if ((host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE )) { +printk ("Using GPIO for power enable as it is marked broken\n" ); +if (gpio_request (SDHCI_POWER_QUIRK_GPIO ,"SDIO_PWR_EN" ) <0 ) { +printk ("Unable to request GPIO. SDIO may be broken." ); + host->quirks2 = host->quirks2 & ~SDHCI_QUIRK2_BROKEN_POWER_ENABLE ; + } +else if (gpio_direction_output (SDHCI_POWER_QUIRK_GPIO ,0 ) <0 ) { +printk ("Unable to set GPIO direction. SDIO may be broken." ); + host->quirks2 = host->quirks2 & ~SDHCI_QUIRK2_BROKEN_POWER_ENABLE ; + } + } +if ((host->version >=SDHCI_SPEC_200 ) && (caps[0 ] &SDHCI_CAN_DO_ADMA2 )) host->flags |=SDHCI_USE_ADMA ;
@@ -3325,6 +3358,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)tasklet_kill (&host->card_tasklet);tasklet_kill (&host->finish_tasklet); +if (host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE ) { +gpio_free (SDHCI_POWER_QUIRK_GPIO ); + } +if (host->vmmc) {regulator_disable (host->vmmc);regulator_put (host->vmmc);
drivers/mmc/host/sdhci.h +1 -0
@@ -84,11 +84,12 @@#define SDHCI_CTRL_ADMA64 0x18#define SDHCI_CTRL_8BITBUS 0x20#define SDHCI_POWER_CONTROL 0x29#define SDHCI_POWER_ON 0x01#define SDHCI_POWER_180 0x0A#define SDHCI_POWER_300 0x0C#define SDHCI_POWER_330 0x0E +#define SDHCI_POWER_QUIRK_GPIO 195/*GPIO offset 41 of SCORE device is SD3_PWR_EN*/ #define SDHCI_BLOCK_GAP_CONTROL 0x2A
include/linux/mmc/sdhci.h +3 -0
@@ -102,6 +102,9 @@ struct sdhci_host {#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)/* Controller does not support DDR50 */ #define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) +/* Controller cannot initialize power (must use GPIO instead) */ +#define SDHCI_QUIRK2_BROKEN_POWER_ENABLE (1<<8) +int irq;/* Device IRQ */ void __iomem *ioaddr;/* Mapped address */
Quoted 32 lines · Jon Pry
This bug effects Asus T100 as well as other Baytrail-T devices. Erratum in question can be found at
http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/atom-Z36xxx-Z37xxx-spec-update.pdfSigned-off-by Jon Pry <jonpry <at> gmail.com>---
drivers/mmc/host/sdhci-acpi.c +1
-1@@ -123,7 +123,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { .quirks =SDHCI_QUIRK_BROKEN_CARD_DETECTION , - .quirks2 =SDHCI_QUIRK2_HOST_OFF_CARD_ON , + .quirks2 =SDHCI_QUIRK2_HOST_OFF_CARD_ON |SDHCI_QUIRK2_BROKEN_POWER_ENABLE , .caps =MMC_CAP_NONREMOVABLE |MMC_CAP_POWER_OFF_CARD , .flags =SDHCI_ACPI_RUNTIME_PM , .pm_caps =MMC_PM_KEEP_POWER ,drivers/mmc/host/sdhci.c +1
-0@@ -22,6 +22,7 @@#include <linux/scatterlist.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> +#include <linux/gpio.h> #include <linux/leds.h>
drivers/mmc/host/sdhci.c static inline void sdhci_update_clock(struct sdhci_host *host) · line 1268
Quoted 7 lines
drivers/mmc/host/sdhci.c +4
-0@@ -1261,6 +1262,10 @@ static inline void sdhci_update_clock(struct sdhci_host *host)sdhci_set_clock (host, clock); } +static inline void sdhci_set_power_quirk (unsigned short power) { +gpio_set_value (SDHCI_POWER_QUIRK_GPIO , power); +} +
I would suggest you to implement this through a gpio regulator instead.
In that way, you likely don't have to change that much in driver, right?
Kind regards
Uffe
Quoted 93 lines
drivers/mmc/host/sdhci.c +25
-0static int sdhci_set_power (struct sdhci_host *host,unsigned short power) {u8 pwr =0 ;@@ -1292,6 +1297,9 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)sdhci_writeb (host,0 ,SDHCI_POWER_CONTROL );if (host->quirks2 &SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON )sdhci_runtime_pm_bus_off (host); +/* The intel Bay Trail SoCs need to assert a GPIO as a work around */ +if (host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE ) +sdhci_set_power_quirk (0 );return 0 ; }@@ -1311,8 +1319,14 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) pwr |=SDHCI_POWER_ON ; +sdhci_writeb (host, pwr,SDHCI_POWER_CONTROL ); +/* The intel Bay Trail SoCs need to assert a GPIO as a work around */ +if (host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE ) +sdhci_set_power_quirk (1 ); + +if (host->quirks2 &SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON )sdhci_runtime_pm_bus_on (host);@@ -2820,6 +2841,18 @@ int sdhci_add_host(struct sdhci_host *host) host->flags &= ~SDHCI_USE_SDMA ; } +if ((host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE )) { +printk ("Using GPIO for power enable as it is marked broken\n" ); +if (gpio_request (SDHCI_POWER_QUIRK_GPIO ,"SDIO_PWR_EN" ) <0 ) { +printk ("Unable to request GPIO. SDIO may be broken." ); + host->quirks2 = host->quirks2 & ~SDHCI_QUIRK2_BROKEN_POWER_ENABLE ; + } +else if (gpio_direction_output (SDHCI_POWER_QUIRK_GPIO ,0 ) <0 ) { +printk ("Unable to set GPIO direction. SDIO may be broken." ); + host->quirks2 = host->quirks2 & ~SDHCI_QUIRK2_BROKEN_POWER_ENABLE ; + } + } +if ((host->version >=SDHCI_SPEC_200 ) && (caps[0 ] &SDHCI_CAN_DO_ADMA2 )) host->flags |=SDHCI_USE_ADMA ;@@ -3325,6 +3358,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)tasklet_kill (&host->card_tasklet);tasklet_kill (&host->finish_tasklet); +if (host->quirks2 &SDHCI_QUIRK2_BROKEN_POWER_ENABLE ) { +gpio_free (SDHCI_POWER_QUIRK_GPIO ); + } +if (host->vmmc) {regulator_disable (host->vmmc);regulator_put (host->vmmc);drivers/mmc/host/sdhci.h +1
-0@@ -84,11 +84,12 @@#define SDHCI_CTRL_ADMA64 0x18#define SDHCI_CTRL_8BITBUS 0x20#define SDHCI_POWER_CONTROL 0x29#define SDHCI_POWER_ON 0x01#define SDHCI_POWER_180 0x0A#define SDHCI_POWER_300 0x0C#define SDHCI_POWER_330 0x0E +#define SDHCI_POWER_QUIRK_GPIO 195/*GPIO offset 41 of SCORE device is SD3_PWR_EN*/ #define SDHCI_BLOCK_GAP_CONTROL 0x2Ainclude/linux/mmc/sdhci.h +3
-1@@ -102,6 +102,9 @@ struct sdhci_host {#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)/* Controller does not support DDR50 */ #define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) +/* Controller cannot initialize power (must use GPIO instead) */ +#define SDHCI_QUIRK2_BROKEN_POWER_ENABLE (1<<8) +int irq;/* Device IRQ */ void __iomem *ioaddr;/* Mapped address */ --To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html