1 /* 2 * Secure Digital Host Controller Interface ACPI driver. 3 * 4 * Copyright (c) 2012, Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 */ 20 21 #include <linux/init.h> 22 #include <linux/export.h> 23 #include <linux/module.h> 24 #include <linux/device.h> 25 #include <linux/platform_device.h> 26 #include <linux/ioport.h> 27 #include <linux/io.h> 28 #include <linux/dma-mapping.h> 29 #include <linux/compiler.h> 30 #include <linux/stddef.h> 31 #include <linux/bitops.h> 32 #include <linux/types.h> 33 #include <linux/err.h> 34 #include <linux/interrupt.h> 35 #include <linux/acpi.h> 36 #include <linux/pm.h> 37 #include <linux/pm_runtime.h> 38 39 #include <linux/mmc/host.h> 40 #include <linux/mmc/pm.h> 41 #include <linux/mmc/sdhci.h> 42 43 #include "sdhci.h" 44 45 enum { 46 SDHCI_ACPI_SD_CD = BIT(0), 47 SDHCI_ACPI_RUNTIME_PM = BIT(1), 48 }; 49 50 struct sdhci_acpi_chip { 51 const struct sdhci_ops *ops; 52 unsigned int quirks; 53 unsigned int quirks2; 54 unsigned long caps; 55 unsigned int caps2; 56 mmc_pm_flag_t pm_caps; 57 }; 58 59 struct sdhci_acpi_slot { 60 const struct sdhci_acpi_chip *chip; 61 unsigned int quirks; 62 unsigned int quirks2; 63 unsigned long caps; 64 unsigned int caps2; 65 mmc_pm_flag_t pm_caps; 66 unsigned int flags; 67 }; 68 69 struct sdhci_acpi_host { 70 struct sdhci_host *host; 71 const struct sdhci_acpi_slot *slot; 72 struct platform_device *pdev; 73 bool use_runtime_pm; 74 }; 75 76 static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) 77 { 78 return c->slot && (c->slot->flags & flag); 79 } 80 81 static int sdhci_acpi_enable_dma(struct sdhci_host *host) 82 { 83 return 0; 84 } 85 86 static const struct sdhci_ops sdhci_acpi_ops_dflt = { 87 .enable_dma = sdhci_acpi_enable_dma, 88 }; 89 90 static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { 91 .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, 92 .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD, 93 .flags = SDHCI_ACPI_RUNTIME_PM, 94 .pm_caps = MMC_PM_KEEP_POWER, 95 }; 96 97 static const struct acpi_device_id sdhci_acpi_ids[] = { 98 { "INT33C6", (kernel_ulong_t)&sdhci_acpi_slot_int_sdio }, 99 { "PNP0D40" }, 100 { }, 101 }; 102 MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids); 103 104 static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid) 105 { 106 const struct acpi_device_id *id; 107 108 for (id = sdhci_acpi_ids; id->id[0]; id++) 109 if (!strcmp(id->id, hid)) 110 return (const struct sdhci_acpi_slot *)id->driver_data; 111 return NULL; 112 } 113 114 static int sdhci_acpi_probe(struct platform_device *pdev) 115 { 116 struct device *dev = &pdev->dev; 117 acpi_handle handle = ACPI_HANDLE(dev); 118 struct acpi_device *device; 119 struct sdhci_acpi_host *c; 120 struct sdhci_host *host; 121 struct resource *iomem; 122 resource_size_t len; 123 const char *hid; 124 int err; 125 126 if (acpi_bus_get_device(handle, &device)) 127 return -ENODEV; 128 129 if (acpi_bus_get_status(device) || !device->status.present) 130 return -ENODEV; 131 132 hid = acpi_device_hid(device); 133 134 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 135 if (!iomem) 136 return -ENOMEM; 137 138 len = resource_size(iomem); 139 if (len < 0x100) 140 dev_err(dev, "Invalid iomem size!\n"); 141 142 if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) 143 return -ENOMEM; 144 145 host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host)); 146 if (IS_ERR(host)) 147 return PTR_ERR(host); 148 149 c = sdhci_priv(host); 150 c->host = host; 151 c->slot = sdhci_acpi_get_slot(hid); 152 c->pdev = pdev; 153 c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); 154 155 platform_set_drvdata(pdev, c); 156 157 host->hw_name = "ACPI"; 158 host->ops = &sdhci_acpi_ops_dflt; 159 host->irq = platform_get_irq(pdev, 0); 160 161 host->ioaddr = devm_ioremap_nocache(dev, iomem->start, 162 resource_size(iomem)); 163 if (host->ioaddr == NULL) { 164 err = -ENOMEM; 165 goto err_free; 166 } 167 168 if (!dev->dma_mask) { 169 u64 dma_mask; 170 171 if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) { 172 /* 64-bit DMA is not supported at present */ 173 dma_mask = DMA_BIT_MASK(32); 174 } else { 175 dma_mask = DMA_BIT_MASK(32); 176 } 177 178 dev->dma_mask = &dev->coherent_dma_mask; 179 dev->coherent_dma_mask = dma_mask; 180 } 181 182 if (c->slot) { 183 if (c->slot->chip) { 184 host->ops = c->slot->chip->ops; 185 host->quirks |= c->slot->chip->quirks; 186 host->quirks2 |= c->slot->chip->quirks2; 187 host->mmc->caps |= c->slot->chip->caps; 188 host->mmc->caps2 |= c->slot->chip->caps2; 189 host->mmc->pm_caps |= c->slot->chip->pm_caps; 190 } 191 host->quirks |= c->slot->quirks; 192 host->quirks2 |= c->slot->quirks2; 193 host->mmc->caps |= c->slot->caps; 194 host->mmc->caps2 |= c->slot->caps2; 195 host->mmc->pm_caps |= c->slot->pm_caps; 196 } 197 198 err = sdhci_add_host(host); 199 if (err) 200 goto err_free; 201 202 if (c->use_runtime_pm) { 203 pm_suspend_ignore_children(dev, 1); 204 pm_runtime_set_autosuspend_delay(dev, 50); 205 pm_runtime_use_autosuspend(dev); 206 pm_runtime_enable(dev); 207 } 208 209 return 0; 210 211 err_free: 212 platform_set_drvdata(pdev, NULL); 213 sdhci_free_host(c->host); 214 return err; 215 } 216 217 static int sdhci_acpi_remove(struct platform_device *pdev) 218 { 219 struct sdhci_acpi_host *c = platform_get_drvdata(pdev); 220 struct device *dev = &pdev->dev; 221 int dead; 222 223 if (c->use_runtime_pm) { 224 pm_runtime_get_sync(dev); 225 pm_runtime_disable(dev); 226 pm_runtime_put_noidle(dev); 227 } 228 229 dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); 230 sdhci_remove_host(c->host, dead); 231 platform_set_drvdata(pdev, NULL); 232 sdhci_free_host(c->host); 233 234 return 0; 235 } 236 237 #ifdef CONFIG_PM_SLEEP 238 239 static int sdhci_acpi_suspend(struct device *dev) 240 { 241 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 242 243 return sdhci_suspend_host(c->host); 244 } 245 246 static int sdhci_acpi_resume(struct device *dev) 247 { 248 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 249 250 return sdhci_resume_host(c->host); 251 } 252 253 #else 254 255 #define sdhci_acpi_suspend NULL 256 #define sdhci_acpi_resume NULL 257 258 #endif 259 260 #ifdef CONFIG_PM_RUNTIME 261 262 static int sdhci_acpi_runtime_suspend(struct device *dev) 263 { 264 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 265 266 return sdhci_runtime_suspend_host(c->host); 267 } 268 269 static int sdhci_acpi_runtime_resume(struct device *dev) 270 { 271 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 272 273 return sdhci_runtime_resume_host(c->host); 274 } 275 276 static int sdhci_acpi_runtime_idle(struct device *dev) 277 { 278 return 0; 279 } 280 281 #else 282 283 #define sdhci_acpi_runtime_suspend NULL 284 #define sdhci_acpi_runtime_resume NULL 285 #define sdhci_acpi_runtime_idle NULL 286 287 #endif 288 289 static const struct dev_pm_ops sdhci_acpi_pm_ops = { 290 .suspend = sdhci_acpi_suspend, 291 .resume = sdhci_acpi_resume, 292 .runtime_suspend = sdhci_acpi_runtime_suspend, 293 .runtime_resume = sdhci_acpi_runtime_resume, 294 .runtime_idle = sdhci_acpi_runtime_idle, 295 }; 296 297 static struct platform_driver sdhci_acpi_driver = { 298 .driver = { 299 .name = "sdhci-acpi", 300 .owner = THIS_MODULE, 301 .acpi_match_table = sdhci_acpi_ids, 302 .pm = &sdhci_acpi_pm_ops, 303 }, 304 .probe = sdhci_acpi_probe, 305 .remove = sdhci_acpi_remove, 306 }; 307 308 module_platform_driver(sdhci_acpi_driver); 309 310 MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver"); 311 MODULE_AUTHOR("Adrian Hunter"); 312 MODULE_LICENSE("GPL v2"); 313