2010-08-11 05:01:49 +04:00
/*
* SDHCI support for CNS3xxx SoC
*
* Copyright 2008 Cavium Networks
* Copyright 2010 MontaVista Software , LLC .
*
* Authors : Scott Shu
* Anton Vorontsov < avorontsov @ mvista . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/mmc/host.h>
2011-11-12 04:54:53 +04:00
# include <linux/module.h>
2010-08-11 05:01:49 +04:00
# include "sdhci-pltfm.h"
static unsigned int sdhci_cns3xxx_get_max_clk ( struct sdhci_host * host )
{
return 150000000 ;
}
static void sdhci_cns3xxx_set_clock ( struct sdhci_host * host , unsigned int clock )
{
struct device * dev = mmc_dev ( host - > mmc ) ;
int div = 1 ;
u16 clk ;
unsigned long timeout ;
2014-04-25 15:58:50 +04:00
host - > mmc - > actual_clock = 0 ;
2010-08-11 05:01:49 +04:00
sdhci_writew ( host , 0 , SDHCI_CLOCK_CONTROL ) ;
if ( clock = = 0 )
2014-04-25 15:58:45 +04:00
return ;
2010-08-11 05:01:49 +04:00
while ( host - > max_clk / div > clock ) {
/*
* On CNS3xxx divider grows linearly up to 4 , and then
* exponentially up to 256.
*/
if ( div < 4 )
div + = 1 ;
else if ( div < 256 )
div * = 2 ;
else
break ;
}
dev_dbg ( dev , " desired SD clock: %d, actual: %d \n " ,
clock , host - > max_clk / div ) ;
/* Divide by 3 is special. */
if ( div ! = 3 )
div > > = 1 ;
clk = div < < SDHCI_DIVIDER_SHIFT ;
clk | = SDHCI_CLOCK_INT_EN ;
sdhci_writew ( host , clk , SDHCI_CLOCK_CONTROL ) ;
timeout = 20 ;
while ( ! ( ( clk = sdhci_readw ( host , SDHCI_CLOCK_CONTROL ) )
& SDHCI_CLOCK_INT_STABLE ) ) {
if ( timeout = = 0 ) {
dev_warn ( dev , " clock is unstable " ) ;
break ;
}
timeout - - ;
mdelay ( 1 ) ;
}
clk | = SDHCI_CLOCK_CARD_EN ;
sdhci_writew ( host , clk , SDHCI_CLOCK_CONTROL ) ;
}
2013-03-13 22:26:05 +04:00
static const struct sdhci_ops sdhci_cns3xxx_ops = {
2010-08-11 05:01:49 +04:00
. get_max_clock = sdhci_cns3xxx_get_max_clk ,
. set_clock = sdhci_cns3xxx_set_clock ,
2014-04-25 15:57:07 +04:00
. set_bus_width = sdhci_set_bus_width ,
2014-04-25 15:57:12 +04:00
. reset = sdhci_reset ,
2014-04-25 15:59:26 +04:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2010-08-11 05:01:49 +04:00
} ;
2013-03-13 22:26:03 +04:00
static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
2010-08-11 05:01:49 +04:00
. ops = & sdhci_cns3xxx_ops ,
. quirks = SDHCI_QUIRK_BROKEN_DMA |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
2014-04-25 15:58:55 +04:00
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ,
2010-08-11 05:01:49 +04:00
} ;
2011-05-27 19:48:12 +04:00
2012-11-19 22:23:06 +04:00
static int sdhci_cns3xxx_probe ( struct platform_device * pdev )
2011-05-27 19:48:12 +04:00
{
2013-05-30 00:50:05 +04:00
return sdhci_pltfm_register ( pdev , & sdhci_cns3xxx_pdata , 0 ) ;
2011-05-27 19:48:12 +04:00
}
static struct platform_driver sdhci_cns3xxx_driver = {
. driver = {
. name = " sdhci-cns3xxx " ,
2011-11-03 14:09:45 +04:00
. pm = SDHCI_PLTFM_PMOPS ,
2011-05-27 19:48:12 +04:00
} ,
. probe = sdhci_cns3xxx_probe ,
2015-02-27 10:47:31 +03:00
. remove = sdhci_pltfm_unregister ,
2011-05-27 19:48:12 +04:00
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( sdhci_cns3xxx_driver ) ;
2011-05-27 19:48:12 +04:00
MODULE_DESCRIPTION ( " SDHCI driver for CNS3xxx " ) ;
MODULE_AUTHOR ( " Scott Shu, "
" Anton Vorontsov <avorontsov@mvista.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;