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 <linux/sdhci-pltfm.h> 19#include <mach/cns3xxx.h> 20#include "sdhci.h" 21#include "sdhci-pltfm.h" 22 23static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) 24{ 25 return 150000000; 26} 27 28static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) 29{ 30 struct device *dev = mmc_dev(host->mmc); 31 int div = 1; 32 u16 clk; 33 unsigned long timeout; 34 35 if (clock == host->clock) 36 return; 37 38 sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 39 40 if (clock == 0) 41 goto out; 42 43 while (host->max_clk / div > clock) { 44 /* 45 * On CNS3xxx divider grows linearly up to 4, and then 46 * exponentially up to 256. 47 */ 48 if (div < 4) 49 div += 1; 50 else if (div < 256) 51 div *= 2; 52 else 53 break; 54 } 55 56 dev_dbg(dev, "desired SD clock: %d, actual: %d\n", 57 clock, host->max_clk / div); 58 59 /* Divide by 3 is special. */ 60 if (div != 3) 61 div >>= 1; 62 63 clk = div << SDHCI_DIVIDER_SHIFT; 64 clk |= SDHCI_CLOCK_INT_EN; 65 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 66 67 timeout = 20; 68 while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 69 & SDHCI_CLOCK_INT_STABLE)) { 70 if (timeout == 0) { 71 dev_warn(dev, "clock is unstable"); 72 break; 73 } 74 timeout--; 75 mdelay(1); 76 } 77 78 clk |= SDHCI_CLOCK_CARD_EN; 79 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 80out: 81 host->clock = clock; 82} 83 84static struct sdhci_ops sdhci_cns3xxx_ops = { 85 .get_max_clock = sdhci_cns3xxx_get_max_clk, 86 .set_clock = sdhci_cns3xxx_set_clock, 87}; 88 89struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { 90 .ops = &sdhci_cns3xxx_ops, 91 .quirks = SDHCI_QUIRK_BROKEN_DMA | 92 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 93 SDHCI_QUIRK_INVERTED_WRITE_PROTECT | 94 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 95 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 96 SDHCI_QUIRK_NONSTANDARD_CLOCK, 97}; 98