sdhci-cns3xxx.c (2620B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * SDHCI support for CNS3xxx SoC 4 * 5 * Copyright 2008 Cavium Networks 6 * Copyright 2010 MontaVista Software, LLC. 7 * 8 * Authors: Scott Shu 9 * Anton Vorontsov <avorontsov@mvista.com> 10 */ 11 12#include <linux/delay.h> 13#include <linux/device.h> 14#include <linux/mmc/host.h> 15#include <linux/module.h> 16#include "sdhci-pltfm.h" 17 18static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) 19{ 20 return 150000000; 21} 22 23static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) 24{ 25 struct device *dev = mmc_dev(host->mmc); 26 int div = 1; 27 u16 clk; 28 unsigned long timeout; 29 30 host->mmc->actual_clock = 0; 31 32 sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 33 34 if (clock == 0) 35 return; 36 37 while (host->max_clk / div > clock) { 38 /* 39 * On CNS3xxx divider grows linearly up to 4, and then 40 * exponentially up to 256. 41 */ 42 if (div < 4) 43 div += 1; 44 else if (div < 256) 45 div *= 2; 46 else 47 break; 48 } 49 50 dev_dbg(dev, "desired SD clock: %d, actual: %d\n", 51 clock, host->max_clk / div); 52 53 /* Divide by 3 is special. */ 54 if (div != 3) 55 div >>= 1; 56 57 clk = div << SDHCI_DIVIDER_SHIFT; 58 clk |= SDHCI_CLOCK_INT_EN; 59 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 60 61 timeout = 20; 62 while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 63 & SDHCI_CLOCK_INT_STABLE)) { 64 if (timeout == 0) { 65 dev_warn(dev, "clock is unstable"); 66 break; 67 } 68 timeout--; 69 mdelay(1); 70 } 71 72 clk |= SDHCI_CLOCK_CARD_EN; 73 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 74} 75 76static const struct sdhci_ops sdhci_cns3xxx_ops = { 77 .get_max_clock = sdhci_cns3xxx_get_max_clk, 78 .set_clock = sdhci_cns3xxx_set_clock, 79 .set_bus_width = sdhci_set_bus_width, 80 .reset = sdhci_reset, 81 .set_uhs_signaling = sdhci_set_uhs_signaling, 82}; 83 84static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { 85 .ops = &sdhci_cns3xxx_ops, 86 .quirks = SDHCI_QUIRK_BROKEN_DMA | 87 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 88 SDHCI_QUIRK_INVERTED_WRITE_PROTECT | 89 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 90 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, 91}; 92 93static int sdhci_cns3xxx_probe(struct platform_device *pdev) 94{ 95 return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0); 96} 97 98static struct platform_driver sdhci_cns3xxx_driver = { 99 .driver = { 100 .name = "sdhci-cns3xxx", 101 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 102 .pm = &sdhci_pltfm_pmops, 103 }, 104 .probe = sdhci_cns3xxx_probe, 105 .remove = sdhci_pltfm_unregister, 106}; 107 108module_platform_driver(sdhci_cns3xxx_driver); 109 110MODULE_DESCRIPTION("SDHCI driver for CNS3xxx"); 111MODULE_AUTHOR("Scott Shu, " 112 "Anton Vorontsov <avorontsov@mvista.com>"); 113MODULE_LICENSE("GPL v2");