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 v3.1-rc6 135 lines 3.1 kB view raw
1/* 2 * SDHCI support for CNS3xxx SoC 3 * 4 * Copyright 2008 Cavium Networks 5 * Copyright 2010 MontaVista Software, LLC. 6 * 7 * Authors: Scott Shu 8 * Anton Vorontsov <avorontsov@mvista.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#include <linux/delay.h> 16#include <linux/device.h> 17#include <linux/mmc/host.h> 18#include <mach/cns3xxx.h> 19#include "sdhci-pltfm.h" 20 21static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) 22{ 23 return 150000000; 24} 25 26static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) 27{ 28 struct device *dev = mmc_dev(host->mmc); 29 int div = 1; 30 u16 clk; 31 unsigned long timeout; 32 33 if (clock == host->clock) 34 return; 35 36 sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 37 38 if (clock == 0) 39 goto out; 40 41 while (host->max_clk / div > clock) { 42 /* 43 * On CNS3xxx divider grows linearly up to 4, and then 44 * exponentially up to 256. 45 */ 46 if (div < 4) 47 div += 1; 48 else if (div < 256) 49 div *= 2; 50 else 51 break; 52 } 53 54 dev_dbg(dev, "desired SD clock: %d, actual: %d\n", 55 clock, host->max_clk / div); 56 57 /* Divide by 3 is special. */ 58 if (div != 3) 59 div >>= 1; 60 61 clk = div << SDHCI_DIVIDER_SHIFT; 62 clk |= SDHCI_CLOCK_INT_EN; 63 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 64 65 timeout = 20; 66 while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 67 & SDHCI_CLOCK_INT_STABLE)) { 68 if (timeout == 0) { 69 dev_warn(dev, "clock is unstable"); 70 break; 71 } 72 timeout--; 73 mdelay(1); 74 } 75 76 clk |= SDHCI_CLOCK_CARD_EN; 77 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 78out: 79 host->clock = clock; 80} 81 82static struct sdhci_ops sdhci_cns3xxx_ops = { 83 .get_max_clock = sdhci_cns3xxx_get_max_clk, 84 .set_clock = sdhci_cns3xxx_set_clock, 85}; 86 87static struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { 88 .ops = &sdhci_cns3xxx_ops, 89 .quirks = SDHCI_QUIRK_BROKEN_DMA | 90 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 91 SDHCI_QUIRK_INVERTED_WRITE_PROTECT | 92 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 93 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 94 SDHCI_QUIRK_NONSTANDARD_CLOCK, 95}; 96 97static int __devinit sdhci_cns3xxx_probe(struct platform_device *pdev) 98{ 99 return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata); 100} 101 102static int __devexit sdhci_cns3xxx_remove(struct platform_device *pdev) 103{ 104 return sdhci_pltfm_unregister(pdev); 105} 106 107static struct platform_driver sdhci_cns3xxx_driver = { 108 .driver = { 109 .name = "sdhci-cns3xxx", 110 .owner = THIS_MODULE, 111 }, 112 .probe = sdhci_cns3xxx_probe, 113 .remove = __devexit_p(sdhci_cns3xxx_remove), 114#ifdef CONFIG_PM 115 .suspend = sdhci_pltfm_suspend, 116 .resume = sdhci_pltfm_resume, 117#endif 118}; 119 120static int __init sdhci_cns3xxx_init(void) 121{ 122 return platform_driver_register(&sdhci_cns3xxx_driver); 123} 124module_init(sdhci_cns3xxx_init); 125 126static void __exit sdhci_cns3xxx_exit(void) 127{ 128 platform_driver_unregister(&sdhci_cns3xxx_driver); 129} 130module_exit(sdhci_cns3xxx_exit); 131 132MODULE_DESCRIPTION("SDHCI driver for CNS3xxx"); 133MODULE_AUTHOR("Scott Shu, " 134 "Anton Vorontsov <avorontsov@mvista.com>"); 135MODULE_LICENSE("GPL v2");