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 host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; 199 200 err = sdhci_add_host(host); 201 if (err) 202 goto err_free; 203 204 if (c->use_runtime_pm) { 205 pm_suspend_ignore_children(dev, 1); 206 pm_runtime_set_autosuspend_delay(dev, 50); 207 pm_runtime_use_autosuspend(dev); 208 pm_runtime_enable(dev); 209 } 210 211 return 0; 212 213 err_free: 214 platform_set_drvdata(pdev, NULL); 215 sdhci_free_host(c->host); 216 return err; 217 } 218 219 static int sdhci_acpi_remove(struct platform_device *pdev) 220 { 221 struct sdhci_acpi_host *c = platform_get_drvdata(pdev); 222 struct device *dev = &pdev->dev; 223 int dead; 224 225 if (c->use_runtime_pm) { 226 pm_runtime_get_sync(dev); 227 pm_runtime_disable(dev); 228 pm_runtime_put_noidle(dev); 229 } 230 231 dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); 232 sdhci_remove_host(c->host, dead); 233 platform_set_drvdata(pdev, NULL); 234 sdhci_free_host(c->host); 235 236 return 0; 237 } 238 239 #ifdef CONFIG_PM_SLEEP 240 241 static int sdhci_acpi_suspend(struct device *dev) 242 { 243 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 244 245 return sdhci_suspend_host(c->host); 246 } 247 248 static int sdhci_acpi_resume(struct device *dev) 249 { 250 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 251 252 return sdhci_resume_host(c->host); 253 } 254 255 #else 256 257 #define sdhci_acpi_suspend NULL 258 #define sdhci_acpi_resume NULL 259 260 #endif 261 262 #ifdef CONFIG_PM_RUNTIME 263 264 static int sdhci_acpi_runtime_suspend(struct device *dev) 265 { 266 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 267 268 return sdhci_runtime_suspend_host(c->host); 269 } 270 271 static int sdhci_acpi_runtime_resume(struct device *dev) 272 { 273 struct sdhci_acpi_host *c = dev_get_drvdata(dev); 274 275 return sdhci_runtime_resume_host(c->host); 276 } 277 278 static int sdhci_acpi_runtime_idle(struct device *dev) 279 { 280 return 0; 281 } 282 283 #else 284 285 #define sdhci_acpi_runtime_suspend NULL 286 #define sdhci_acpi_runtime_resume NULL 287 #define sdhci_acpi_runtime_idle NULL 288 289 #endif 290 291 static const struct dev_pm_ops sdhci_acpi_pm_ops = { 292 .suspend = sdhci_acpi_suspend, 293 .resume = sdhci_acpi_resume, 294 .runtime_suspend = sdhci_acpi_runtime_suspend, 295 .runtime_resume = sdhci_acpi_runtime_resume, 296 .runtime_idle = sdhci_acpi_runtime_idle, 297 }; 298 299 static struct platform_driver sdhci_acpi_driver = { 300 .driver = { 301 .name = "sdhci-acpi", 302 .owner = THIS_MODULE, 303 .acpi_match_table = sdhci_acpi_ids, 304 .pm = &sdhci_acpi_pm_ops, 305 }, 306 .probe = sdhci_acpi_probe, 307 .remove = sdhci_acpi_remove, 308 }; 309 310 module_platform_driver(sdhci_acpi_driver); 311 312 MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver"); 313 MODULE_AUTHOR("Adrian Hunter"); 314 MODULE_LICENSE("GPL v2"); 315