2010-11-08 00:57:12 +03:00
/*
* sdhci - dove . c Support for SDHCI on Marvell ' s Dove SoC
*
* Author : Saeed Bishara < saeed @ marvell . com >
* Mike Rapoport < mike @ compulab . co . il >
* Based on sdhci - cns3xxx . c
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2012-07-05 14:14:01 +04:00
# include <linux/clk.h>
# include <linux/err.h>
2012-11-23 03:55:51 +04:00
# include <linux/gpio.h>
# include <linux/io.h>
2010-11-08 00:57:12 +03:00
# include <linux/mmc/host.h>
2012-11-23 03:55:51 +04:00
# include <linux/module.h>
2012-07-31 12:12:59 +04:00
# include <linux/of.h>
2012-11-23 03:55:51 +04:00
# include <linux/of_gpio.h>
2010-11-08 00:57:12 +03:00
# include "sdhci-pltfm.h"
2012-07-05 14:14:01 +04:00
struct sdhci_dove_priv {
struct clk * clk ;
2012-11-23 03:55:51 +04:00
int gpio_cd ;
2012-07-05 14:14:01 +04:00
} ;
2012-11-23 03:55:51 +04:00
static irqreturn_t sdhci_dove_carddetect_irq ( int irq , void * data )
{
struct sdhci_host * host = data ;
tasklet_schedule ( & host - > card_tasklet ) ;
return IRQ_HANDLED ;
}
2010-11-08 00:57:12 +03:00
static u16 sdhci_dove_readw ( struct sdhci_host * host , int reg )
{
u16 ret ;
switch ( reg ) {
case SDHCI_HOST_VERSION :
case SDHCI_SLOT_INT_STATUS :
/* those registers don't exist */
return 0 ;
default :
ret = readw ( host - > ioaddr + reg ) ;
}
return ret ;
}
static u32 sdhci_dove_readl ( struct sdhci_host * host , int reg )
{
2012-11-23 03:55:51 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_dove_priv * priv = pltfm_host - > priv ;
2010-11-08 00:57:12 +03:00
u32 ret ;
2012-11-23 03:55:51 +04:00
ret = readl ( host - > ioaddr + reg ) ;
2010-11-08 00:57:12 +03:00
switch ( reg ) {
case SDHCI_CAPABILITIES :
/* Mask the support for 3.0V */
ret & = ~ SDHCI_CAN_VDD_300 ;
break ;
2012-11-23 03:55:51 +04:00
case SDHCI_PRESENT_STATE :
if ( gpio_is_valid ( priv - > gpio_cd ) ) {
if ( gpio_get_value ( priv - > gpio_cd ) = = 0 )
ret | = SDHCI_CARD_PRESENT ;
else
ret & = ~ SDHCI_CARD_PRESENT ;
}
break ;
2010-11-08 00:57:12 +03:00
}
return ret ;
}
2013-03-13 22:26:05 +04:00
static const struct sdhci_ops sdhci_dove_ops = {
2010-11-08 00:57:12 +03:00
. read_w = sdhci_dove_readw ,
. read_l = sdhci_dove_readl ,
} ;
2013-03-13 22:26:03 +04:00
static const struct sdhci_pltfm_data sdhci_dove_pdata = {
2010-11-08 00:57:12 +03:00
. ops = & sdhci_dove_ops ,
. quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
SDHCI_QUIRK_NO_BUSY_IRQ |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
2012-07-22 03:26:19 +04:00
SDHCI_QUIRK_FORCE_DMA |
SDHCI_QUIRK_NO_HISPD_BIT ,
2010-11-08 00:57:12 +03:00
} ;
2011-05-27 19:48:12 +04:00
2012-11-19 22:23:06 +04:00
static int sdhci_dove_probe ( struct platform_device * pdev )
2011-05-27 19:48:12 +04:00
{
2012-07-05 14:14:01 +04:00
struct sdhci_host * host ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_dove_priv * priv ;
int ret ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct sdhci_dove_priv ) ,
GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( & pdev - > dev , " unable to allocate private data " ) ;
2012-10-30 01:43:07 +04:00
return - ENOMEM ;
2012-07-05 14:14:01 +04:00
}
2012-11-23 03:55:10 +04:00
priv - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2012-10-30 01:43:07 +04:00
2012-11-23 03:55:51 +04:00
if ( pdev - > dev . of_node ) {
priv - > gpio_cd = of_get_named_gpio ( pdev - > dev . of_node ,
" cd-gpios " , 0 ) ;
} else {
priv - > gpio_cd = - EINVAL ;
}
if ( gpio_is_valid ( priv - > gpio_cd ) ) {
ret = gpio_request ( priv - > gpio_cd , " sdhci-cd " ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " card detect gpio request failed: %d \n " ,
ret ) ;
return ret ;
}
gpio_direction_input ( priv - > gpio_cd ) ;
}
2013-05-30 00:50:05 +04:00
host = sdhci_pltfm_init ( pdev , & sdhci_dove_pdata , 0 ) ;
2012-11-23 03:55:30 +04:00
if ( IS_ERR ( host ) ) {
ret = PTR_ERR ( host ) ;
goto err_sdhci_pltfm_init ;
}
2012-10-30 01:43:07 +04:00
2012-07-05 14:14:01 +04:00
pltfm_host = sdhci_priv ( host ) ;
pltfm_host - > priv = priv ;
2012-11-23 03:55:30 +04:00
if ( ! IS_ERR ( priv - > clk ) )
clk_prepare_enable ( priv - > clk ) ;
sdhci_get_of_property ( pdev ) ;
ret = sdhci_add_host ( host ) ;
if ( ret )
goto err_sdhci_add ;
2012-11-23 03:55:51 +04:00
/*
* We must request the IRQ after sdhci_add_host ( ) , as the tasklet only
* gets setup in sdhci_add_host ( ) and we oops .
*/
if ( gpio_is_valid ( priv - > gpio_cd ) ) {
ret = request_irq ( gpio_to_irq ( priv - > gpio_cd ) ,
sdhci_dove_carddetect_irq ,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING ,
mmc_hostname ( host - > mmc ) , host ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " card detect irq request failed: %d \n " ,
ret ) ;
goto err_request_irq ;
}
}
2012-07-05 14:14:01 +04:00
return 0 ;
2012-11-23 03:55:51 +04:00
err_request_irq :
sdhci_remove_host ( host , 0 ) ;
2012-11-23 03:55:30 +04:00
err_sdhci_add :
2012-11-23 03:55:10 +04:00
if ( ! IS_ERR ( priv - > clk ) )
2012-10-30 01:43:07 +04:00
clk_disable_unprepare ( priv - > clk ) ;
2012-11-23 03:55:30 +04:00
sdhci_pltfm_free ( pdev ) ;
err_sdhci_pltfm_init :
2012-11-23 03:55:51 +04:00
if ( gpio_is_valid ( priv - > gpio_cd ) )
gpio_free ( priv - > gpio_cd ) ;
2012-07-05 14:14:01 +04:00
return ret ;
2011-05-27 19:48:12 +04:00
}
2012-11-19 22:26:03 +04:00
static int sdhci_dove_remove ( struct platform_device * pdev )
2011-05-27 19:48:12 +04:00
{
2012-07-05 14:14:01 +04:00
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_dove_priv * priv = pltfm_host - > priv ;
2012-10-30 01:43:07 +04:00
sdhci_pltfm_unregister ( pdev ) ;
2012-11-23 03:55:51 +04:00
if ( gpio_is_valid ( priv - > gpio_cd ) ) {
free_irq ( gpio_to_irq ( priv - > gpio_cd ) , host ) ;
gpio_free ( priv - > gpio_cd ) ;
}
2012-11-23 03:55:10 +04:00
if ( ! IS_ERR ( priv - > clk ) )
2012-10-30 01:43:07 +04:00
clk_disable_unprepare ( priv - > clk ) ;
2012-11-23 03:55:10 +04:00
2012-10-30 01:43:07 +04:00
return 0 ;
2011-05-27 19:48:12 +04:00
}
2012-11-19 22:24:22 +04:00
static const struct of_device_id sdhci_dove_of_match_table [ ] = {
2012-07-31 12:12:59 +04:00
{ . compatible = " marvell,dove-sdhci " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sdhci_dove_of_match_table ) ;
2011-05-27 19:48:12 +04:00
static struct platform_driver sdhci_dove_driver = {
. driver = {
. name = " sdhci-dove " ,
. owner = THIS_MODULE ,
2011-11-03 14:09:45 +04:00
. pm = SDHCI_PLTFM_PMOPS ,
2014-02-25 13:48:24 +04:00
. of_match_table = sdhci_dove_of_match_table ,
2011-05-27 19:48:12 +04:00
} ,
. probe = sdhci_dove_probe ,
2012-11-19 22:20:26 +04:00
. remove = sdhci_dove_remove ,
2011-05-27 19:48:12 +04:00
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( sdhci_dove_driver ) ;
2011-05-27 19:48:12 +04:00
MODULE_DESCRIPTION ( " SDHCI driver for Dove " ) ;
MODULE_AUTHOR ( " Saeed Bishara <saeed@marvell.com>, "
" Mike Rapoport <mike@compulab.co.il> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;