aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-pci.c
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2011-08-29 09:42:13 -0400
committerChris Ball <cjb@laptop.org>2011-10-26 16:32:07 -0400
commit0f2016558e4f9a8d2f9b1202064915205f4dd450 (patch)
tree2b42e80f7de9b018b7c4867d3ae51d8b7981b602 /drivers/mmc/host/sdhci-pci.c
parent20758b66dce76af0527363186f44b464d83e5666 (diff)
mmc: sdhci-pci: add eMMC hardware reset support
Implement eMMC hardware reset for Medfield. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/sdhci-pci.c')
-rw-r--r--drivers/mmc/host/sdhci-pci.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 26c528648f3c..3b30c515cbc2 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -21,6 +21,8 @@
21#include <linux/mmc/host.h> 21#include <linux/mmc/host.h>
22#include <linux/scatterlist.h> 22#include <linux/scatterlist.h>
23#include <linux/io.h> 23#include <linux/io.h>
24#include <linux/gpio.h>
25#include <linux/sfi.h>
24 26
25#include "sdhci.h" 27#include "sdhci.h"
26 28
@@ -59,6 +61,7 @@ struct sdhci_pci_slot {
59 struct sdhci_host *host; 61 struct sdhci_host *host;
60 62
61 int pci_bar; 63 int pci_bar;
64 int rst_n_gpio;
62}; 65};
63 66
64struct sdhci_pci_chip { 67struct sdhci_pci_chip {
@@ -163,12 +166,63 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
163 return 0; 166 return 0;
164} 167}
165 168
169/* Medfield eMMC hardware reset GPIOs */
170static int mfd_emmc0_rst_gpio = -EINVAL;
171static int mfd_emmc1_rst_gpio = -EINVAL;
172
173static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
174{
175 struct sfi_table_simple *sb = (struct sfi_table_simple *)table;
176 struct sfi_gpio_table_entry *entry;
177 int i, num;
178
179 num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
180 entry = (struct sfi_gpio_table_entry *)sb->pentry;
181
182 for (i = 0; i < num; i++, entry++) {
183 if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN))
184 mfd_emmc0_rst_gpio = entry->pin_no;
185 else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN))
186 mfd_emmc1_rst_gpio = entry->pin_no;
187 }
188
189 return 0;
190}
191
166static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) 192static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
167{ 193{
194 const char *name = NULL;
195 int gpio = -EINVAL;
196
197 sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse);
198
199 switch (slot->chip->pdev->device) {
200 case PCI_DEVICE_ID_INTEL_MFD_EMMC0:
201 gpio = mfd_emmc0_rst_gpio;
202 name = "eMMC0_reset";
203 break;
204 case PCI_DEVICE_ID_INTEL_MFD_EMMC1:
205 gpio = mfd_emmc1_rst_gpio;
206 name = "eMMC1_reset";
207 break;
208 }
209
210 if (!gpio_request(gpio, name)) {
211 gpio_direction_output(gpio, 1);
212 slot->rst_n_gpio = gpio;
213 slot->host->mmc->caps |= MMC_CAP_HW_RESET;
214 }
215
168 slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; 216 slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
217
169 return 0; 218 return 0;
170} 219}
171 220
221static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
222{
223 gpio_free(slot->rst_n_gpio);
224}
225
172static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { 226static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
173 .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, 227 .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
174 .probe_slot = mrst_hc_probe_slot, 228 .probe_slot = mrst_hc_probe_slot,
@@ -190,6 +244,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
190static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { 244static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
191 .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 245 .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
192 .probe_slot = mfd_emmc_probe_slot, 246 .probe_slot = mfd_emmc_probe_slot,
247 .remove_slot = mfd_emmc_remove_slot,
193}; 248};
194 249
195/* O2Micro extra registers */ 250/* O2Micro extra registers */
@@ -832,9 +887,25 @@ static int sdhci_pci_8bit_width(struct sdhci_host *host, int width)
832 return 0; 887 return 0;
833} 888}
834 889
890static void sdhci_pci_hw_reset(struct sdhci_host *host)
891{
892 struct sdhci_pci_slot *slot = sdhci_priv(host);
893 int rst_n_gpio = slot->rst_n_gpio;
894
895 if (!gpio_is_valid(rst_n_gpio))
896 return;
897 gpio_set_value_cansleep(rst_n_gpio, 0);
898 /* For eMMC, minimum is 1us but give it 10us for good measure */
899 udelay(10);
900 gpio_set_value_cansleep(rst_n_gpio, 1);
901 /* For eMMC, minimum is 200us but give it 300us for good measure */
902 usleep_range(300, 1000);
903}
904
835static struct sdhci_ops sdhci_pci_ops = { 905static struct sdhci_ops sdhci_pci_ops = {
836 .enable_dma = sdhci_pci_enable_dma, 906 .enable_dma = sdhci_pci_enable_dma,
837 .platform_8bit_width = sdhci_pci_8bit_width, 907 .platform_8bit_width = sdhci_pci_8bit_width,
908 .hw_reset = sdhci_pci_hw_reset,
838}; 909};
839 910
840/*****************************************************************************\ 911/*****************************************************************************\
@@ -988,6 +1059,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
988 slot->chip = chip; 1059 slot->chip = chip;
989 slot->host = host; 1060 slot->host = host;
990 slot->pci_bar = bar; 1061 slot->pci_bar = bar;
1062 slot->rst_n_gpio = -EINVAL;
991 1063
992 host->hw_name = "PCI"; 1064 host->hw_name = "PCI";
993 host->ops = &sdhci_pci_ops; 1065 host->ops = &sdhci_pci_ops;