Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.37 257 lines 6.6 kB view raw
1/* linux/drivers/mmc/host/sdhci-pxa.c 2 * 3 * Copyright (C) 2010 Marvell International Ltd. 4 * Zhangfei Gao <zhangfei.gao@marvell.com> 5 * Kevin Wang <dwang4@marvell.com> 6 * Mingwei Wang <mwwang@marvell.com> 7 * Philip Rakity <prakity@marvell.com> 8 * Mark Brown <markb@marvell.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15/* Supports: 16 * SDHCI support for MMP2/PXA910/PXA168 17 * 18 * Refer to sdhci-s3c.c. 19 */ 20 21#include <linux/delay.h> 22#include <linux/platform_device.h> 23#include <linux/mmc/host.h> 24#include <linux/clk.h> 25#include <linux/io.h> 26#include <linux/err.h> 27#include <plat/sdhci.h> 28#include "sdhci.h" 29 30#define DRIVER_NAME "sdhci-pxa" 31 32#define SD_FIFO_PARAM 0x104 33#define DIS_PAD_SD_CLK_GATE 0x400 34 35struct sdhci_pxa { 36 struct sdhci_host *host; 37 struct sdhci_pxa_platdata *pdata; 38 struct clk *clk; 39 struct resource *res; 40 41 u8 clk_enable; 42}; 43 44/*****************************************************************************\ 45 * * 46 * SDHCI core callbacks * 47 * * 48\*****************************************************************************/ 49static void set_clock(struct sdhci_host *host, unsigned int clock) 50{ 51 struct sdhci_pxa *pxa = sdhci_priv(host); 52 u32 tmp = 0; 53 54 if (clock == 0) { 55 if (pxa->clk_enable) { 56 clk_disable(pxa->clk); 57 pxa->clk_enable = 0; 58 } 59 } else { 60 if (0 == pxa->clk_enable) { 61 if (pxa->pdata->flags & PXA_FLAG_DISABLE_CLOCK_GATING) { 62 tmp = readl(host->ioaddr + SD_FIFO_PARAM); 63 tmp |= DIS_PAD_SD_CLK_GATE; 64 writel(tmp, host->ioaddr + SD_FIFO_PARAM); 65 } 66 clk_enable(pxa->clk); 67 pxa->clk_enable = 1; 68 } 69 } 70} 71 72static struct sdhci_ops sdhci_pxa_ops = { 73 .set_clock = set_clock, 74}; 75 76/*****************************************************************************\ 77 * * 78 * Device probing/removal * 79 * * 80\*****************************************************************************/ 81 82static int __devinit sdhci_pxa_probe(struct platform_device *pdev) 83{ 84 struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; 85 struct device *dev = &pdev->dev; 86 struct sdhci_host *host = NULL; 87 struct resource *iomem = NULL; 88 struct sdhci_pxa *pxa = NULL; 89 int ret, irq; 90 91 irq = platform_get_irq(pdev, 0); 92 if (irq < 0) { 93 dev_err(dev, "no irq specified\n"); 94 return irq; 95 } 96 97 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 98 if (!iomem) { 99 dev_err(dev, "no memory specified\n"); 100 return -ENOENT; 101 } 102 103 host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pxa)); 104 if (IS_ERR(host)) { 105 dev_err(dev, "failed to alloc host\n"); 106 return PTR_ERR(host); 107 } 108 109 pxa = sdhci_priv(host); 110 pxa->host = host; 111 pxa->pdata = pdata; 112 pxa->clk_enable = 0; 113 114 pxa->clk = clk_get(dev, "PXA-SDHCLK"); 115 if (IS_ERR(pxa->clk)) { 116 dev_err(dev, "failed to get io clock\n"); 117 ret = PTR_ERR(pxa->clk); 118 goto out; 119 } 120 121 pxa->res = request_mem_region(iomem->start, resource_size(iomem), 122 mmc_hostname(host->mmc)); 123 if (!pxa->res) { 124 dev_err(&pdev->dev, "cannot request region\n"); 125 ret = -EBUSY; 126 goto out; 127 } 128 129 host->ioaddr = ioremap(iomem->start, resource_size(iomem)); 130 if (!host->ioaddr) { 131 dev_err(&pdev->dev, "failed to remap registers\n"); 132 ret = -ENOMEM; 133 goto out; 134 } 135 136 host->hw_name = "MMC"; 137 host->ops = &sdhci_pxa_ops; 138 host->irq = irq; 139 host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 140 141 if (pdata->quirks) 142 host->quirks |= pdata->quirks; 143 144 /* If slot design supports 8 bit data, indicate this to MMC. */ 145 if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) 146 host->mmc->caps |= MMC_CAP_8_BIT_DATA; 147 148 ret = sdhci_add_host(host); 149 if (ret) { 150 dev_err(&pdev->dev, "failed to add host\n"); 151 goto out; 152 } 153 154 if (pxa->pdata->max_speed) 155 host->mmc->f_max = pxa->pdata->max_speed; 156 157 platform_set_drvdata(pdev, host); 158 159 return 0; 160out: 161 if (host) { 162 clk_put(pxa->clk); 163 if (host->ioaddr) 164 iounmap(host->ioaddr); 165 if (pxa->res) 166 release_mem_region(pxa->res->start, 167 resource_size(pxa->res)); 168 sdhci_free_host(host); 169 } 170 171 return ret; 172} 173 174static int __devexit sdhci_pxa_remove(struct platform_device *pdev) 175{ 176 struct sdhci_host *host = platform_get_drvdata(pdev); 177 struct sdhci_pxa *pxa = sdhci_priv(host); 178 int dead = 0; 179 u32 scratch; 180 181 if (host) { 182 scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 183 if (scratch == (u32)-1) 184 dead = 1; 185 186 sdhci_remove_host(host, dead); 187 188 if (host->ioaddr) 189 iounmap(host->ioaddr); 190 if (pxa->res) 191 release_mem_region(pxa->res->start, 192 resource_size(pxa->res)); 193 if (pxa->clk_enable) { 194 clk_disable(pxa->clk); 195 pxa->clk_enable = 0; 196 } 197 clk_put(pxa->clk); 198 199 sdhci_free_host(host); 200 platform_set_drvdata(pdev, NULL); 201 } 202 203 return 0; 204} 205 206#ifdef CONFIG_PM 207static int sdhci_pxa_suspend(struct platform_device *dev, pm_message_t state) 208{ 209 struct sdhci_host *host = platform_get_drvdata(dev); 210 211 return sdhci_suspend_host(host, state); 212} 213 214static int sdhci_pxa_resume(struct platform_device *dev) 215{ 216 struct sdhci_host *host = platform_get_drvdata(dev); 217 218 return sdhci_resume_host(host); 219} 220#else 221#define sdhci_pxa_suspend NULL 222#define sdhci_pxa_resume NULL 223#endif 224 225static struct platform_driver sdhci_pxa_driver = { 226 .probe = sdhci_pxa_probe, 227 .remove = __devexit_p(sdhci_pxa_remove), 228 .suspend = sdhci_pxa_suspend, 229 .resume = sdhci_pxa_resume, 230 .driver = { 231 .name = DRIVER_NAME, 232 .owner = THIS_MODULE, 233 }, 234}; 235 236/*****************************************************************************\ 237 * * 238 * Driver init/exit * 239 * * 240\*****************************************************************************/ 241 242static int __init sdhci_pxa_init(void) 243{ 244 return platform_driver_register(&sdhci_pxa_driver); 245} 246 247static void __exit sdhci_pxa_exit(void) 248{ 249 platform_driver_unregister(&sdhci_pxa_driver); 250} 251 252module_init(sdhci_pxa_init); 253module_exit(sdhci_pxa_exit); 254 255MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2"); 256MODULE_AUTHOR("Zhangfei Gao <zhangfei.gao@marvell.com>"); 257MODULE_LICENSE("GPL v2");