xref: /openbmc/linux/drivers/mmc/host/sdhci-acpi.c (revision f07b7952)
1c4e05037SAdrian Hunter /*
2c4e05037SAdrian Hunter  * Secure Digital Host Controller Interface ACPI driver.
3c4e05037SAdrian Hunter  *
4c4e05037SAdrian Hunter  * Copyright (c) 2012, Intel Corporation.
5c4e05037SAdrian Hunter  *
6c4e05037SAdrian Hunter  * This program is free software; you can redistribute it and/or modify it
7c4e05037SAdrian Hunter  * under the terms and conditions of the GNU General Public License,
8c4e05037SAdrian Hunter  * version 2, as published by the Free Software Foundation.
9c4e05037SAdrian Hunter  *
10c4e05037SAdrian Hunter  * This program is distributed in the hope it will be useful, but WITHOUT
11c4e05037SAdrian Hunter  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12c4e05037SAdrian Hunter  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13c4e05037SAdrian Hunter  * more details.
14c4e05037SAdrian Hunter  *
15c4e05037SAdrian Hunter  * You should have received a copy of the GNU General Public License along with
16c4e05037SAdrian Hunter  * this program; if not, write to the Free Software Foundation, Inc.,
17c4e05037SAdrian Hunter  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18c4e05037SAdrian Hunter  *
19c4e05037SAdrian Hunter  */
20c4e05037SAdrian Hunter 
21c4e05037SAdrian Hunter #include <linux/init.h>
22c4e05037SAdrian Hunter #include <linux/export.h>
23c4e05037SAdrian Hunter #include <linux/module.h>
24c4e05037SAdrian Hunter #include <linux/device.h>
25c4e05037SAdrian Hunter #include <linux/platform_device.h>
26c4e05037SAdrian Hunter #include <linux/ioport.h>
27c4e05037SAdrian Hunter #include <linux/io.h>
28c4e05037SAdrian Hunter #include <linux/dma-mapping.h>
29c4e05037SAdrian Hunter #include <linux/compiler.h>
30c4e05037SAdrian Hunter #include <linux/stddef.h>
31c4e05037SAdrian Hunter #include <linux/bitops.h>
32c4e05037SAdrian Hunter #include <linux/types.h>
33c4e05037SAdrian Hunter #include <linux/err.h>
34c4e05037SAdrian Hunter #include <linux/interrupt.h>
35c4e05037SAdrian Hunter #include <linux/acpi.h>
36c4e05037SAdrian Hunter #include <linux/pm.h>
37c4e05037SAdrian Hunter #include <linux/pm_runtime.h>
38b04fa064SAdrian Hunter #include <linux/delay.h>
39c4e05037SAdrian Hunter 
40c4e05037SAdrian Hunter #include <linux/mmc/host.h>
41c4e05037SAdrian Hunter #include <linux/mmc/pm.h>
424fd4409cSAdrian Hunter #include <linux/mmc/slot-gpio.h>
43c4e05037SAdrian Hunter 
446e1c7d61SAdrian Hunter #ifdef CONFIG_X86
456e1c7d61SAdrian Hunter #include <asm/cpu_device_id.h>
468ba4cb53SDave Hansen #include <asm/intel-family.h>
476e1c7d61SAdrian Hunter #include <asm/iosf_mbi.h>
4817753d16SAdrian Hunter #include <linux/pci.h>
496e1c7d61SAdrian Hunter #endif
506e1c7d61SAdrian Hunter 
51c4e05037SAdrian Hunter #include "sdhci.h"
52c4e05037SAdrian Hunter 
53c4e05037SAdrian Hunter enum {
54c4e05037SAdrian Hunter 	SDHCI_ACPI_SD_CD		= BIT(0),
55c4e05037SAdrian Hunter 	SDHCI_ACPI_RUNTIME_PM		= BIT(1),
564fd4409cSAdrian Hunter 	SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL	= BIT(2),
57c4e05037SAdrian Hunter };
58c4e05037SAdrian Hunter 
59c4e05037SAdrian Hunter struct sdhci_acpi_chip {
60c4e05037SAdrian Hunter 	const struct	sdhci_ops *ops;
61c4e05037SAdrian Hunter 	unsigned int	quirks;
62c4e05037SAdrian Hunter 	unsigned int	quirks2;
63c4e05037SAdrian Hunter 	unsigned long	caps;
64c4e05037SAdrian Hunter 	unsigned int	caps2;
65c4e05037SAdrian Hunter 	mmc_pm_flag_t	pm_caps;
66c4e05037SAdrian Hunter };
67c4e05037SAdrian Hunter 
68c4e05037SAdrian Hunter struct sdhci_acpi_slot {
69c4e05037SAdrian Hunter 	const struct	sdhci_acpi_chip *chip;
70c4e05037SAdrian Hunter 	unsigned int	quirks;
71c4e05037SAdrian Hunter 	unsigned int	quirks2;
72c4e05037SAdrian Hunter 	unsigned long	caps;
73c4e05037SAdrian Hunter 	unsigned int	caps2;
74c4e05037SAdrian Hunter 	mmc_pm_flag_t	pm_caps;
75c4e05037SAdrian Hunter 	unsigned int	flags;
76f07b7952SAdrian Hunter 	size_t		priv_size;
777dafca83SAdrian Hunter 	int (*probe_slot)(struct platform_device *, const char *, const char *);
78578b36b6SGao, Yunpeng 	int (*remove_slot)(struct platform_device *);
79c4e05037SAdrian Hunter };
80c4e05037SAdrian Hunter 
81c4e05037SAdrian Hunter struct sdhci_acpi_host {
82c4e05037SAdrian Hunter 	struct sdhci_host		*host;
83c4e05037SAdrian Hunter 	const struct sdhci_acpi_slot	*slot;
84c4e05037SAdrian Hunter 	struct platform_device		*pdev;
85c4e05037SAdrian Hunter 	bool				use_runtime_pm;
86f07b7952SAdrian Hunter 	unsigned long			private[0] ____cacheline_aligned;
87c4e05037SAdrian Hunter };
88c4e05037SAdrian Hunter 
89f07b7952SAdrian Hunter static inline void *sdhci_acpi_priv(struct sdhci_acpi_host *c)
90f07b7952SAdrian Hunter {
91f07b7952SAdrian Hunter 	return (void *)c->private;
92f07b7952SAdrian Hunter }
93f07b7952SAdrian Hunter 
94c4e05037SAdrian Hunter static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
95c4e05037SAdrian Hunter {
96c4e05037SAdrian Hunter 	return c->slot && (c->slot->flags & flag);
97c4e05037SAdrian Hunter }
98c4e05037SAdrian Hunter 
99b04fa064SAdrian Hunter static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
100b04fa064SAdrian Hunter {
101b04fa064SAdrian Hunter 	u8 reg;
102b04fa064SAdrian Hunter 
103b04fa064SAdrian Hunter 	reg = sdhci_readb(host, SDHCI_POWER_CONTROL);
104b04fa064SAdrian Hunter 	reg |= 0x10;
105b04fa064SAdrian Hunter 	sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
106b04fa064SAdrian Hunter 	/* For eMMC, minimum is 1us but give it 9us for good measure */
107b04fa064SAdrian Hunter 	udelay(9);
108b04fa064SAdrian Hunter 	reg &= ~0x10;
109b04fa064SAdrian Hunter 	sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
110b04fa064SAdrian Hunter 	/* For eMMC, minimum is 200us but give it 300us for good measure */
111b04fa064SAdrian Hunter 	usleep_range(300, 1000);
112b04fa064SAdrian Hunter }
113b04fa064SAdrian Hunter 
114c4e05037SAdrian Hunter static const struct sdhci_ops sdhci_acpi_ops_dflt = {
1151771059cSRussell King 	.set_clock = sdhci_set_clock,
1162317f56cSRussell King 	.set_bus_width = sdhci_set_bus_width,
11703231f9bSRussell King 	.reset = sdhci_reset,
11896d7b78cSRussell King 	.set_uhs_signaling = sdhci_set_uhs_signaling,
119c4e05037SAdrian Hunter };
120c4e05037SAdrian Hunter 
121b04fa064SAdrian Hunter static const struct sdhci_ops sdhci_acpi_ops_int = {
1221771059cSRussell King 	.set_clock = sdhci_set_clock,
1232317f56cSRussell King 	.set_bus_width = sdhci_set_bus_width,
12403231f9bSRussell King 	.reset = sdhci_reset,
12596d7b78cSRussell King 	.set_uhs_signaling = sdhci_set_uhs_signaling,
126b04fa064SAdrian Hunter 	.hw_reset   = sdhci_acpi_int_hw_reset,
127b04fa064SAdrian Hunter };
128b04fa064SAdrian Hunter 
129b04fa064SAdrian Hunter static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
130b04fa064SAdrian Hunter 	.ops = &sdhci_acpi_ops_int,
131b04fa064SAdrian Hunter };
132b04fa064SAdrian Hunter 
1336e1c7d61SAdrian Hunter #ifdef CONFIG_X86
1346e1c7d61SAdrian Hunter 
1356e1c7d61SAdrian Hunter static bool sdhci_acpi_byt(void)
1366e1c7d61SAdrian Hunter {
1376e1c7d61SAdrian Hunter 	static const struct x86_cpu_id byt[] = {
1388ba4cb53SDave Hansen 		{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 },
1396e1c7d61SAdrian Hunter 		{}
1406e1c7d61SAdrian Hunter 	};
1416e1c7d61SAdrian Hunter 
1426e1c7d61SAdrian Hunter 	return x86_match_cpu(byt);
1436e1c7d61SAdrian Hunter }
1446e1c7d61SAdrian Hunter 
14517753d16SAdrian Hunter static bool sdhci_acpi_cht(void)
14617753d16SAdrian Hunter {
14717753d16SAdrian Hunter 	static const struct x86_cpu_id cht[] = {
14817753d16SAdrian Hunter 		{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT },
14917753d16SAdrian Hunter 		{}
15017753d16SAdrian Hunter 	};
15117753d16SAdrian Hunter 
15217753d16SAdrian Hunter 	return x86_match_cpu(cht);
15317753d16SAdrian Hunter }
15417753d16SAdrian Hunter 
1556e1c7d61SAdrian Hunter #define BYT_IOSF_SCCEP			0x63
1566e1c7d61SAdrian Hunter #define BYT_IOSF_OCP_NETCTRL0		0x1078
1576e1c7d61SAdrian Hunter #define BYT_IOSF_OCP_TIMEOUT_BASE	GENMASK(10, 8)
1586e1c7d61SAdrian Hunter 
1596e1c7d61SAdrian Hunter static void sdhci_acpi_byt_setting(struct device *dev)
1606e1c7d61SAdrian Hunter {
1616e1c7d61SAdrian Hunter 	u32 val = 0;
1626e1c7d61SAdrian Hunter 
1636e1c7d61SAdrian Hunter 	if (!sdhci_acpi_byt())
1646e1c7d61SAdrian Hunter 		return;
1656e1c7d61SAdrian Hunter 
1666e1c7d61SAdrian Hunter 	if (iosf_mbi_read(BYT_IOSF_SCCEP, MBI_CR_READ, BYT_IOSF_OCP_NETCTRL0,
1676e1c7d61SAdrian Hunter 			  &val)) {
1686e1c7d61SAdrian Hunter 		dev_err(dev, "%s read error\n", __func__);
1696e1c7d61SAdrian Hunter 		return;
1706e1c7d61SAdrian Hunter 	}
1716e1c7d61SAdrian Hunter 
1726e1c7d61SAdrian Hunter 	if (!(val & BYT_IOSF_OCP_TIMEOUT_BASE))
1736e1c7d61SAdrian Hunter 		return;
1746e1c7d61SAdrian Hunter 
1756e1c7d61SAdrian Hunter 	val &= ~BYT_IOSF_OCP_TIMEOUT_BASE;
1766e1c7d61SAdrian Hunter 
1776e1c7d61SAdrian Hunter 	if (iosf_mbi_write(BYT_IOSF_SCCEP, MBI_CR_WRITE, BYT_IOSF_OCP_NETCTRL0,
1786e1c7d61SAdrian Hunter 			   val)) {
1796e1c7d61SAdrian Hunter 		dev_err(dev, "%s write error\n", __func__);
1806e1c7d61SAdrian Hunter 		return;
1816e1c7d61SAdrian Hunter 	}
1826e1c7d61SAdrian Hunter 
1836e1c7d61SAdrian Hunter 	dev_dbg(dev, "%s completed\n", __func__);
1846e1c7d61SAdrian Hunter }
1856e1c7d61SAdrian Hunter 
1866e1c7d61SAdrian Hunter static bool sdhci_acpi_byt_defer(struct device *dev)
1876e1c7d61SAdrian Hunter {
1886e1c7d61SAdrian Hunter 	if (!sdhci_acpi_byt())
1896e1c7d61SAdrian Hunter 		return false;
1906e1c7d61SAdrian Hunter 
1916e1c7d61SAdrian Hunter 	if (!iosf_mbi_available())
1926e1c7d61SAdrian Hunter 		return true;
1936e1c7d61SAdrian Hunter 
1946e1c7d61SAdrian Hunter 	sdhci_acpi_byt_setting(dev);
1956e1c7d61SAdrian Hunter 
1966e1c7d61SAdrian Hunter 	return false;
1976e1c7d61SAdrian Hunter }
1986e1c7d61SAdrian Hunter 
19917753d16SAdrian Hunter static bool sdhci_acpi_cht_pci_wifi(unsigned int vendor, unsigned int device,
20017753d16SAdrian Hunter 				    unsigned int slot, unsigned int parent_slot)
20117753d16SAdrian Hunter {
20217753d16SAdrian Hunter 	struct pci_dev *dev, *parent, *from = NULL;
20317753d16SAdrian Hunter 
20417753d16SAdrian Hunter 	while (1) {
20517753d16SAdrian Hunter 		dev = pci_get_device(vendor, device, from);
20617753d16SAdrian Hunter 		pci_dev_put(from);
20717753d16SAdrian Hunter 		if (!dev)
20817753d16SAdrian Hunter 			break;
20917753d16SAdrian Hunter 		parent = pci_upstream_bridge(dev);
21017753d16SAdrian Hunter 		if (ACPI_COMPANION(&dev->dev) && PCI_SLOT(dev->devfn) == slot &&
21117753d16SAdrian Hunter 		    parent && PCI_SLOT(parent->devfn) == parent_slot &&
21217753d16SAdrian Hunter 		    !pci_upstream_bridge(parent)) {
21317753d16SAdrian Hunter 			pci_dev_put(dev);
21417753d16SAdrian Hunter 			return true;
21517753d16SAdrian Hunter 		}
21617753d16SAdrian Hunter 		from = dev;
21717753d16SAdrian Hunter 	}
21817753d16SAdrian Hunter 
21917753d16SAdrian Hunter 	return false;
22017753d16SAdrian Hunter }
22117753d16SAdrian Hunter 
22217753d16SAdrian Hunter /*
22317753d16SAdrian Hunter  * GPDwin uses PCI wifi which conflicts with SDIO's use of
22417753d16SAdrian Hunter  * acpi_device_fix_up_power() on child device nodes. Identifying GPDwin is
22517753d16SAdrian Hunter  * problematic, but since SDIO is only used for wifi, the presence of the PCI
22617753d16SAdrian Hunter  * wifi card in the expected slot with an ACPI companion node, is used to
22717753d16SAdrian Hunter  * indicate that acpi_device_fix_up_power() should be avoided.
22817753d16SAdrian Hunter  */
22917753d16SAdrian Hunter static inline bool sdhci_acpi_no_fixup_child_power(const char *hid,
23017753d16SAdrian Hunter 						   const char *uid)
23117753d16SAdrian Hunter {
23217753d16SAdrian Hunter 	return sdhci_acpi_cht() &&
23317753d16SAdrian Hunter 	       !strcmp(hid, "80860F14") &&
23417753d16SAdrian Hunter 	       !strcmp(uid, "2") &&
23517753d16SAdrian Hunter 	       sdhci_acpi_cht_pci_wifi(0x14e4, 0x43ec, 0, 28);
23617753d16SAdrian Hunter }
23717753d16SAdrian Hunter 
2386e1c7d61SAdrian Hunter #else
2396e1c7d61SAdrian Hunter 
2406e1c7d61SAdrian Hunter static inline void sdhci_acpi_byt_setting(struct device *dev)
2416e1c7d61SAdrian Hunter {
2426e1c7d61SAdrian Hunter }
2436e1c7d61SAdrian Hunter 
2446e1c7d61SAdrian Hunter static inline bool sdhci_acpi_byt_defer(struct device *dev)
2456e1c7d61SAdrian Hunter {
2466e1c7d61SAdrian Hunter 	return false;
2476e1c7d61SAdrian Hunter }
2486e1c7d61SAdrian Hunter 
24917753d16SAdrian Hunter static inline bool sdhci_acpi_no_fixup_child_power(const char *hid,
25017753d16SAdrian Hunter 						   const char *uid)
25117753d16SAdrian Hunter {
25217753d16SAdrian Hunter 	return false;
25317753d16SAdrian Hunter }
25417753d16SAdrian Hunter 
2556e1c7d61SAdrian Hunter #endif
2566e1c7d61SAdrian Hunter 
2576a645dd8SAdrian Hunter static int bxt_get_cd(struct mmc_host *mmc)
2586a645dd8SAdrian Hunter {
2596a645dd8SAdrian Hunter 	int gpio_cd = mmc_gpio_get_cd(mmc);
2606a645dd8SAdrian Hunter 	struct sdhci_host *host = mmc_priv(mmc);
2616a645dd8SAdrian Hunter 	unsigned long flags;
2626a645dd8SAdrian Hunter 	int ret = 0;
2636a645dd8SAdrian Hunter 
2646a645dd8SAdrian Hunter 	if (!gpio_cd)
2656a645dd8SAdrian Hunter 		return 0;
2666a645dd8SAdrian Hunter 
2676a645dd8SAdrian Hunter 	spin_lock_irqsave(&host->lock, flags);
2686a645dd8SAdrian Hunter 
2696a645dd8SAdrian Hunter 	if (host->flags & SDHCI_DEVICE_DEAD)
2706a645dd8SAdrian Hunter 		goto out;
2716a645dd8SAdrian Hunter 
2726a645dd8SAdrian Hunter 	ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
2736a645dd8SAdrian Hunter out:
2746a645dd8SAdrian Hunter 	spin_unlock_irqrestore(&host->lock, flags);
2756a645dd8SAdrian Hunter 
2766a645dd8SAdrian Hunter 	return ret;
2776a645dd8SAdrian Hunter }
2786a645dd8SAdrian Hunter 
279159cd328SAdrian Hunter static int intel_probe_slot(struct platform_device *pdev, const char *hid,
280159cd328SAdrian Hunter 			    const char *uid)
281578b36b6SGao, Yunpeng {
282578b36b6SGao, Yunpeng 	struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
283159cd328SAdrian Hunter 	struct sdhci_host *host = c->host;
284578b36b6SGao, Yunpeng 
2858024379eSAdrian Hunter 	if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
2868024379eSAdrian Hunter 	    sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
2878024379eSAdrian Hunter 	    sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807)
2888024379eSAdrian Hunter 		host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
2898024379eSAdrian Hunter 
290d3e97407SAzhar Shaikh 	if (hid && !strcmp(hid, "80865ACA"))
2916a645dd8SAdrian Hunter 		host->mmc_host_ops.get_cd = bxt_get_cd;
2926a645dd8SAdrian Hunter 
293578b36b6SGao, Yunpeng 	return 0;
294578b36b6SGao, Yunpeng }
295578b36b6SGao, Yunpeng 
29607a58883SAdrian Hunter static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
297b04fa064SAdrian Hunter 	.chip    = &sdhci_acpi_chip_int,
298f25c3372SMaurice Petallo 	.caps    = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
2999d65cb88SAdrian Hunter 		   MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
300c80f275fSAdrian Hunter 		   MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY,
30107a58883SAdrian Hunter 	.flags   = SDHCI_ACPI_RUNTIME_PM,
302e1f5633aSAdrian Hunter 	.quirks  = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
303e839b134SAdrian Hunter 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
304e839b134SAdrian Hunter 		   SDHCI_QUIRK2_STOP_WITH_TC |
305e839b134SAdrian Hunter 		   SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
306159cd328SAdrian Hunter 	.probe_slot	= intel_probe_slot,
30707a58883SAdrian Hunter };
30807a58883SAdrian Hunter 
309e5571397SAdrian Hunter static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
310e1f5633aSAdrian Hunter 	.quirks  = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
311e1f5633aSAdrian Hunter 		   SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
312e5571397SAdrian Hunter 	.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
3139d65cb88SAdrian Hunter 	.caps    = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
314265984b3SAdrian Hunter 		   MMC_CAP_WAIT_WHILE_BUSY,
315e5571397SAdrian Hunter 	.flags   = SDHCI_ACPI_RUNTIME_PM,
316e5571397SAdrian Hunter 	.pm_caps = MMC_PM_KEEP_POWER,
317159cd328SAdrian Hunter 	.probe_slot	= intel_probe_slot,
318e5571397SAdrian Hunter };
319e5571397SAdrian Hunter 
32007a58883SAdrian Hunter static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
3214fd4409cSAdrian Hunter 	.flags   = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
3224fd4409cSAdrian Hunter 		   SDHCI_ACPI_RUNTIME_PM,
323e1f5633aSAdrian Hunter 	.quirks  = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
324934e31b9SAdrian Hunter 	.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
325934e31b9SAdrian Hunter 		   SDHCI_QUIRK2_STOP_WITH_TC,
326d3e97407SAzhar Shaikh 	.caps    = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM,
327159cd328SAdrian Hunter 	.probe_slot	= intel_probe_slot,
32807a58883SAdrian Hunter };
32907a58883SAdrian Hunter 
33070cce2afSPhilip Elcan static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = {
33170cce2afSPhilip Elcan 	.quirks  = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
33270cce2afSPhilip Elcan 	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
33370cce2afSPhilip Elcan 	.caps    = MMC_CAP_NONREMOVABLE,
33470cce2afSPhilip Elcan };
33570cce2afSPhilip Elcan 
33670cce2afSPhilip Elcan static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd = {
33770cce2afSPhilip Elcan 	.quirks  = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
33870cce2afSPhilip Elcan 	.caps    = MMC_CAP_NONREMOVABLE,
33970cce2afSPhilip Elcan };
34070cce2afSPhilip Elcan 
34107a58883SAdrian Hunter struct sdhci_acpi_uid_slot {
34207a58883SAdrian Hunter 	const char *hid;
34307a58883SAdrian Hunter 	const char *uid;
34407a58883SAdrian Hunter 	const struct sdhci_acpi_slot *slot;
34507a58883SAdrian Hunter };
34607a58883SAdrian Hunter 
34707a58883SAdrian Hunter static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
348e839b134SAdrian Hunter 	{ "80865ACA", NULL, &sdhci_acpi_slot_int_sd },
349e839b134SAdrian Hunter 	{ "80865ACC", NULL, &sdhci_acpi_slot_int_emmc },
350e839b134SAdrian Hunter 	{ "80865AD0", NULL, &sdhci_acpi_slot_int_sdio },
35107a58883SAdrian Hunter 	{ "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },
352db52d4f8SDaniel Drake 	{ "80860F14" , "2" , &sdhci_acpi_slot_int_sdio },
35307a58883SAdrian Hunter 	{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd   },
354aad95dc4SAdrian Hunter 	{ "80860F16" , NULL, &sdhci_acpi_slot_int_sd   },
35507a58883SAdrian Hunter 	{ "INT33BB"  , "2" , &sdhci_acpi_slot_int_sdio },
3567147eaf3SAdrian Hunter 	{ "INT33BB"  , "3" , &sdhci_acpi_slot_int_sd },
35707a58883SAdrian Hunter 	{ "INT33C6"  , NULL, &sdhci_acpi_slot_int_sdio },
35807c001c1SMika Westerberg 	{ "INT3436"  , NULL, &sdhci_acpi_slot_int_sdio },
359d0ed8e6bSAdrian Hunter 	{ "INT344D"  , NULL, &sdhci_acpi_slot_int_sdio },
3600cd2f044SMichele Curti 	{ "PNP0FFF"  , "3" , &sdhci_acpi_slot_int_sd   },
36107a58883SAdrian Hunter 	{ "PNP0D40"  },
36270cce2afSPhilip Elcan 	{ "QCOM8051", NULL, &sdhci_acpi_slot_qcom_sd_3v },
36370cce2afSPhilip Elcan 	{ "QCOM8052", NULL, &sdhci_acpi_slot_qcom_sd },
36407a58883SAdrian Hunter 	{ },
36507a58883SAdrian Hunter };
36607a58883SAdrian Hunter 
367c4e05037SAdrian Hunter static const struct acpi_device_id sdhci_acpi_ids[] = {
368e839b134SAdrian Hunter 	{ "80865ACA" },
369e839b134SAdrian Hunter 	{ "80865ACC" },
370e839b134SAdrian Hunter 	{ "80865AD0" },
37107a58883SAdrian Hunter 	{ "80860F14" },
372aad95dc4SAdrian Hunter 	{ "80860F16" },
37307a58883SAdrian Hunter 	{ "INT33BB"  },
37407a58883SAdrian Hunter 	{ "INT33C6"  },
37507c001c1SMika Westerberg 	{ "INT3436"  },
376d0ed8e6bSAdrian Hunter 	{ "INT344D"  },
377c4e05037SAdrian Hunter 	{ "PNP0D40"  },
37870cce2afSPhilip Elcan 	{ "QCOM8051" },
37970cce2afSPhilip Elcan 	{ "QCOM8052" },
380c4e05037SAdrian Hunter 	{ },
381c4e05037SAdrian Hunter };
382c4e05037SAdrian Hunter MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
383c4e05037SAdrian Hunter 
3843db35251SAdrian Hunter static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid,
38507a58883SAdrian Hunter 							 const char *uid)
386c4e05037SAdrian Hunter {
38707a58883SAdrian Hunter 	const struct sdhci_acpi_uid_slot *u;
388c4e05037SAdrian Hunter 
38907a58883SAdrian Hunter 	for (u = sdhci_acpi_uids; u->hid; u++) {
39007a58883SAdrian Hunter 		if (strcmp(u->hid, hid))
39107a58883SAdrian Hunter 			continue;
39207a58883SAdrian Hunter 		if (!u->uid)
39307a58883SAdrian Hunter 			return u->slot;
39407a58883SAdrian Hunter 		if (uid && !strcmp(u->uid, uid))
39507a58883SAdrian Hunter 			return u->slot;
39607a58883SAdrian Hunter 	}
397c4e05037SAdrian Hunter 	return NULL;
398c4e05037SAdrian Hunter }
399c4e05037SAdrian Hunter 
4004e608e4eSGreg Kroah-Hartman static int sdhci_acpi_probe(struct platform_device *pdev)
401c4e05037SAdrian Hunter {
402c4e05037SAdrian Hunter 	struct device *dev = &pdev->dev;
403f07b7952SAdrian Hunter 	const struct sdhci_acpi_slot *slot;
404e5bbf307SAdrian Hunter 	struct acpi_device *device, *child;
405c4e05037SAdrian Hunter 	struct sdhci_acpi_host *c;
406c4e05037SAdrian Hunter 	struct sdhci_host *host;
407c4e05037SAdrian Hunter 	struct resource *iomem;
408c4e05037SAdrian Hunter 	resource_size_t len;
409f07b7952SAdrian Hunter 	size_t priv_size;
410c4e05037SAdrian Hunter 	const char *hid;
4113db35251SAdrian Hunter 	const char *uid;
41287875655SMika Westerberg 	int err;
413c4e05037SAdrian Hunter 
414cd25c7beSAndy Shevchenko 	device = ACPI_COMPANION(dev);
415cd25c7beSAndy Shevchenko 	if (!device)
416c4e05037SAdrian Hunter 		return -ENODEV;
417c4e05037SAdrian Hunter 
41817753d16SAdrian Hunter 	hid = acpi_device_hid(device);
419a2038497SAdrian Hunter 	uid = acpi_device_uid(device);
42017753d16SAdrian Hunter 
421f07b7952SAdrian Hunter 	slot = sdhci_acpi_get_slot(hid, uid);
422f07b7952SAdrian Hunter 
423e5bbf307SAdrian Hunter 	/* Power on the SDHCI controller and its children */
424e5bbf307SAdrian Hunter 	acpi_device_fix_up_power(device);
42517753d16SAdrian Hunter 	if (!sdhci_acpi_no_fixup_child_power(hid, uid)) {
426e5bbf307SAdrian Hunter 		list_for_each_entry(child, &device->children, node)
427e1d070c3SHans de Goede 			if (child->status.present && child->status.enabled)
428e5bbf307SAdrian Hunter 				acpi_device_fix_up_power(child);
42917753d16SAdrian Hunter 	}
430e5bbf307SAdrian Hunter 
4316e1c7d61SAdrian Hunter 	if (sdhci_acpi_byt_defer(dev))
4326e1c7d61SAdrian Hunter 		return -EPROBE_DEFER;
4336e1c7d61SAdrian Hunter 
434c4e05037SAdrian Hunter 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
435c4e05037SAdrian Hunter 	if (!iomem)
436c4e05037SAdrian Hunter 		return -ENOMEM;
437c4e05037SAdrian Hunter 
438c4e05037SAdrian Hunter 	len = resource_size(iomem);
439c4e05037SAdrian Hunter 	if (len < 0x100)
440c4e05037SAdrian Hunter 		dev_err(dev, "Invalid iomem size!\n");
441c4e05037SAdrian Hunter 
442c4e05037SAdrian Hunter 	if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev)))
443c4e05037SAdrian Hunter 		return -ENOMEM;
444c4e05037SAdrian Hunter 
445f07b7952SAdrian Hunter 	priv_size = slot ? slot->priv_size : 0;
446f07b7952SAdrian Hunter 	host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host) + priv_size);
447c4e05037SAdrian Hunter 	if (IS_ERR(host))
448c4e05037SAdrian Hunter 		return PTR_ERR(host);
449c4e05037SAdrian Hunter 
450c4e05037SAdrian Hunter 	c = sdhci_priv(host);
451c4e05037SAdrian Hunter 	c->host = host;
452f07b7952SAdrian Hunter 	c->slot = slot;
453c4e05037SAdrian Hunter 	c->pdev = pdev;
454c4e05037SAdrian Hunter 	c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);
455c4e05037SAdrian Hunter 
456c4e05037SAdrian Hunter 	platform_set_drvdata(pdev, c);
457c4e05037SAdrian Hunter 
458c4e05037SAdrian Hunter 	host->hw_name	= "ACPI";
459c4e05037SAdrian Hunter 	host->ops	= &sdhci_acpi_ops_dflt;
460c4e05037SAdrian Hunter 	host->irq	= platform_get_irq(pdev, 0);
461c4e05037SAdrian Hunter 
462c4e05037SAdrian Hunter 	host->ioaddr = devm_ioremap_nocache(dev, iomem->start,
463c4e05037SAdrian Hunter 					    resource_size(iomem));
464c4e05037SAdrian Hunter 	if (host->ioaddr == NULL) {
465c4e05037SAdrian Hunter 		err = -ENOMEM;
466c4e05037SAdrian Hunter 		goto err_free;
467c4e05037SAdrian Hunter 	}
468c4e05037SAdrian Hunter 
469c4e05037SAdrian Hunter 	if (c->slot) {
470578b36b6SGao, Yunpeng 		if (c->slot->probe_slot) {
4717dafca83SAdrian Hunter 			err = c->slot->probe_slot(pdev, hid, uid);
472578b36b6SGao, Yunpeng 			if (err)
473578b36b6SGao, Yunpeng 				goto err_free;
474578b36b6SGao, Yunpeng 		}
475c4e05037SAdrian Hunter 		if (c->slot->chip) {
476c4e05037SAdrian Hunter 			host->ops            = c->slot->chip->ops;
477c4e05037SAdrian Hunter 			host->quirks        |= c->slot->chip->quirks;
478c4e05037SAdrian Hunter 			host->quirks2       |= c->slot->chip->quirks2;
479c4e05037SAdrian Hunter 			host->mmc->caps     |= c->slot->chip->caps;
480c4e05037SAdrian Hunter 			host->mmc->caps2    |= c->slot->chip->caps2;
481c4e05037SAdrian Hunter 			host->mmc->pm_caps  |= c->slot->chip->pm_caps;
482c4e05037SAdrian Hunter 		}
483c4e05037SAdrian Hunter 		host->quirks        |= c->slot->quirks;
484c4e05037SAdrian Hunter 		host->quirks2       |= c->slot->quirks2;
485c4e05037SAdrian Hunter 		host->mmc->caps     |= c->slot->caps;
486c4e05037SAdrian Hunter 		host->mmc->caps2    |= c->slot->caps2;
487c4e05037SAdrian Hunter 		host->mmc->pm_caps  |= c->slot->pm_caps;
488c4e05037SAdrian Hunter 	}
489c4e05037SAdrian Hunter 
4900d3e3350SAdrian Hunter 	host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
4910d3e3350SAdrian Hunter 
4924fd4409cSAdrian Hunter 	if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
4934fd4409cSAdrian Hunter 		bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
4944fd4409cSAdrian Hunter 
495e28d6f04SZhang Rui 		err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL);
496e28d6f04SZhang Rui 		if (err) {
497e28d6f04SZhang Rui 			if (err == -EPROBE_DEFER)
498e28d6f04SZhang Rui 				goto err_free;
4994fd4409cSAdrian Hunter 			dev_warn(dev, "failed to setup card detect gpio\n");
5004fd4409cSAdrian Hunter 			c->use_runtime_pm = false;
5014fd4409cSAdrian Hunter 		}
5024fd4409cSAdrian Hunter 	}
5034fd4409cSAdrian Hunter 
504c4e05037SAdrian Hunter 	err = sdhci_add_host(host);
505c4e05037SAdrian Hunter 	if (err)
506c4e05037SAdrian Hunter 		goto err_free;
507c4e05037SAdrian Hunter 
508c4e05037SAdrian Hunter 	if (c->use_runtime_pm) {
5091d1ff458SAdrian Hunter 		pm_runtime_set_active(dev);
510c4e05037SAdrian Hunter 		pm_suspend_ignore_children(dev, 1);
511c4e05037SAdrian Hunter 		pm_runtime_set_autosuspend_delay(dev, 50);
512c4e05037SAdrian Hunter 		pm_runtime_use_autosuspend(dev);
513c4e05037SAdrian Hunter 		pm_runtime_enable(dev);
514c4e05037SAdrian Hunter 	}
515c4e05037SAdrian Hunter 
5164e6a2ef9SFu, Zhonghui 	device_enable_async_suspend(dev);
5174e6a2ef9SFu, Zhonghui 
518c4e05037SAdrian Hunter 	return 0;
519c4e05037SAdrian Hunter 
520c4e05037SAdrian Hunter err_free:
521c4e05037SAdrian Hunter 	sdhci_free_host(c->host);
522c4e05037SAdrian Hunter 	return err;
523c4e05037SAdrian Hunter }
524c4e05037SAdrian Hunter 
5254e608e4eSGreg Kroah-Hartman static int sdhci_acpi_remove(struct platform_device *pdev)
526c4e05037SAdrian Hunter {
527c4e05037SAdrian Hunter 	struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
528c4e05037SAdrian Hunter 	struct device *dev = &pdev->dev;
529c4e05037SAdrian Hunter 	int dead;
530c4e05037SAdrian Hunter 
531c4e05037SAdrian Hunter 	if (c->use_runtime_pm) {
532c4e05037SAdrian Hunter 		pm_runtime_get_sync(dev);
533c4e05037SAdrian Hunter 		pm_runtime_disable(dev);
534c4e05037SAdrian Hunter 		pm_runtime_put_noidle(dev);
535c4e05037SAdrian Hunter 	}
536c4e05037SAdrian Hunter 
537578b36b6SGao, Yunpeng 	if (c->slot && c->slot->remove_slot)
538578b36b6SGao, Yunpeng 		c->slot->remove_slot(pdev);
539578b36b6SGao, Yunpeng 
540c4e05037SAdrian Hunter 	dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
541c4e05037SAdrian Hunter 	sdhci_remove_host(c->host, dead);
542c4e05037SAdrian Hunter 	sdhci_free_host(c->host);
543c4e05037SAdrian Hunter 
544c4e05037SAdrian Hunter 	return 0;
545c4e05037SAdrian Hunter }
546c4e05037SAdrian Hunter 
547c4e05037SAdrian Hunter #ifdef CONFIG_PM_SLEEP
548c4e05037SAdrian Hunter 
549c4e05037SAdrian Hunter static int sdhci_acpi_suspend(struct device *dev)
550c4e05037SAdrian Hunter {
551c4e05037SAdrian Hunter 	struct sdhci_acpi_host *c = dev_get_drvdata(dev);
552d38dcad4SAdrian Hunter 	struct sdhci_host *host = c->host;
553c4e05037SAdrian Hunter 
554d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
555d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
556d38dcad4SAdrian Hunter 
557d38dcad4SAdrian Hunter 	return sdhci_suspend_host(host);
558c4e05037SAdrian Hunter }
559c4e05037SAdrian Hunter 
560c4e05037SAdrian Hunter static int sdhci_acpi_resume(struct device *dev)
561c4e05037SAdrian Hunter {
562c4e05037SAdrian Hunter 	struct sdhci_acpi_host *c = dev_get_drvdata(dev);
563c4e05037SAdrian Hunter 
5646e1c7d61SAdrian Hunter 	sdhci_acpi_byt_setting(&c->pdev->dev);
5656e1c7d61SAdrian Hunter 
566c4e05037SAdrian Hunter 	return sdhci_resume_host(c->host);
567c4e05037SAdrian Hunter }
568c4e05037SAdrian Hunter 
569c4e05037SAdrian Hunter #endif
570c4e05037SAdrian Hunter 
571162d6f98SRafael J. Wysocki #ifdef CONFIG_PM
572c4e05037SAdrian Hunter 
573c4e05037SAdrian Hunter static int sdhci_acpi_runtime_suspend(struct device *dev)
574c4e05037SAdrian Hunter {
575c4e05037SAdrian Hunter 	struct sdhci_acpi_host *c = dev_get_drvdata(dev);
576d38dcad4SAdrian Hunter 	struct sdhci_host *host = c->host;
577c4e05037SAdrian Hunter 
578d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
579d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
580d38dcad4SAdrian Hunter 
581d38dcad4SAdrian Hunter 	return sdhci_runtime_suspend_host(host);
582c4e05037SAdrian Hunter }
583c4e05037SAdrian Hunter 
584c4e05037SAdrian Hunter static int sdhci_acpi_runtime_resume(struct device *dev)
585c4e05037SAdrian Hunter {
586c4e05037SAdrian Hunter 	struct sdhci_acpi_host *c = dev_get_drvdata(dev);
587c4e05037SAdrian Hunter 
5886e1c7d61SAdrian Hunter 	sdhci_acpi_byt_setting(&c->pdev->dev);
5896e1c7d61SAdrian Hunter 
590c4e05037SAdrian Hunter 	return sdhci_runtime_resume_host(c->host);
591c4e05037SAdrian Hunter }
592c4e05037SAdrian Hunter 
593c4e05037SAdrian Hunter #endif
594c4e05037SAdrian Hunter 
595c4e05037SAdrian Hunter static const struct dev_pm_ops sdhci_acpi_pm_ops = {
596dafed447SUlf Hansson 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
5971d75f74bSPeter Griffin 	SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
5989b449e99SUlf Hansson 			sdhci_acpi_runtime_resume, NULL)
599c4e05037SAdrian Hunter };
600c4e05037SAdrian Hunter 
601c4e05037SAdrian Hunter static struct platform_driver sdhci_acpi_driver = {
602c4e05037SAdrian Hunter 	.driver = {
603c4e05037SAdrian Hunter 		.name			= "sdhci-acpi",
604c4e05037SAdrian Hunter 		.acpi_match_table	= sdhci_acpi_ids,
605c4e05037SAdrian Hunter 		.pm			= &sdhci_acpi_pm_ops,
606c4e05037SAdrian Hunter 	},
607c4e05037SAdrian Hunter 	.probe	= sdhci_acpi_probe,
6084e608e4eSGreg Kroah-Hartman 	.remove	= sdhci_acpi_remove,
609c4e05037SAdrian Hunter };
610c4e05037SAdrian Hunter 
611c4e05037SAdrian Hunter module_platform_driver(sdhci_acpi_driver);
612c4e05037SAdrian Hunter 
613c4e05037SAdrian Hunter MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver");
614c4e05037SAdrian Hunter MODULE_AUTHOR("Adrian Hunter");
615c4e05037SAdrian Hunter MODULE_LICENSE("GPL v2");
616