| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1 | /*  linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface | 
 | 2 |  * | 
 | 3 |  *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. | 
 | 4 |  * | 
 | 5 |  * This program is free software; you can redistribute it and/or modify | 
 | 6 |  * it under the terms of the GNU General Public License as published by | 
 | 7 |  * the Free Software Foundation; either version 2 of the License, or (at | 
 | 8 |  * your option) any later version. | 
 | 9 |  * | 
 | 10 |  * Thanks to the following companies for their support: | 
 | 11 |  * | 
 | 12 |  *     - JMicron (hardware and technical support) | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | #include <linux/delay.h> | 
 | 16 | #include <linux/highmem.h> | 
| Paul Gortmaker | 88b4767 | 2011-07-03 15:15:51 -0400 | [diff] [blame] | 17 | #include <linux/module.h> | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 18 | #include <linux/pci.h> | 
 | 19 | #include <linux/dma-mapping.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 20 | #include <linux/slab.h> | 
| Maxim Levitsky | ccc92c2 | 2010-08-10 18:01:42 -0700 | [diff] [blame] | 21 | #include <linux/device.h> | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 22 | #include <linux/mmc/host.h> | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 23 | #include <linux/scatterlist.h> | 
 | 24 | #include <linux/io.h> | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 25 | #include <linux/gpio.h> | 
 | 26 | #include <linux/sfi.h> | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 27 | #include <linux/pm_runtime.h> | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 28 |  | 
 | 29 | #include "sdhci.h" | 
 | 30 |  | 
 | 31 | /* | 
 | 32 |  * PCI registers | 
 | 33 |  */ | 
 | 34 |  | 
 | 35 | #define PCI_SDHCI_IFPIO			0x00 | 
 | 36 | #define PCI_SDHCI_IFDMA			0x01 | 
 | 37 | #define PCI_SDHCI_IFVENDOR		0x02 | 
 | 38 |  | 
 | 39 | #define PCI_SLOT_INFO			0x40	/* 8 bits */ | 
 | 40 | #define  PCI_SLOT_INFO_SLOTS(x)		((x >> 4) & 7) | 
 | 41 | #define  PCI_SLOT_INFO_FIRST_BAR_MASK	0x07 | 
 | 42 |  | 
 | 43 | #define MAX_SLOTS			8 | 
 | 44 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 45 | struct sdhci_pci_chip; | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 46 | struct sdhci_pci_slot; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 47 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 48 | struct sdhci_pci_fixes { | 
 | 49 | 	unsigned int		quirks; | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 50 | 	bool			allow_runtime_pm; | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 51 |  | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 52 | 	int			(*probe) (struct sdhci_pci_chip *); | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 53 |  | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 54 | 	int			(*probe_slot) (struct sdhci_pci_slot *); | 
 | 55 | 	void			(*remove_slot) (struct sdhci_pci_slot *, int); | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 56 |  | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 57 | 	int			(*suspend) (struct sdhci_pci_chip *); | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 58 | 	int			(*resume) (struct sdhci_pci_chip *); | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 59 | }; | 
 | 60 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 61 | struct sdhci_pci_slot { | 
 | 62 | 	struct sdhci_pci_chip	*chip; | 
 | 63 | 	struct sdhci_host	*host; | 
 | 64 |  | 
 | 65 | 	int			pci_bar; | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 66 | 	int			rst_n_gpio; | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 67 | 	int			cd_gpio; | 
 | 68 | 	int			cd_irq; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 69 | }; | 
 | 70 |  | 
 | 71 | struct sdhci_pci_chip { | 
 | 72 | 	struct pci_dev		*pdev; | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 73 |  | 
 | 74 | 	unsigned int		quirks; | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 75 | 	bool			allow_runtime_pm; | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 76 | 	const struct sdhci_pci_fixes *fixes; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 77 |  | 
 | 78 | 	int			num_slots;	/* Slots on controller */ | 
 | 79 | 	struct sdhci_pci_slot	*slots[MAX_SLOTS]; /* Pointers to host slots */ | 
 | 80 | }; | 
 | 81 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 82 |  | 
 | 83 | /*****************************************************************************\ | 
 | 84 |  *                                                                           * | 
 | 85 |  * Hardware specific quirk handling                                          * | 
 | 86 |  *                                                                           * | 
 | 87 | \*****************************************************************************/ | 
 | 88 |  | 
 | 89 | static int ricoh_probe(struct sdhci_pci_chip *chip) | 
 | 90 | { | 
| Chris Ball | c99436f | 2009-09-22 16:45:22 -0700 | [diff] [blame] | 91 | 	if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || | 
 | 92 | 	    chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 93 | 		chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; | 
| Maxim Levitsky | ccc92c2 | 2010-08-10 18:01:42 -0700 | [diff] [blame] | 94 | 	return 0; | 
 | 95 | } | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 96 |  | 
| Maxim Levitsky | ccc92c2 | 2010-08-10 18:01:42 -0700 | [diff] [blame] | 97 | static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) | 
 | 98 | { | 
 | 99 | 	slot->host->caps = | 
 | 100 | 		((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) | 
 | 101 | 			& SDHCI_TIMEOUT_CLK_MASK) | | 
 | 102 |  | 
 | 103 | 		((0x21 << SDHCI_CLOCK_BASE_SHIFT) | 
 | 104 | 			& SDHCI_CLOCK_BASE_MASK) | | 
 | 105 |  | 
 | 106 | 		SDHCI_TIMEOUT_CLK_UNIT | | 
 | 107 | 		SDHCI_CAN_VDD_330 | | 
 | 108 | 		SDHCI_CAN_DO_SDMA; | 
 | 109 | 	return 0; | 
 | 110 | } | 
 | 111 |  | 
 | 112 | static int ricoh_mmc_resume(struct sdhci_pci_chip *chip) | 
 | 113 | { | 
 | 114 | 	/* Apply a delay to allow controller to settle */ | 
 | 115 | 	/* Otherwise it becomes confused if card state changed | 
 | 116 | 		during suspend */ | 
 | 117 | 	msleep(500); | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 118 | 	return 0; | 
 | 119 | } | 
 | 120 |  | 
 | 121 | static const struct sdhci_pci_fixes sdhci_ricoh = { | 
 | 122 | 	.probe		= ricoh_probe, | 
| Vasily Khoruzhick | 8493829 | 2010-03-05 13:43:46 -0800 | [diff] [blame] | 123 | 	.quirks		= SDHCI_QUIRK_32BIT_DMA_ADDR | | 
 | 124 | 			  SDHCI_QUIRK_FORCE_DMA | | 
 | 125 | 			  SDHCI_QUIRK_CLOCK_BEFORE_RESET, | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 126 | }; | 
 | 127 |  | 
| Maxim Levitsky | ccc92c2 | 2010-08-10 18:01:42 -0700 | [diff] [blame] | 128 | static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { | 
 | 129 | 	.probe_slot	= ricoh_mmc_probe_slot, | 
 | 130 | 	.resume		= ricoh_mmc_resume, | 
 | 131 | 	.quirks		= SDHCI_QUIRK_32BIT_DMA_ADDR | | 
 | 132 | 			  SDHCI_QUIRK_CLOCK_BEFORE_RESET | | 
 | 133 | 			  SDHCI_QUIRK_NO_CARD_NO_RESET | | 
 | 134 | 			  SDHCI_QUIRK_MISSING_CAPS | 
 | 135 | }; | 
 | 136 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 137 | static const struct sdhci_pci_fixes sdhci_ene_712 = { | 
 | 138 | 	.quirks		= SDHCI_QUIRK_SINGLE_POWER_WRITE | | 
 | 139 | 			  SDHCI_QUIRK_BROKEN_DMA, | 
 | 140 | }; | 
 | 141 |  | 
 | 142 | static const struct sdhci_pci_fixes sdhci_ene_714 = { | 
 | 143 | 	.quirks		= SDHCI_QUIRK_SINGLE_POWER_WRITE | | 
 | 144 | 			  SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | | 
 | 145 | 			  SDHCI_QUIRK_BROKEN_DMA, | 
 | 146 | }; | 
 | 147 |  | 
 | 148 | static const struct sdhci_pci_fixes sdhci_cafe = { | 
 | 149 | 	.quirks		= SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | | 
| Andres Salomon | a087489 | 2009-03-02 21:48:20 +0100 | [diff] [blame] | 150 | 			  SDHCI_QUIRK_NO_BUSY_IRQ | | 
| Pierre Ossman | ee53ab5 | 2008-07-05 00:25:15 +0200 | [diff] [blame] | 151 | 			  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 152 | }; | 
 | 153 |  | 
| Major Lee | 68077b0 | 2011-06-29 14:23:46 +0300 | [diff] [blame] | 154 | static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot) | 
 | 155 | { | 
 | 156 | 	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; | 
 | 157 | 	return 0; | 
 | 158 | } | 
 | 159 |  | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 160 | /* | 
 | 161 |  * ADMA operation is disabled for Moorestown platform due to | 
 | 162 |  * hardware bugs. | 
 | 163 |  */ | 
| Jacob Pan | 35ac6f0 | 2010-11-09 13:57:29 +0000 | [diff] [blame] | 164 | static int mrst_hc_probe(struct sdhci_pci_chip *chip) | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 165 | { | 
 | 166 | 	/* | 
| Jacob Pan | 35ac6f0 | 2010-11-09 13:57:29 +0000 | [diff] [blame] | 167 | 	 * slots number is fixed here for MRST as SDIO3/5 are never used and | 
 | 168 | 	 * have hardware bugs. | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 169 | 	 */ | 
 | 170 | 	chip->num_slots = 1; | 
 | 171 | 	return 0; | 
 | 172 | } | 
 | 173 |  | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 174 | /* Medfield eMMC hardware reset GPIOs */ | 
 | 175 | static int mfd_emmc0_rst_gpio = -EINVAL; | 
 | 176 | static int mfd_emmc1_rst_gpio = -EINVAL; | 
 | 177 |  | 
 | 178 | static int mfd_emmc_gpio_parse(struct sfi_table_header *table) | 
 | 179 | { | 
 | 180 | 	struct sfi_table_simple *sb = (struct sfi_table_simple *)table; | 
 | 181 | 	struct sfi_gpio_table_entry *entry; | 
 | 182 | 	int i, num; | 
 | 183 |  | 
 | 184 | 	num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); | 
 | 185 | 	entry = (struct sfi_gpio_table_entry *)sb->pentry; | 
 | 186 |  | 
 | 187 | 	for (i = 0; i < num; i++, entry++) { | 
 | 188 | 		if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN)) | 
 | 189 | 			mfd_emmc0_rst_gpio = entry->pin_no; | 
 | 190 | 		else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN)) | 
 | 191 | 			mfd_emmc1_rst_gpio = entry->pin_no; | 
 | 192 | 	} | 
 | 193 |  | 
 | 194 | 	return 0; | 
 | 195 | } | 
 | 196 |  | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 197 | #ifdef CONFIG_PM_RUNTIME | 
 | 198 |  | 
 | 199 | static irqreturn_t mfd_sd_cd(int irq, void *dev_id) | 
 | 200 | { | 
 | 201 | 	struct sdhci_pci_slot *slot = dev_id; | 
 | 202 | 	struct sdhci_host *host = slot->host; | 
 | 203 |  | 
 | 204 | 	mmc_detect_change(host->mmc, msecs_to_jiffies(200)); | 
 | 205 | 	return IRQ_HANDLED; | 
 | 206 | } | 
 | 207 |  | 
 | 208 | #define MFLD_SD_CD_PIN 69 | 
 | 209 |  | 
 | 210 | static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot) | 
 | 211 | { | 
 | 212 | 	int err, irq, gpio = MFLD_SD_CD_PIN; | 
 | 213 |  | 
 | 214 | 	slot->cd_gpio = -EINVAL; | 
 | 215 | 	slot->cd_irq = -EINVAL; | 
 | 216 |  | 
 | 217 | 	err = gpio_request(gpio, "sd_cd"); | 
 | 218 | 	if (err < 0) | 
 | 219 | 		goto out; | 
 | 220 |  | 
 | 221 | 	err = gpio_direction_input(gpio); | 
 | 222 | 	if (err < 0) | 
 | 223 | 		goto out_free; | 
 | 224 |  | 
 | 225 | 	irq = gpio_to_irq(gpio); | 
 | 226 | 	if (irq < 0) | 
 | 227 | 		goto out_free; | 
 | 228 |  | 
 | 229 | 	err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING | | 
 | 230 | 			  IRQF_TRIGGER_FALLING, "sd_cd", slot); | 
 | 231 | 	if (err) | 
 | 232 | 		goto out_free; | 
 | 233 |  | 
 | 234 | 	slot->cd_gpio = gpio; | 
 | 235 | 	slot->cd_irq = irq; | 
 | 236 | 	slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION; | 
 | 237 |  | 
 | 238 | 	return 0; | 
 | 239 |  | 
 | 240 | out_free: | 
 | 241 | 	gpio_free(gpio); | 
 | 242 | out: | 
 | 243 | 	dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n"); | 
 | 244 | 	return 0; | 
 | 245 | } | 
 | 246 |  | 
 | 247 | static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead) | 
 | 248 | { | 
 | 249 | 	if (slot->cd_irq >= 0) | 
 | 250 | 		free_irq(slot->cd_irq, slot); | 
 | 251 | 	gpio_free(slot->cd_gpio); | 
 | 252 | } | 
 | 253 |  | 
 | 254 | #else | 
 | 255 |  | 
 | 256 | #define mfd_sd_probe_slot	NULL | 
 | 257 | #define mfd_sd_remove_slot	NULL | 
 | 258 |  | 
 | 259 | #endif | 
 | 260 |  | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 261 | static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) | 
 | 262 | { | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 263 | 	const char *name = NULL; | 
 | 264 | 	int gpio = -EINVAL; | 
 | 265 |  | 
 | 266 | 	sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse); | 
 | 267 |  | 
 | 268 | 	switch (slot->chip->pdev->device) { | 
 | 269 | 	case PCI_DEVICE_ID_INTEL_MFD_EMMC0: | 
 | 270 | 		gpio = mfd_emmc0_rst_gpio; | 
 | 271 | 		name = "eMMC0_reset"; | 
 | 272 | 		break; | 
 | 273 | 	case PCI_DEVICE_ID_INTEL_MFD_EMMC1: | 
 | 274 | 		gpio = mfd_emmc1_rst_gpio; | 
 | 275 | 		name = "eMMC1_reset"; | 
 | 276 | 		break; | 
 | 277 | 	} | 
 | 278 |  | 
 | 279 | 	if (!gpio_request(gpio, name)) { | 
 | 280 | 		gpio_direction_output(gpio, 1); | 
 | 281 | 		slot->rst_n_gpio = gpio; | 
 | 282 | 		slot->host->mmc->caps |= MMC_CAP_HW_RESET; | 
 | 283 | 	} | 
 | 284 |  | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 285 | 	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 286 |  | 
| Adrian Hunter | f7c56ef | 2011-09-23 12:48:21 +0300 | [diff] [blame] | 287 | 	slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC; | 
 | 288 |  | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 289 | 	return 0; | 
 | 290 | } | 
 | 291 |  | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 292 | static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead) | 
 | 293 | { | 
 | 294 | 	gpio_free(slot->rst_n_gpio); | 
 | 295 | } | 
 | 296 |  | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 297 | static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { | 
 | 298 | 	.quirks		= SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, | 
| Major Lee | 68077b0 | 2011-06-29 14:23:46 +0300 | [diff] [blame] | 299 | 	.probe_slot	= mrst_hc_probe_slot, | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 300 | }; | 
 | 301 |  | 
| Jacob Pan | 35ac6f0 | 2010-11-09 13:57:29 +0000 | [diff] [blame] | 302 | static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = { | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 303 | 	.quirks		= SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, | 
| Jacob Pan | 35ac6f0 | 2010-11-09 13:57:29 +0000 | [diff] [blame] | 304 | 	.probe		= mrst_hc_probe, | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 305 | }; | 
 | 306 |  | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 307 | static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { | 
 | 308 | 	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 309 | 	.allow_runtime_pm = true, | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 310 | 	.probe_slot	= mfd_sd_probe_slot, | 
 | 311 | 	.remove_slot	= mfd_sd_remove_slot, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 312 | }; | 
 | 313 |  | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 314 | static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 315 | 	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 316 | 	.allow_runtime_pm = true, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 317 | }; | 
 | 318 |  | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 319 | static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { | 
 | 320 | 	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 321 | 	.allow_runtime_pm = true, | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 322 | 	.probe_slot	= mfd_emmc_probe_slot, | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 323 | 	.remove_slot	= mfd_emmc_remove_slot, | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 324 | }; | 
 | 325 |  | 
| Jennifer Li | 26daa1e | 2010-11-17 23:01:59 -0500 | [diff] [blame] | 326 | /* O2Micro extra registers */ | 
 | 327 | #define O2_SD_LOCK_WP		0xD3 | 
 | 328 | #define O2_SD_MULTI_VCC3V	0xEE | 
 | 329 | #define O2_SD_CLKREQ		0xEC | 
 | 330 | #define O2_SD_CAPS		0xE0 | 
 | 331 | #define O2_SD_ADMA1		0xE2 | 
 | 332 | #define O2_SD_ADMA2		0xE7 | 
 | 333 | #define O2_SD_INF_MOD		0xF1 | 
 | 334 |  | 
 | 335 | static int o2_probe(struct sdhci_pci_chip *chip) | 
 | 336 | { | 
 | 337 | 	int ret; | 
 | 338 | 	u8 scratch; | 
 | 339 |  | 
 | 340 | 	switch (chip->pdev->device) { | 
 | 341 | 	case PCI_DEVICE_ID_O2_8220: | 
 | 342 | 	case PCI_DEVICE_ID_O2_8221: | 
 | 343 | 	case PCI_DEVICE_ID_O2_8320: | 
 | 344 | 	case PCI_DEVICE_ID_O2_8321: | 
 | 345 | 		/* This extra setup is required due to broken ADMA. */ | 
 | 346 | 		ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); | 
 | 347 | 		if (ret) | 
 | 348 | 			return ret; | 
 | 349 | 		scratch &= 0x7f; | 
 | 350 | 		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); | 
 | 351 |  | 
 | 352 | 		/* Set Multi 3 to VCC3V# */ | 
 | 353 | 		pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); | 
 | 354 |  | 
 | 355 | 		/* Disable CLK_REQ# support after media DET */ | 
 | 356 | 		ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch); | 
 | 357 | 		if (ret) | 
 | 358 | 			return ret; | 
 | 359 | 		scratch |= 0x20; | 
 | 360 | 		pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); | 
 | 361 |  | 
 | 362 | 		/* Choose capabilities, enable SDMA.  We have to write 0x01 | 
 | 363 | 		 * to the capabilities register first to unlock it. | 
 | 364 | 		 */ | 
 | 365 | 		ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); | 
 | 366 | 		if (ret) | 
 | 367 | 			return ret; | 
 | 368 | 		scratch |= 0x01; | 
 | 369 | 		pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); | 
 | 370 | 		pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); | 
 | 371 |  | 
 | 372 | 		/* Disable ADMA1/2 */ | 
 | 373 | 		pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); | 
 | 374 | 		pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); | 
 | 375 |  | 
 | 376 | 		/* Disable the infinite transfer mode */ | 
 | 377 | 		ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch); | 
 | 378 | 		if (ret) | 
 | 379 | 			return ret; | 
 | 380 | 		scratch |= 0x08; | 
 | 381 | 		pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); | 
 | 382 |  | 
 | 383 | 		/* Lock WP */ | 
 | 384 | 		ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); | 
 | 385 | 		if (ret) | 
 | 386 | 			return ret; | 
 | 387 | 		scratch |= 0x80; | 
 | 388 | 		pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); | 
 | 389 | 	} | 
 | 390 |  | 
 | 391 | 	return 0; | 
 | 392 | } | 
 | 393 |  | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 394 | static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) | 
 | 395 | { | 
 | 396 | 	u8 scratch; | 
 | 397 | 	int ret; | 
 | 398 |  | 
 | 399 | 	ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch); | 
 | 400 | 	if (ret) | 
 | 401 | 		return ret; | 
 | 402 |  | 
 | 403 | 	/* | 
 | 404 | 	 * Turn PMOS on [bit 0], set over current detection to 2.4 V | 
 | 405 | 	 * [bit 1:2] and enable over current debouncing [bit 6]. | 
 | 406 | 	 */ | 
 | 407 | 	if (on) | 
 | 408 | 		scratch |= 0x47; | 
 | 409 | 	else | 
 | 410 | 		scratch &= ~0x47; | 
 | 411 |  | 
 | 412 | 	ret = pci_write_config_byte(chip->pdev, 0xAE, scratch); | 
 | 413 | 	if (ret) | 
 | 414 | 		return ret; | 
 | 415 |  | 
 | 416 | 	return 0; | 
 | 417 | } | 
 | 418 |  | 
 | 419 | static int jmicron_probe(struct sdhci_pci_chip *chip) | 
 | 420 | { | 
 | 421 | 	int ret; | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 422 | 	u16 mmcdev = 0; | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 423 |  | 
| Pierre Ossman | 93fc48c | 2008-06-28 18:21:41 +0200 | [diff] [blame] | 424 | 	if (chip->pdev->revision == 0) { | 
 | 425 | 		chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | | 
 | 426 | 			  SDHCI_QUIRK_32BIT_DMA_SIZE | | 
| Pierre Ossman | 2134a92 | 2008-06-28 18:28:51 +0200 | [diff] [blame] | 427 | 			  SDHCI_QUIRK_32BIT_ADMA_SIZE | | 
| Pierre Ossman | 4a3cba3 | 2008-07-29 00:11:16 +0200 | [diff] [blame] | 428 | 			  SDHCI_QUIRK_RESET_AFTER_REQUEST | | 
| Pierre Ossman | 86a6a87 | 2009-02-02 21:13:49 +0100 | [diff] [blame] | 429 | 			  SDHCI_QUIRK_BROKEN_SMALL_PIO; | 
| Pierre Ossman | 93fc48c | 2008-06-28 18:21:41 +0200 | [diff] [blame] | 430 | 	} | 
 | 431 |  | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 432 | 	/* | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 433 | 	 * JMicron chips can have two interfaces to the same hardware | 
 | 434 | 	 * in order to work around limitations in Microsoft's driver. | 
 | 435 | 	 * We need to make sure we only bind to one of them. | 
 | 436 | 	 * | 
 | 437 | 	 * This code assumes two things: | 
 | 438 | 	 * | 
 | 439 | 	 * 1. The PCI code adds subfunctions in order. | 
 | 440 | 	 * | 
 | 441 | 	 * 2. The MMC interface has a lower subfunction number | 
 | 442 | 	 *    than the SD interface. | 
 | 443 | 	 */ | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 444 | 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) | 
 | 445 | 		mmcdev = PCI_DEVICE_ID_JMICRON_JMB38X_MMC; | 
 | 446 | 	else if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD) | 
 | 447 | 		mmcdev = PCI_DEVICE_ID_JMICRON_JMB388_ESD; | 
 | 448 |  | 
 | 449 | 	if (mmcdev) { | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 450 | 		struct pci_dev *sd_dev; | 
 | 451 |  | 
 | 452 | 		sd_dev = NULL; | 
 | 453 | 		while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 454 | 						mmcdev, sd_dev)) != NULL) { | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 455 | 			if ((PCI_SLOT(chip->pdev->devfn) == | 
 | 456 | 				PCI_SLOT(sd_dev->devfn)) && | 
 | 457 | 				(chip->pdev->bus == sd_dev->bus)) | 
 | 458 | 				break; | 
 | 459 | 		} | 
 | 460 |  | 
 | 461 | 		if (sd_dev) { | 
 | 462 | 			pci_dev_put(sd_dev); | 
 | 463 | 			dev_info(&chip->pdev->dev, "Refusing to bind to " | 
 | 464 | 				"secondary interface.\n"); | 
 | 465 | 			return -ENODEV; | 
 | 466 | 		} | 
 | 467 | 	} | 
 | 468 |  | 
 | 469 | 	/* | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 470 | 	 * JMicron chips need a bit of a nudge to enable the power | 
 | 471 | 	 * output pins. | 
 | 472 | 	 */ | 
 | 473 | 	ret = jmicron_pmos(chip, 1); | 
 | 474 | 	if (ret) { | 
 | 475 | 		dev_err(&chip->pdev->dev, "Failure enabling card power\n"); | 
 | 476 | 		return ret; | 
 | 477 | 	} | 
 | 478 |  | 
| Takashi Iwai | 82b0e23 | 2011-04-21 20:26:38 +0200 | [diff] [blame] | 479 | 	/* quirk for unsable RO-detection on JM388 chips */ | 
 | 480 | 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD || | 
 | 481 | 	    chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) | 
 | 482 | 		chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT; | 
 | 483 |  | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 484 | 	return 0; | 
 | 485 | } | 
 | 486 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 487 | static void jmicron_enable_mmc(struct sdhci_host *host, int on) | 
 | 488 | { | 
 | 489 | 	u8 scratch; | 
 | 490 |  | 
 | 491 | 	scratch = readb(host->ioaddr + 0xC0); | 
 | 492 |  | 
 | 493 | 	if (on) | 
 | 494 | 		scratch |= 0x01; | 
 | 495 | 	else | 
 | 496 | 		scratch &= ~0x01; | 
 | 497 |  | 
 | 498 | 	writeb(scratch, host->ioaddr + 0xC0); | 
 | 499 | } | 
 | 500 |  | 
 | 501 | static int jmicron_probe_slot(struct sdhci_pci_slot *slot) | 
 | 502 | { | 
| Pierre Ossman | 2134a92 | 2008-06-28 18:28:51 +0200 | [diff] [blame] | 503 | 	if (slot->chip->pdev->revision == 0) { | 
 | 504 | 		u16 version; | 
 | 505 |  | 
 | 506 | 		version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION); | 
 | 507 | 		version = (version & SDHCI_VENDOR_VER_MASK) >> | 
 | 508 | 			SDHCI_VENDOR_VER_SHIFT; | 
 | 509 |  | 
 | 510 | 		/* | 
 | 511 | 		 * Older versions of the chip have lots of nasty glitches | 
 | 512 | 		 * in the ADMA engine. It's best just to avoid it | 
 | 513 | 		 * completely. | 
 | 514 | 		 */ | 
 | 515 | 		if (version < 0xAC) | 
 | 516 | 			slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; | 
 | 517 | 	} | 
 | 518 |  | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 519 | 	/* JM388 MMC doesn't support 1.8V while SD supports it */ | 
 | 520 | 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { | 
 | 521 | 		slot->host->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34 | | 
 | 522 | 			MMC_VDD_29_30 | MMC_VDD_30_31 | | 
 | 523 | 			MMC_VDD_165_195; /* allow 1.8V */ | 
 | 524 | 		slot->host->ocr_avail_mmc = MMC_VDD_32_33 | MMC_VDD_33_34 | | 
 | 525 | 			MMC_VDD_29_30 | MMC_VDD_30_31; /* no 1.8V for MMC */ | 
 | 526 | 	} | 
 | 527 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 528 | 	/* | 
 | 529 | 	 * The secondary interface requires a bit set to get the | 
 | 530 | 	 * interrupts. | 
 | 531 | 	 */ | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 532 | 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | 
 | 533 | 	    slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 534 | 		jmicron_enable_mmc(slot->host, 1); | 
 | 535 |  | 
| Takashi Iwai | d75c108 | 2010-12-16 17:54:14 +0100 | [diff] [blame] | 536 | 	slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST; | 
 | 537 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 538 | 	return 0; | 
 | 539 | } | 
 | 540 |  | 
| Pierre Ossman | 1e72859 | 2008-04-16 19:13:13 +0200 | [diff] [blame] | 541 | static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 542 | { | 
| Pierre Ossman | 1e72859 | 2008-04-16 19:13:13 +0200 | [diff] [blame] | 543 | 	if (dead) | 
 | 544 | 		return; | 
 | 545 |  | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 546 | 	if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | 
 | 547 | 	    slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 548 | 		jmicron_enable_mmc(slot->host, 0); | 
 | 549 | } | 
 | 550 |  | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 551 | static int jmicron_suspend(struct sdhci_pci_chip *chip) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 552 | { | 
 | 553 | 	int i; | 
 | 554 |  | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 555 | 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | 
 | 556 | 	    chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 557 | 		for (i = 0; i < chip->num_slots; i++) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 558 | 			jmicron_enable_mmc(chip->slots[i]->host, 0); | 
 | 559 | 	} | 
 | 560 |  | 
 | 561 | 	return 0; | 
 | 562 | } | 
 | 563 |  | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 564 | static int jmicron_resume(struct sdhci_pci_chip *chip) | 
 | 565 | { | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 566 | 	int ret, i; | 
 | 567 |  | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 568 | 	if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || | 
 | 569 | 	    chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 570 | 		for (i = 0; i < chip->num_slots; i++) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 571 | 			jmicron_enable_mmc(chip->slots[i]->host, 1); | 
 | 572 | 	} | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 573 |  | 
 | 574 | 	ret = jmicron_pmos(chip, 1); | 
 | 575 | 	if (ret) { | 
 | 576 | 		dev_err(&chip->pdev->dev, "Failure enabling card power\n"); | 
 | 577 | 		return ret; | 
 | 578 | 	} | 
 | 579 |  | 
 | 580 | 	return 0; | 
 | 581 | } | 
 | 582 |  | 
| Jennifer Li | 26daa1e | 2010-11-17 23:01:59 -0500 | [diff] [blame] | 583 | static const struct sdhci_pci_fixes sdhci_o2 = { | 
 | 584 | 	.probe		= o2_probe, | 
 | 585 | }; | 
 | 586 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 587 | static const struct sdhci_pci_fixes sdhci_jmicron = { | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 588 | 	.probe		= jmicron_probe, | 
 | 589 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 590 | 	.probe_slot	= jmicron_probe_slot, | 
 | 591 | 	.remove_slot	= jmicron_remove_slot, | 
 | 592 |  | 
 | 593 | 	.suspend	= jmicron_suspend, | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 594 | 	.resume		= jmicron_resume, | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 595 | }; | 
 | 596 |  | 
| Nicolas Pitre | a7a6186 | 2009-12-14 18:01:26 -0800 | [diff] [blame] | 597 | /* SysKonnect CardBus2SDIO extra registers */ | 
 | 598 | #define SYSKT_CTRL		0x200 | 
 | 599 | #define SYSKT_RDFIFO_STAT	0x204 | 
 | 600 | #define SYSKT_WRFIFO_STAT	0x208 | 
 | 601 | #define SYSKT_POWER_DATA	0x20c | 
 | 602 | #define   SYSKT_POWER_330	0xef | 
 | 603 | #define   SYSKT_POWER_300	0xf8 | 
 | 604 | #define   SYSKT_POWER_184	0xcc | 
 | 605 | #define SYSKT_POWER_CMD		0x20d | 
 | 606 | #define   SYSKT_POWER_START	(1 << 7) | 
 | 607 | #define SYSKT_POWER_STATUS	0x20e | 
 | 608 | #define   SYSKT_POWER_STATUS_OK	(1 << 0) | 
 | 609 | #define SYSKT_BOARD_REV		0x210 | 
 | 610 | #define SYSKT_CHIP_REV		0x211 | 
 | 611 | #define SYSKT_CONF_DATA		0x212 | 
 | 612 | #define   SYSKT_CONF_DATA_1V8	(1 << 2) | 
 | 613 | #define   SYSKT_CONF_DATA_2V5	(1 << 1) | 
 | 614 | #define   SYSKT_CONF_DATA_3V3	(1 << 0) | 
 | 615 |  | 
 | 616 | static int syskt_probe(struct sdhci_pci_chip *chip) | 
 | 617 | { | 
 | 618 | 	if ((chip->pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { | 
 | 619 | 		chip->pdev->class &= ~0x0000FF; | 
 | 620 | 		chip->pdev->class |= PCI_SDHCI_IFDMA; | 
 | 621 | 	} | 
 | 622 | 	return 0; | 
 | 623 | } | 
 | 624 |  | 
 | 625 | static int syskt_probe_slot(struct sdhci_pci_slot *slot) | 
 | 626 | { | 
 | 627 | 	int tm, ps; | 
 | 628 |  | 
 | 629 | 	u8 board_rev = readb(slot->host->ioaddr + SYSKT_BOARD_REV); | 
 | 630 | 	u8  chip_rev = readb(slot->host->ioaddr + SYSKT_CHIP_REV); | 
 | 631 | 	dev_info(&slot->chip->pdev->dev, "SysKonnect CardBus2SDIO, " | 
 | 632 | 					 "board rev %d.%d, chip rev %d.%d\n", | 
 | 633 | 					 board_rev >> 4, board_rev & 0xf, | 
 | 634 | 					 chip_rev >> 4,  chip_rev & 0xf); | 
 | 635 | 	if (chip_rev >= 0x20) | 
 | 636 | 		slot->host->quirks |= SDHCI_QUIRK_FORCE_DMA; | 
 | 637 |  | 
 | 638 | 	writeb(SYSKT_POWER_330, slot->host->ioaddr + SYSKT_POWER_DATA); | 
 | 639 | 	writeb(SYSKT_POWER_START, slot->host->ioaddr + SYSKT_POWER_CMD); | 
 | 640 | 	udelay(50); | 
 | 641 | 	tm = 10;  /* Wait max 1 ms */ | 
 | 642 | 	do { | 
 | 643 | 		ps = readw(slot->host->ioaddr + SYSKT_POWER_STATUS); | 
 | 644 | 		if (ps & SYSKT_POWER_STATUS_OK) | 
 | 645 | 			break; | 
 | 646 | 		udelay(100); | 
 | 647 | 	} while (--tm); | 
 | 648 | 	if (!tm) { | 
 | 649 | 		dev_err(&slot->chip->pdev->dev, | 
 | 650 | 			"power regulator never stabilized"); | 
 | 651 | 		writeb(0, slot->host->ioaddr + SYSKT_POWER_CMD); | 
 | 652 | 		return -ENODEV; | 
 | 653 | 	} | 
 | 654 |  | 
 | 655 | 	return 0; | 
 | 656 | } | 
 | 657 |  | 
 | 658 | static const struct sdhci_pci_fixes sdhci_syskt = { | 
 | 659 | 	.quirks		= SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER, | 
 | 660 | 	.probe		= syskt_probe, | 
 | 661 | 	.probe_slot	= syskt_probe_slot, | 
 | 662 | }; | 
 | 663 |  | 
| Harald Welte | 557b069 | 2009-06-18 16:53:38 +0200 | [diff] [blame] | 664 | static int via_probe(struct sdhci_pci_chip *chip) | 
 | 665 | { | 
 | 666 | 	if (chip->pdev->revision == 0x10) | 
 | 667 | 		chip->quirks |= SDHCI_QUIRK_DELAY_AFTER_POWER; | 
 | 668 |  | 
 | 669 | 	return 0; | 
 | 670 | } | 
 | 671 |  | 
 | 672 | static const struct sdhci_pci_fixes sdhci_via = { | 
 | 673 | 	.probe		= via_probe, | 
 | 674 | }; | 
 | 675 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 676 | static const struct pci_device_id pci_ids[] __devinitdata = { | 
 | 677 | 	{ | 
 | 678 | 		.vendor		= PCI_VENDOR_ID_RICOH, | 
 | 679 | 		.device		= PCI_DEVICE_ID_RICOH_R5C822, | 
 | 680 | 		.subvendor	= PCI_ANY_ID, | 
 | 681 | 		.subdevice	= PCI_ANY_ID, | 
 | 682 | 		.driver_data	= (kernel_ulong_t)&sdhci_ricoh, | 
 | 683 | 	}, | 
 | 684 |  | 
 | 685 | 	{ | 
| Maxim Levitsky | ccc92c2 | 2010-08-10 18:01:42 -0700 | [diff] [blame] | 686 | 		.vendor         = PCI_VENDOR_ID_RICOH, | 
 | 687 | 		.device         = 0x843, | 
 | 688 | 		.subvendor      = PCI_ANY_ID, | 
 | 689 | 		.subdevice      = PCI_ANY_ID, | 
 | 690 | 		.driver_data    = (kernel_ulong_t)&sdhci_ricoh_mmc, | 
 | 691 | 	}, | 
 | 692 |  | 
 | 693 | 	{ | 
| Pablo Castillo | 568133e | 2010-08-10 18:02:01 -0700 | [diff] [blame] | 694 | 		.vendor         = PCI_VENDOR_ID_RICOH, | 
 | 695 | 		.device         = 0xe822, | 
 | 696 | 		.subvendor      = PCI_ANY_ID, | 
 | 697 | 		.subdevice      = PCI_ANY_ID, | 
 | 698 | 		.driver_data    = (kernel_ulong_t)&sdhci_ricoh_mmc, | 
 | 699 | 	}, | 
 | 700 |  | 
 | 701 | 	{ | 
| Manoj Iyer | 5fd11c0 | 2011-02-11 16:25:31 -0600 | [diff] [blame] | 702 | 		.vendor         = PCI_VENDOR_ID_RICOH, | 
 | 703 | 		.device         = 0xe823, | 
 | 704 | 		.subvendor      = PCI_ANY_ID, | 
 | 705 | 		.subdevice      = PCI_ANY_ID, | 
 | 706 | 		.driver_data    = (kernel_ulong_t)&sdhci_ricoh_mmc, | 
 | 707 | 	}, | 
 | 708 |  | 
 | 709 | 	{ | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 710 | 		.vendor		= PCI_VENDOR_ID_ENE, | 
 | 711 | 		.device		= PCI_DEVICE_ID_ENE_CB712_SD, | 
 | 712 | 		.subvendor	= PCI_ANY_ID, | 
 | 713 | 		.subdevice	= PCI_ANY_ID, | 
 | 714 | 		.driver_data	= (kernel_ulong_t)&sdhci_ene_712, | 
 | 715 | 	}, | 
 | 716 |  | 
 | 717 | 	{ | 
 | 718 | 		.vendor		= PCI_VENDOR_ID_ENE, | 
 | 719 | 		.device		= PCI_DEVICE_ID_ENE_CB712_SD_2, | 
 | 720 | 		.subvendor	= PCI_ANY_ID, | 
 | 721 | 		.subdevice	= PCI_ANY_ID, | 
 | 722 | 		.driver_data	= (kernel_ulong_t)&sdhci_ene_712, | 
 | 723 | 	}, | 
 | 724 |  | 
 | 725 | 	{ | 
 | 726 | 		.vendor		= PCI_VENDOR_ID_ENE, | 
 | 727 | 		.device		= PCI_DEVICE_ID_ENE_CB714_SD, | 
 | 728 | 		.subvendor	= PCI_ANY_ID, | 
 | 729 | 		.subdevice	= PCI_ANY_ID, | 
 | 730 | 		.driver_data	= (kernel_ulong_t)&sdhci_ene_714, | 
 | 731 | 	}, | 
 | 732 |  | 
 | 733 | 	{ | 
 | 734 | 		.vendor		= PCI_VENDOR_ID_ENE, | 
 | 735 | 		.device		= PCI_DEVICE_ID_ENE_CB714_SD_2, | 
 | 736 | 		.subvendor	= PCI_ANY_ID, | 
 | 737 | 		.subdevice	= PCI_ANY_ID, | 
 | 738 | 		.driver_data	= (kernel_ulong_t)&sdhci_ene_714, | 
 | 739 | 	}, | 
 | 740 |  | 
 | 741 | 	{ | 
 | 742 | 		.vendor         = PCI_VENDOR_ID_MARVELL, | 
| David Woodhouse | 8c5eb88 | 2008-09-03 09:45:57 +0100 | [diff] [blame] | 743 | 		.device         = PCI_DEVICE_ID_MARVELL_88ALP01_SD, | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 744 | 		.subvendor      = PCI_ANY_ID, | 
 | 745 | 		.subdevice      = PCI_ANY_ID, | 
 | 746 | 		.driver_data    = (kernel_ulong_t)&sdhci_cafe, | 
 | 747 | 	}, | 
 | 748 |  | 
 | 749 | 	{ | 
 | 750 | 		.vendor		= PCI_VENDOR_ID_JMICRON, | 
 | 751 | 		.device		= PCI_DEVICE_ID_JMICRON_JMB38X_SD, | 
 | 752 | 		.subvendor	= PCI_ANY_ID, | 
 | 753 | 		.subdevice	= PCI_ANY_ID, | 
 | 754 | 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | 
 | 755 | 	}, | 
 | 756 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 757 | 	{ | 
 | 758 | 		.vendor		= PCI_VENDOR_ID_JMICRON, | 
 | 759 | 		.device		= PCI_DEVICE_ID_JMICRON_JMB38X_MMC, | 
 | 760 | 		.subvendor	= PCI_ANY_ID, | 
 | 761 | 		.subdevice	= PCI_ANY_ID, | 
 | 762 | 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | 
 | 763 | 	}, | 
 | 764 |  | 
| Harald Welte | 557b069 | 2009-06-18 16:53:38 +0200 | [diff] [blame] | 765 | 	{ | 
| Takashi Iwai | 8f230f4 | 2010-12-08 10:04:30 +0100 | [diff] [blame] | 766 | 		.vendor		= PCI_VENDOR_ID_JMICRON, | 
 | 767 | 		.device		= PCI_DEVICE_ID_JMICRON_JMB388_SD, | 
 | 768 | 		.subvendor	= PCI_ANY_ID, | 
 | 769 | 		.subdevice	= PCI_ANY_ID, | 
 | 770 | 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | 
 | 771 | 	}, | 
 | 772 |  | 
 | 773 | 	{ | 
 | 774 | 		.vendor		= PCI_VENDOR_ID_JMICRON, | 
 | 775 | 		.device		= PCI_DEVICE_ID_JMICRON_JMB388_ESD, | 
 | 776 | 		.subvendor	= PCI_ANY_ID, | 
 | 777 | 		.subdevice	= PCI_ANY_ID, | 
 | 778 | 		.driver_data	= (kernel_ulong_t)&sdhci_jmicron, | 
 | 779 | 	}, | 
 | 780 |  | 
 | 781 | 	{ | 
| Nicolas Pitre | a7a6186 | 2009-12-14 18:01:26 -0800 | [diff] [blame] | 782 | 		.vendor		= PCI_VENDOR_ID_SYSKONNECT, | 
 | 783 | 		.device		= 0x8000, | 
 | 784 | 		.subvendor	= PCI_ANY_ID, | 
 | 785 | 		.subdevice	= PCI_ANY_ID, | 
 | 786 | 		.driver_data	= (kernel_ulong_t)&sdhci_syskt, | 
 | 787 | 	}, | 
 | 788 |  | 
 | 789 | 	{ | 
| Harald Welte | 557b069 | 2009-06-18 16:53:38 +0200 | [diff] [blame] | 790 | 		.vendor		= PCI_VENDOR_ID_VIA, | 
 | 791 | 		.device		= 0x95d0, | 
 | 792 | 		.subvendor	= PCI_ANY_ID, | 
 | 793 | 		.subdevice	= PCI_ANY_ID, | 
 | 794 | 		.driver_data	= (kernel_ulong_t)&sdhci_via, | 
 | 795 | 	}, | 
 | 796 |  | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 797 | 	{ | 
 | 798 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 799 | 		.device		= PCI_DEVICE_ID_INTEL_MRST_SD0, | 
 | 800 | 		.subvendor	= PCI_ANY_ID, | 
 | 801 | 		.subdevice	= PCI_ANY_ID, | 
 | 802 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mrst_hc0, | 
 | 803 | 	}, | 
 | 804 |  | 
 | 805 | 	{ | 
 | 806 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
 | 807 | 		.device		= PCI_DEVICE_ID_INTEL_MRST_SD1, | 
 | 808 | 		.subvendor	= PCI_ANY_ID, | 
 | 809 | 		.subdevice	= PCI_ANY_ID, | 
| Jacob Pan | 35ac6f0 | 2010-11-09 13:57:29 +0000 | [diff] [blame] | 810 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mrst_hc1_hc2, | 
 | 811 | 	}, | 
 | 812 |  | 
 | 813 | 	{ | 
 | 814 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
 | 815 | 		.device		= PCI_DEVICE_ID_INTEL_MRST_SD2, | 
 | 816 | 		.subvendor	= PCI_ANY_ID, | 
 | 817 | 		.subdevice	= PCI_ANY_ID, | 
 | 818 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mrst_hc1_hc2, | 
| Alan Cox | f9ee3ea | 2010-10-04 15:25:11 +0100 | [diff] [blame] | 819 | 	}, | 
 | 820 |  | 
 | 821 | 	{ | 
 | 822 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 823 | 		.device		= PCI_DEVICE_ID_INTEL_MFD_SD, | 
 | 824 | 		.subvendor	= PCI_ANY_ID, | 
 | 825 | 		.subdevice	= PCI_ANY_ID, | 
 | 826 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_sd, | 
 | 827 | 	}, | 
 | 828 |  | 
 | 829 | 	{ | 
 | 830 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
 | 831 | 		.device		= PCI_DEVICE_ID_INTEL_MFD_SDIO1, | 
 | 832 | 		.subvendor	= PCI_ANY_ID, | 
 | 833 | 		.subdevice	= PCI_ANY_ID, | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 834 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_sdio, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 835 | 	}, | 
 | 836 |  | 
 | 837 | 	{ | 
 | 838 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
 | 839 | 		.device		= PCI_DEVICE_ID_INTEL_MFD_SDIO2, | 
 | 840 | 		.subvendor	= PCI_ANY_ID, | 
 | 841 | 		.subdevice	= PCI_ANY_ID, | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 842 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_sdio, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 843 | 	}, | 
 | 844 |  | 
 | 845 | 	{ | 
 | 846 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
 | 847 | 		.device		= PCI_DEVICE_ID_INTEL_MFD_EMMC0, | 
 | 848 | 		.subvendor	= PCI_ANY_ID, | 
 | 849 | 		.subdevice	= PCI_ANY_ID, | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 850 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_emmc, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 851 | 	}, | 
 | 852 |  | 
 | 853 | 	{ | 
 | 854 | 		.vendor		= PCI_VENDOR_ID_INTEL, | 
 | 855 | 		.device		= PCI_DEVICE_ID_INTEL_MFD_EMMC1, | 
 | 856 | 		.subvendor	= PCI_ANY_ID, | 
 | 857 | 		.subdevice	= PCI_ANY_ID, | 
| Adrian Hunter | 0d013bc | 2011-06-29 14:23:47 +0300 | [diff] [blame] | 858 | 		.driver_data	= (kernel_ulong_t)&sdhci_intel_mfd_emmc, | 
| Xiaochen Shen | 2922905 | 2010-10-04 15:24:52 +0100 | [diff] [blame] | 859 | 	}, | 
 | 860 |  | 
| Jennifer Li | 26daa1e | 2010-11-17 23:01:59 -0500 | [diff] [blame] | 861 | 	{ | 
 | 862 | 		.vendor		= PCI_VENDOR_ID_O2, | 
 | 863 | 		.device		= PCI_DEVICE_ID_O2_8120, | 
 | 864 | 		.subvendor	= PCI_ANY_ID, | 
 | 865 | 		.subdevice	= PCI_ANY_ID, | 
 | 866 | 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | 
 | 867 | 	}, | 
 | 868 |  | 
 | 869 | 	{ | 
 | 870 | 		.vendor		= PCI_VENDOR_ID_O2, | 
 | 871 | 		.device		= PCI_DEVICE_ID_O2_8220, | 
 | 872 | 		.subvendor	= PCI_ANY_ID, | 
 | 873 | 		.subdevice	= PCI_ANY_ID, | 
 | 874 | 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | 
 | 875 | 	}, | 
 | 876 |  | 
 | 877 | 	{ | 
 | 878 | 		.vendor		= PCI_VENDOR_ID_O2, | 
 | 879 | 		.device		= PCI_DEVICE_ID_O2_8221, | 
 | 880 | 		.subvendor	= PCI_ANY_ID, | 
 | 881 | 		.subdevice	= PCI_ANY_ID, | 
 | 882 | 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | 
 | 883 | 	}, | 
 | 884 |  | 
 | 885 | 	{ | 
 | 886 | 		.vendor		= PCI_VENDOR_ID_O2, | 
 | 887 | 		.device		= PCI_DEVICE_ID_O2_8320, | 
 | 888 | 		.subvendor	= PCI_ANY_ID, | 
 | 889 | 		.subdevice	= PCI_ANY_ID, | 
 | 890 | 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | 
 | 891 | 	}, | 
 | 892 |  | 
 | 893 | 	{ | 
 | 894 | 		.vendor		= PCI_VENDOR_ID_O2, | 
 | 895 | 		.device		= PCI_DEVICE_ID_O2_8321, | 
 | 896 | 		.subvendor	= PCI_ANY_ID, | 
 | 897 | 		.subdevice	= PCI_ANY_ID, | 
 | 898 | 		.driver_data	= (kernel_ulong_t)&sdhci_o2, | 
 | 899 | 	}, | 
 | 900 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 901 | 	{	/* Generic SD host controller */ | 
 | 902 | 		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) | 
 | 903 | 	}, | 
 | 904 |  | 
 | 905 | 	{ /* end: all zeroes */ }, | 
 | 906 | }; | 
 | 907 |  | 
 | 908 | MODULE_DEVICE_TABLE(pci, pci_ids); | 
 | 909 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 910 | /*****************************************************************************\ | 
 | 911 |  *                                                                           * | 
 | 912 |  * SDHCI core callbacks                                                      * | 
 | 913 |  *                                                                           * | 
 | 914 | \*****************************************************************************/ | 
 | 915 |  | 
 | 916 | static int sdhci_pci_enable_dma(struct sdhci_host *host) | 
 | 917 | { | 
 | 918 | 	struct sdhci_pci_slot *slot; | 
 | 919 | 	struct pci_dev *pdev; | 
 | 920 | 	int ret; | 
 | 921 |  | 
 | 922 | 	slot = sdhci_priv(host); | 
 | 923 | 	pdev = slot->chip->pdev; | 
 | 924 |  | 
 | 925 | 	if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && | 
 | 926 | 		((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && | 
| Richard Röjfors | a13abc7 | 2009-09-22 16:45:30 -0700 | [diff] [blame] | 927 | 		(host->flags & SDHCI_USE_SDMA)) { | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 928 | 		dev_warn(&pdev->dev, "Will use DMA mode even though HW " | 
 | 929 | 			"doesn't fully claim to support it.\n"); | 
 | 930 | 	} | 
 | 931 |  | 
| Yang Hongyang | 284901a | 2009-04-06 19:01:15 -0700 | [diff] [blame] | 932 | 	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 933 | 	if (ret) | 
 | 934 | 		return ret; | 
 | 935 |  | 
 | 936 | 	pci_set_master(pdev); | 
 | 937 |  | 
 | 938 | 	return 0; | 
 | 939 | } | 
 | 940 |  | 
| Major Lee | 68077b0 | 2011-06-29 14:23:46 +0300 | [diff] [blame] | 941 | static int sdhci_pci_8bit_width(struct sdhci_host *host, int width) | 
 | 942 | { | 
 | 943 | 	u8 ctrl; | 
 | 944 |  | 
 | 945 | 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); | 
 | 946 |  | 
 | 947 | 	switch (width) { | 
 | 948 | 	case MMC_BUS_WIDTH_8: | 
 | 949 | 		ctrl |= SDHCI_CTRL_8BITBUS; | 
 | 950 | 		ctrl &= ~SDHCI_CTRL_4BITBUS; | 
 | 951 | 		break; | 
 | 952 | 	case MMC_BUS_WIDTH_4: | 
 | 953 | 		ctrl |= SDHCI_CTRL_4BITBUS; | 
 | 954 | 		ctrl &= ~SDHCI_CTRL_8BITBUS; | 
 | 955 | 		break; | 
 | 956 | 	default: | 
 | 957 | 		ctrl &= ~(SDHCI_CTRL_8BITBUS | SDHCI_CTRL_4BITBUS); | 
 | 958 | 		break; | 
 | 959 | 	} | 
 | 960 |  | 
 | 961 | 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); | 
 | 962 |  | 
 | 963 | 	return 0; | 
 | 964 | } | 
 | 965 |  | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 966 | static void sdhci_pci_hw_reset(struct sdhci_host *host) | 
 | 967 | { | 
 | 968 | 	struct sdhci_pci_slot *slot = sdhci_priv(host); | 
 | 969 | 	int rst_n_gpio = slot->rst_n_gpio; | 
 | 970 |  | 
 | 971 | 	if (!gpio_is_valid(rst_n_gpio)) | 
 | 972 | 		return; | 
 | 973 | 	gpio_set_value_cansleep(rst_n_gpio, 0); | 
 | 974 | 	/* For eMMC, minimum is 1us but give it 10us for good measure */ | 
 | 975 | 	udelay(10); | 
 | 976 | 	gpio_set_value_cansleep(rst_n_gpio, 1); | 
 | 977 | 	/* For eMMC, minimum is 200us but give it 300us for good measure */ | 
 | 978 | 	usleep_range(300, 1000); | 
 | 979 | } | 
 | 980 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 981 | static struct sdhci_ops sdhci_pci_ops = { | 
 | 982 | 	.enable_dma	= sdhci_pci_enable_dma, | 
| Major Lee | 68077b0 | 2011-06-29 14:23:46 +0300 | [diff] [blame] | 983 | 	.platform_8bit_width	= sdhci_pci_8bit_width, | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 984 | 	.hw_reset		= sdhci_pci_hw_reset, | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 985 | }; | 
 | 986 |  | 
 | 987 | /*****************************************************************************\ | 
 | 988 |  *                                                                           * | 
 | 989 |  * Suspend/resume                                                            * | 
 | 990 |  *                                                                           * | 
 | 991 | \*****************************************************************************/ | 
 | 992 |  | 
 | 993 | #ifdef CONFIG_PM | 
 | 994 |  | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 995 | static int sdhci_pci_suspend(struct device *dev) | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 996 | { | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 997 | 	struct pci_dev *pdev = to_pci_dev(dev); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 998 | 	struct sdhci_pci_chip *chip; | 
 | 999 | 	struct sdhci_pci_slot *slot; | 
| Daniel Drake | 5f61970 | 2010-11-04 22:20:39 +0000 | [diff] [blame] | 1000 | 	mmc_pm_flag_t slot_pm_flags; | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1001 | 	mmc_pm_flag_t pm_flags = 0; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1002 | 	int i, ret; | 
 | 1003 |  | 
 | 1004 | 	chip = pci_get_drvdata(pdev); | 
 | 1005 | 	if (!chip) | 
 | 1006 | 		return 0; | 
 | 1007 |  | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1008 | 	for (i = 0; i < chip->num_slots; i++) { | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1009 | 		slot = chip->slots[i]; | 
 | 1010 | 		if (!slot) | 
 | 1011 | 			continue; | 
 | 1012 |  | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1013 | 		ret = sdhci_suspend_host(slot->host); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1014 |  | 
 | 1015 | 		if (ret) { | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1016 | 			for (i--; i >= 0; i--) | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1017 | 				sdhci_resume_host(chip->slots[i]->host); | 
 | 1018 | 			return ret; | 
 | 1019 | 		} | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1020 |  | 
| Daniel Drake | 5f61970 | 2010-11-04 22:20:39 +0000 | [diff] [blame] | 1021 | 		slot_pm_flags = slot->host->mmc->pm_flags; | 
 | 1022 | 		if (slot_pm_flags & MMC_PM_WAKE_SDIO_IRQ) | 
 | 1023 | 			sdhci_enable_irq_wakeups(slot->host); | 
 | 1024 |  | 
 | 1025 | 		pm_flags |= slot_pm_flags; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1026 | 	} | 
 | 1027 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1028 | 	if (chip->fixes && chip->fixes->suspend) { | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1029 | 		ret = chip->fixes->suspend(chip); | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1030 | 		if (ret) { | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1031 | 			for (i = chip->num_slots - 1; i >= 0; i--) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1032 | 				sdhci_resume_host(chip->slots[i]->host); | 
 | 1033 | 			return ret; | 
 | 1034 | 		} | 
 | 1035 | 	} | 
 | 1036 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1037 | 	pci_save_state(pdev); | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1038 | 	if (pm_flags & MMC_PM_KEEP_POWER) { | 
| Daniel Drake | 5f61970 | 2010-11-04 22:20:39 +0000 | [diff] [blame] | 1039 | 		if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) { | 
 | 1040 | 			pci_pme_active(pdev, true); | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1041 | 			pci_enable_wake(pdev, PCI_D3hot, 1); | 
| Daniel Drake | 5f61970 | 2010-11-04 22:20:39 +0000 | [diff] [blame] | 1042 | 		} | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1043 | 		pci_set_power_state(pdev, PCI_D3hot); | 
 | 1044 | 	} else { | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1045 | 		pci_enable_wake(pdev, PCI_D3hot, 0); | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1046 | 		pci_disable_device(pdev); | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1047 | 		pci_set_power_state(pdev, PCI_D3hot); | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1048 | 	} | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1049 |  | 
 | 1050 | 	return 0; | 
 | 1051 | } | 
 | 1052 |  | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1053 | static int sdhci_pci_resume(struct device *dev) | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1054 | { | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1055 | 	struct pci_dev *pdev = to_pci_dev(dev); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1056 | 	struct sdhci_pci_chip *chip; | 
 | 1057 | 	struct sdhci_pci_slot *slot; | 
 | 1058 | 	int i, ret; | 
 | 1059 |  | 
 | 1060 | 	chip = pci_get_drvdata(pdev); | 
 | 1061 | 	if (!chip) | 
 | 1062 | 		return 0; | 
 | 1063 |  | 
 | 1064 | 	pci_set_power_state(pdev, PCI_D0); | 
 | 1065 | 	pci_restore_state(pdev); | 
 | 1066 | 	ret = pci_enable_device(pdev); | 
 | 1067 | 	if (ret) | 
 | 1068 | 		return ret; | 
 | 1069 |  | 
| Pierre Ossman | 45211e2 | 2008-03-24 13:09:09 +0100 | [diff] [blame] | 1070 | 	if (chip->fixes && chip->fixes->resume) { | 
 | 1071 | 		ret = chip->fixes->resume(chip); | 
 | 1072 | 		if (ret) | 
 | 1073 | 			return ret; | 
 | 1074 | 	} | 
 | 1075 |  | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1076 | 	for (i = 0; i < chip->num_slots; i++) { | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1077 | 		slot = chip->slots[i]; | 
 | 1078 | 		if (!slot) | 
 | 1079 | 			continue; | 
 | 1080 |  | 
 | 1081 | 		ret = sdhci_resume_host(slot->host); | 
 | 1082 | 		if (ret) | 
 | 1083 | 			return ret; | 
 | 1084 | 	} | 
 | 1085 |  | 
 | 1086 | 	return 0; | 
 | 1087 | } | 
 | 1088 |  | 
 | 1089 | #else /* CONFIG_PM */ | 
 | 1090 |  | 
 | 1091 | #define sdhci_pci_suspend NULL | 
 | 1092 | #define sdhci_pci_resume NULL | 
 | 1093 |  | 
 | 1094 | #endif /* CONFIG_PM */ | 
 | 1095 |  | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1096 | #ifdef CONFIG_PM_RUNTIME | 
 | 1097 |  | 
 | 1098 | static int sdhci_pci_runtime_suspend(struct device *dev) | 
 | 1099 | { | 
 | 1100 | 	struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); | 
 | 1101 | 	struct sdhci_pci_chip *chip; | 
 | 1102 | 	struct sdhci_pci_slot *slot; | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1103 | 	int i, ret; | 
 | 1104 |  | 
 | 1105 | 	chip = pci_get_drvdata(pdev); | 
 | 1106 | 	if (!chip) | 
 | 1107 | 		return 0; | 
 | 1108 |  | 
 | 1109 | 	for (i = 0; i < chip->num_slots; i++) { | 
 | 1110 | 		slot = chip->slots[i]; | 
 | 1111 | 		if (!slot) | 
 | 1112 | 			continue; | 
 | 1113 |  | 
 | 1114 | 		ret = sdhci_runtime_suspend_host(slot->host); | 
 | 1115 |  | 
 | 1116 | 		if (ret) { | 
 | 1117 | 			for (i--; i >= 0; i--) | 
 | 1118 | 				sdhci_runtime_resume_host(chip->slots[i]->host); | 
 | 1119 | 			return ret; | 
 | 1120 | 		} | 
 | 1121 | 	} | 
 | 1122 |  | 
 | 1123 | 	if (chip->fixes && chip->fixes->suspend) { | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1124 | 		ret = chip->fixes->suspend(chip); | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1125 | 		if (ret) { | 
 | 1126 | 			for (i = chip->num_slots - 1; i >= 0; i--) | 
 | 1127 | 				sdhci_runtime_resume_host(chip->slots[i]->host); | 
 | 1128 | 			return ret; | 
 | 1129 | 		} | 
 | 1130 | 	} | 
 | 1131 |  | 
 | 1132 | 	return 0; | 
 | 1133 | } | 
 | 1134 |  | 
 | 1135 | static int sdhci_pci_runtime_resume(struct device *dev) | 
 | 1136 | { | 
 | 1137 | 	struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); | 
 | 1138 | 	struct sdhci_pci_chip *chip; | 
 | 1139 | 	struct sdhci_pci_slot *slot; | 
 | 1140 | 	int i, ret; | 
 | 1141 |  | 
 | 1142 | 	chip = pci_get_drvdata(pdev); | 
 | 1143 | 	if (!chip) | 
 | 1144 | 		return 0; | 
 | 1145 |  | 
 | 1146 | 	if (chip->fixes && chip->fixes->resume) { | 
 | 1147 | 		ret = chip->fixes->resume(chip); | 
 | 1148 | 		if (ret) | 
 | 1149 | 			return ret; | 
 | 1150 | 	} | 
 | 1151 |  | 
 | 1152 | 	for (i = 0; i < chip->num_slots; i++) { | 
 | 1153 | 		slot = chip->slots[i]; | 
 | 1154 | 		if (!slot) | 
 | 1155 | 			continue; | 
 | 1156 |  | 
 | 1157 | 		ret = sdhci_runtime_resume_host(slot->host); | 
 | 1158 | 		if (ret) | 
 | 1159 | 			return ret; | 
 | 1160 | 	} | 
 | 1161 |  | 
 | 1162 | 	return 0; | 
 | 1163 | } | 
 | 1164 |  | 
 | 1165 | static int sdhci_pci_runtime_idle(struct device *dev) | 
 | 1166 | { | 
 | 1167 | 	return 0; | 
 | 1168 | } | 
 | 1169 |  | 
 | 1170 | #else | 
 | 1171 |  | 
 | 1172 | #define sdhci_pci_runtime_suspend	NULL | 
 | 1173 | #define sdhci_pci_runtime_resume	NULL | 
 | 1174 | #define sdhci_pci_runtime_idle		NULL | 
 | 1175 |  | 
 | 1176 | #endif | 
 | 1177 |  | 
 | 1178 | static const struct dev_pm_ops sdhci_pci_pm_ops = { | 
| Manuel Lauss | 29495aa | 2011-11-03 11:09:45 +0100 | [diff] [blame] | 1179 | 	.suspend = sdhci_pci_suspend, | 
 | 1180 | 	.resume = sdhci_pci_resume, | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1181 | 	.runtime_suspend = sdhci_pci_runtime_suspend, | 
 | 1182 | 	.runtime_resume = sdhci_pci_runtime_resume, | 
 | 1183 | 	.runtime_idle = sdhci_pci_runtime_idle, | 
 | 1184 | }; | 
 | 1185 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1186 | /*****************************************************************************\ | 
 | 1187 |  *                                                                           * | 
 | 1188 |  * Device probing/removal                                                    * | 
 | 1189 |  *                                                                           * | 
 | 1190 | \*****************************************************************************/ | 
 | 1191 |  | 
 | 1192 | static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( | 
 | 1193 | 	struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar) | 
 | 1194 | { | 
 | 1195 | 	struct sdhci_pci_slot *slot; | 
 | 1196 | 	struct sdhci_host *host; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1197 | 	int ret; | 
 | 1198 |  | 
 | 1199 | 	if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { | 
 | 1200 | 		dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); | 
 | 1201 | 		return ERR_PTR(-ENODEV); | 
 | 1202 | 	} | 
 | 1203 |  | 
 | 1204 | 	if (pci_resource_len(pdev, bar) != 0x100) { | 
 | 1205 | 		dev_err(&pdev->dev, "Invalid iomem size. You may " | 
 | 1206 | 			"experience problems.\n"); | 
 | 1207 | 	} | 
 | 1208 |  | 
 | 1209 | 	if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { | 
 | 1210 | 		dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n"); | 
 | 1211 | 		return ERR_PTR(-ENODEV); | 
 | 1212 | 	} | 
 | 1213 |  | 
 | 1214 | 	if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { | 
 | 1215 | 		dev_err(&pdev->dev, "Unknown interface. Aborting.\n"); | 
 | 1216 | 		return ERR_PTR(-ENODEV); | 
 | 1217 | 	} | 
 | 1218 |  | 
 | 1219 | 	host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot)); | 
 | 1220 | 	if (IS_ERR(host)) { | 
| Dan Carpenter | c60a32c | 2009-04-10 23:31:10 +0200 | [diff] [blame] | 1221 | 		dev_err(&pdev->dev, "cannot allocate host\n"); | 
| Julia Lawall | dc0fd7b | 2010-05-26 14:42:11 -0700 | [diff] [blame] | 1222 | 		return ERR_CAST(host); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1223 | 	} | 
 | 1224 |  | 
 | 1225 | 	slot = sdhci_priv(host); | 
 | 1226 |  | 
 | 1227 | 	slot->chip = chip; | 
 | 1228 | 	slot->host = host; | 
 | 1229 | 	slot->pci_bar = bar; | 
| Adrian Hunter | 0f20165 | 2011-08-29 16:42:13 +0300 | [diff] [blame] | 1230 | 	slot->rst_n_gpio = -EINVAL; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1231 |  | 
 | 1232 | 	host->hw_name = "PCI"; | 
 | 1233 | 	host->ops = &sdhci_pci_ops; | 
 | 1234 | 	host->quirks = chip->quirks; | 
 | 1235 |  | 
 | 1236 | 	host->irq = pdev->irq; | 
 | 1237 |  | 
 | 1238 | 	ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); | 
 | 1239 | 	if (ret) { | 
 | 1240 | 		dev_err(&pdev->dev, "cannot request region\n"); | 
| Dan Carpenter | c60a32c | 2009-04-10 23:31:10 +0200 | [diff] [blame] | 1241 | 		goto free; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1242 | 	} | 
 | 1243 |  | 
| Arjan van de Ven | 092f82e | 2008-09-28 16:15:56 -0700 | [diff] [blame] | 1244 | 	host->ioaddr = pci_ioremap_bar(pdev, bar); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1245 | 	if (!host->ioaddr) { | 
 | 1246 | 		dev_err(&pdev->dev, "failed to remap registers\n"); | 
| Chris Ball | 9fdcdbb | 2011-03-29 00:46:12 -0400 | [diff] [blame] | 1247 | 		ret = -ENOMEM; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1248 | 		goto release; | 
 | 1249 | 	} | 
 | 1250 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1251 | 	if (chip->fixes && chip->fixes->probe_slot) { | 
 | 1252 | 		ret = chip->fixes->probe_slot(slot); | 
 | 1253 | 		if (ret) | 
 | 1254 | 			goto unmap; | 
 | 1255 | 	} | 
 | 1256 |  | 
| Nicolas Pitre | 2f4cbb3 | 2010-03-05 13:43:32 -0800 | [diff] [blame] | 1257 | 	host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; | 
 | 1258 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1259 | 	ret = sdhci_add_host(host); | 
 | 1260 | 	if (ret) | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1261 | 		goto remove; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1262 |  | 
 | 1263 | 	return slot; | 
 | 1264 |  | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1265 | remove: | 
 | 1266 | 	if (chip->fixes && chip->fixes->remove_slot) | 
| Pierre Ossman | 1e72859 | 2008-04-16 19:13:13 +0200 | [diff] [blame] | 1267 | 		chip->fixes->remove_slot(slot, 0); | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1268 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1269 | unmap: | 
 | 1270 | 	iounmap(host->ioaddr); | 
 | 1271 |  | 
 | 1272 | release: | 
 | 1273 | 	pci_release_region(pdev, bar); | 
| Dan Carpenter | c60a32c | 2009-04-10 23:31:10 +0200 | [diff] [blame] | 1274 |  | 
 | 1275 | free: | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1276 | 	sdhci_free_host(host); | 
 | 1277 |  | 
 | 1278 | 	return ERR_PTR(ret); | 
 | 1279 | } | 
 | 1280 |  | 
 | 1281 | static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) | 
 | 1282 | { | 
| Pierre Ossman | 1e72859 | 2008-04-16 19:13:13 +0200 | [diff] [blame] | 1283 | 	int dead; | 
 | 1284 | 	u32 scratch; | 
 | 1285 |  | 
 | 1286 | 	dead = 0; | 
 | 1287 | 	scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); | 
 | 1288 | 	if (scratch == (u32)-1) | 
 | 1289 | 		dead = 1; | 
 | 1290 |  | 
 | 1291 | 	sdhci_remove_host(slot->host, dead); | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1292 |  | 
 | 1293 | 	if (slot->chip->fixes && slot->chip->fixes->remove_slot) | 
| Pierre Ossman | 1e72859 | 2008-04-16 19:13:13 +0200 | [diff] [blame] | 1294 | 		slot->chip->fixes->remove_slot(slot, dead); | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1295 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1296 | 	pci_release_region(slot->chip->pdev, slot->pci_bar); | 
| Pierre Ossman | 4489428 | 2008-04-04 19:36:59 +0200 | [diff] [blame] | 1297 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1298 | 	sdhci_free_host(slot->host); | 
 | 1299 | } | 
 | 1300 |  | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1301 | static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev) | 
 | 1302 | { | 
 | 1303 | 	pm_runtime_put_noidle(dev); | 
 | 1304 | 	pm_runtime_allow(dev); | 
 | 1305 | 	pm_runtime_set_autosuspend_delay(dev, 50); | 
 | 1306 | 	pm_runtime_use_autosuspend(dev); | 
 | 1307 | 	pm_suspend_ignore_children(dev, 1); | 
 | 1308 | } | 
 | 1309 |  | 
 | 1310 | static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev) | 
 | 1311 | { | 
 | 1312 | 	pm_runtime_forbid(dev); | 
 | 1313 | 	pm_runtime_get_noresume(dev); | 
 | 1314 | } | 
 | 1315 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1316 | static int __devinit sdhci_pci_probe(struct pci_dev *pdev, | 
 | 1317 | 				     const struct pci_device_id *ent) | 
 | 1318 | { | 
 | 1319 | 	struct sdhci_pci_chip *chip; | 
 | 1320 | 	struct sdhci_pci_slot *slot; | 
 | 1321 |  | 
| Sergei Shtylyov | cf5e23e | 2011-03-17 16:46:17 -0400 | [diff] [blame] | 1322 | 	u8 slots, first_bar; | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1323 | 	int ret, i; | 
 | 1324 |  | 
 | 1325 | 	BUG_ON(pdev == NULL); | 
 | 1326 | 	BUG_ON(ent == NULL); | 
 | 1327 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1328 | 	dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n", | 
| Sergei Shtylyov | cf5e23e | 2011-03-17 16:46:17 -0400 | [diff] [blame] | 1329 | 		 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1330 |  | 
 | 1331 | 	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); | 
 | 1332 | 	if (ret) | 
 | 1333 | 		return ret; | 
 | 1334 |  | 
 | 1335 | 	slots = PCI_SLOT_INFO_SLOTS(slots) + 1; | 
 | 1336 | 	dev_dbg(&pdev->dev, "found %d slot(s)\n", slots); | 
 | 1337 | 	if (slots == 0) | 
 | 1338 | 		return -ENODEV; | 
 | 1339 |  | 
 | 1340 | 	BUG_ON(slots > MAX_SLOTS); | 
 | 1341 |  | 
 | 1342 | 	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); | 
 | 1343 | 	if (ret) | 
 | 1344 | 		return ret; | 
 | 1345 |  | 
 | 1346 | 	first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; | 
 | 1347 |  | 
 | 1348 | 	if (first_bar > 5) { | 
 | 1349 | 		dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n"); | 
 | 1350 | 		return -ENODEV; | 
 | 1351 | 	} | 
 | 1352 |  | 
 | 1353 | 	ret = pci_enable_device(pdev); | 
 | 1354 | 	if (ret) | 
 | 1355 | 		return ret; | 
 | 1356 |  | 
 | 1357 | 	chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL); | 
 | 1358 | 	if (!chip) { | 
 | 1359 | 		ret = -ENOMEM; | 
 | 1360 | 		goto err; | 
 | 1361 | 	} | 
 | 1362 |  | 
 | 1363 | 	chip->pdev = pdev; | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1364 | 	chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 1365 | 	if (chip->fixes) { | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 1366 | 		chip->quirks = chip->fixes->quirks; | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 1367 | 		chip->allow_runtime_pm = chip->fixes->allow_runtime_pm; | 
 | 1368 | 	} | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1369 | 	chip->num_slots = slots; | 
 | 1370 |  | 
 | 1371 | 	pci_set_drvdata(pdev, chip); | 
 | 1372 |  | 
| Pierre Ossman | 2260640 | 2008-03-23 19:33:23 +0100 | [diff] [blame] | 1373 | 	if (chip->fixes && chip->fixes->probe) { | 
 | 1374 | 		ret = chip->fixes->probe(chip); | 
 | 1375 | 		if (ret) | 
 | 1376 | 			goto free; | 
 | 1377 | 	} | 
 | 1378 |  | 
| Alan Cox | 225d85f | 2010-10-04 15:24:21 +0100 | [diff] [blame] | 1379 | 	slots = chip->num_slots;	/* Quirk may have changed this */ | 
 | 1380 |  | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1381 | 	for (i = 0; i < slots; i++) { | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1382 | 		slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); | 
 | 1383 | 		if (IS_ERR(slot)) { | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1384 | 			for (i--; i >= 0; i--) | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1385 | 				sdhci_pci_remove_slot(chip->slots[i]); | 
 | 1386 | 			ret = PTR_ERR(slot); | 
 | 1387 | 			goto free; | 
 | 1388 | 		} | 
 | 1389 |  | 
 | 1390 | 		chip->slots[i] = slot; | 
 | 1391 | 	} | 
 | 1392 |  | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 1393 | 	if (chip->allow_runtime_pm) | 
 | 1394 | 		sdhci_pci_runtime_pm_allow(&pdev->dev); | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1395 |  | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1396 | 	return 0; | 
 | 1397 |  | 
 | 1398 | free: | 
 | 1399 | 	pci_set_drvdata(pdev, NULL); | 
 | 1400 | 	kfree(chip); | 
 | 1401 |  | 
 | 1402 | err: | 
 | 1403 | 	pci_disable_device(pdev); | 
 | 1404 | 	return ret; | 
 | 1405 | } | 
 | 1406 |  | 
 | 1407 | static void __devexit sdhci_pci_remove(struct pci_dev *pdev) | 
 | 1408 | { | 
 | 1409 | 	int i; | 
 | 1410 | 	struct sdhci_pci_chip *chip; | 
 | 1411 |  | 
 | 1412 | 	chip = pci_get_drvdata(pdev); | 
 | 1413 |  | 
 | 1414 | 	if (chip) { | 
| Adrian Hunter | c43fd77 | 2011-10-17 10:52:44 +0300 | [diff] [blame] | 1415 | 		if (chip->allow_runtime_pm) | 
 | 1416 | 			sdhci_pci_runtime_pm_forbid(&pdev->dev); | 
 | 1417 |  | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1418 | 		for (i = 0; i < chip->num_slots; i++) | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1419 | 			sdhci_pci_remove_slot(chip->slots[i]); | 
 | 1420 |  | 
 | 1421 | 		pci_set_drvdata(pdev, NULL); | 
 | 1422 | 		kfree(chip); | 
 | 1423 | 	} | 
 | 1424 |  | 
 | 1425 | 	pci_disable_device(pdev); | 
 | 1426 | } | 
 | 1427 |  | 
 | 1428 | static struct pci_driver sdhci_driver = { | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1429 | 	.name =		"sdhci-pci", | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1430 | 	.id_table =	pci_ids, | 
| Ameya Palande | b177bc9 | 2011-04-05 21:13:13 +0300 | [diff] [blame] | 1431 | 	.probe =	sdhci_pci_probe, | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1432 | 	.remove =	__devexit_p(sdhci_pci_remove), | 
| Adrian Hunter | 66fd8ad | 2011-10-03 15:33:34 +0300 | [diff] [blame] | 1433 | 	.driver =	{ | 
 | 1434 | 		.pm =   &sdhci_pci_pm_ops | 
 | 1435 | 	}, | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1436 | }; | 
 | 1437 |  | 
 | 1438 | /*****************************************************************************\ | 
 | 1439 |  *                                                                           * | 
 | 1440 |  * Driver init/exit                                                          * | 
 | 1441 |  *                                                                           * | 
 | 1442 | \*****************************************************************************/ | 
 | 1443 |  | 
 | 1444 | static int __init sdhci_drv_init(void) | 
 | 1445 | { | 
 | 1446 | 	return pci_register_driver(&sdhci_driver); | 
 | 1447 | } | 
 | 1448 |  | 
 | 1449 | static void __exit sdhci_drv_exit(void) | 
 | 1450 | { | 
 | 1451 | 	pci_unregister_driver(&sdhci_driver); | 
 | 1452 | } | 
 | 1453 |  | 
 | 1454 | module_init(sdhci_drv_init); | 
 | 1455 | module_exit(sdhci_drv_exit); | 
 | 1456 |  | 
| Pierre Ossman | 32710e8 | 2009-04-08 20:14:54 +0200 | [diff] [blame] | 1457 | MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>"); | 
| Pierre Ossman | b8c86fc | 2008-03-18 17:35:49 +0100 | [diff] [blame] | 1458 | MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); | 
 | 1459 | MODULE_LICENSE("GPL"); |