2018-08-22 01:02:17 +03:00
// SPDX-License-Identifier: GPL-2.0
2008-07-15 19:02:21 +04:00
/*
2017-05-10 12:25:25 +03:00
* Driver for the MMC / SD / SDIO cell found in :
*
* TC6393XB TC6391XB TC6387XB T7L66XB ASIC3
2008-07-15 19:02:21 +04:00
*
2017-05-30 15:50:52 +03:00
* Copyright ( C ) 2017 Renesas Electronics Corporation
* Copyright ( C ) 2017 Horms Solutions , Simon Horman
2011-03-23 14:42:44 +03:00
* Copyright ( C ) 2007 Ian Molton
* Copyright ( C ) 2004 Ian Molton
2008-07-15 19:02:21 +04:00
*/
2010-11-23 19:24:11 +03:00
2018-08-23 07:44:16 +03:00
# include <linux/delay.h>
2010-11-23 19:24:11 +03:00
# include <linux/device.h>
2008-07-15 19:02:21 +04:00
# include <linux/mfd/core.h>
# include <linux/mfd/tmio.h>
2010-11-23 19:24:11 +03:00
# include <linux/mmc/host.h>
# include <linux/module.h>
# include <linux/pagemap.h>
# include <linux/scatterlist.h>
2011-01-06 01:36:14 +03:00
2011-03-23 14:42:44 +03:00
# include "tmio_mmc.h"
2008-07-15 19:02:21 +04:00
2018-10-10 06:51:32 +03:00
/* Registers specific to this variant */
# define CTL_SDIO_REGS 0x100
# define CTL_CLK_AND_WAIT_CTL 0x138
# define CTL_RESET_SDIO 0x1e0
2018-08-23 07:44:16 +03:00
static void tmio_mmc_clk_start ( struct tmio_mmc_host * host )
{
sd_ctrl_write16 ( host , CTL_SD_CARD_CLK_CTL , CLK_CTL_SCLKEN |
sd_ctrl_read16 ( host , CTL_SD_CARD_CLK_CTL ) ) ;
usleep_range ( 10000 , 11000 ) ;
sd_ctrl_write16 ( host , CTL_CLK_AND_WAIT_CTL , 0x0100 ) ;
usleep_range ( 10000 , 11000 ) ;
}
static void tmio_mmc_clk_stop ( struct tmio_mmc_host * host )
{
sd_ctrl_write16 ( host , CTL_CLK_AND_WAIT_CTL , 0x0000 ) ;
usleep_range ( 10000 , 11000 ) ;
sd_ctrl_write16 ( host , CTL_SD_CARD_CLK_CTL , ~ CLK_CTL_SCLKEN &
sd_ctrl_read16 ( host , CTL_SD_CARD_CLK_CTL ) ) ;
usleep_range ( 10000 , 11000 ) ;
}
static void tmio_mmc_set_clock ( struct tmio_mmc_host * host ,
unsigned int new_clock )
{
2018-08-30 15:16:03 +03:00
unsigned int divisor ;
2018-08-23 07:44:20 +03:00
u32 clk = 0 ;
int clk_sel ;
2018-08-23 07:44:16 +03:00
if ( new_clock = = 0 ) {
tmio_mmc_clk_stop ( host ) ;
return ;
}
2018-08-23 07:44:20 +03:00
divisor = host - > pdata - > hclk / new_clock ;
2018-08-23 07:44:16 +03:00
2018-08-30 15:14:38 +03:00
/* bit7 set: 1/512, ... bit0 set: 1/4, all bits clear: 1/2 */
clk_sel = ( divisor < = 1 ) ;
clk = clk_sel ? 0 : ( roundup_pow_of_two ( divisor ) > > 2 ) ;
2018-08-23 07:44:16 +03:00
2018-08-23 07:44:20 +03:00
host - > pdata - > set_clk_div ( host - > pdev , clk_sel ) ;
2018-08-23 07:44:16 +03:00
sd_ctrl_write16 ( host , CTL_SD_CARD_CLK_CTL , ~ CLK_CTL_SCLKEN &
sd_ctrl_read16 ( host , CTL_SD_CARD_CLK_CTL ) ) ;
sd_ctrl_write16 ( host , CTL_SD_CARD_CLK_CTL , clk & CLK_CTL_DIV_MASK ) ;
usleep_range ( 10000 , 11000 ) ;
tmio_mmc_clk_start ( host ) ;
}
2018-10-10 06:51:31 +03:00
static void tmio_mmc_reset ( struct tmio_mmc_host * host )
{
/* FIXME - should we set stop clock reg here */
sd_ctrl_write16 ( host , CTL_RESET_SD , 0x0000 ) ;
sd_ctrl_write16 ( host , CTL_RESET_SDIO , 0x0000 ) ;
usleep_range ( 10000 , 11000 ) ;
sd_ctrl_write16 ( host , CTL_RESET_SD , 0x0001 ) ;
sd_ctrl_write16 ( host , CTL_RESET_SDIO , 0x0001 ) ;
usleep_range ( 10000 , 11000 ) ;
if ( host - > pdata - > flags & TMIO_MMC_SDIO_IRQ ) {
sd_ctrl_write16 ( host , CTL_SDIO_IRQ_MASK , host - > sdio_irq_mask ) ;
sd_ctrl_write16 ( host , CTL_TRANSACTION_CTL , 0x0001 ) ;
}
}
2013-10-23 16:57:50 +04:00
# ifdef CONFIG_PM_SLEEP
static int tmio_mmc_suspend ( struct device * dev )
2008-07-15 19:02:21 +04:00
{
2013-10-23 16:57:50 +04:00
struct platform_device * pdev = to_platform_device ( dev ) ;
const struct mfd_cell * cell = mfd_get_cell ( pdev ) ;
2008-07-15 19:02:21 +04:00
int ret ;
2014-08-25 14:28:20 +04:00
ret = pm_runtime_force_suspend ( dev ) ;
2008-07-15 19:02:21 +04:00
/* Tell MFD core it can disable us now.*/
if ( ! ret & & cell - > disable )
2013-10-23 16:57:50 +04:00
cell - > disable ( pdev ) ;
2008-07-15 19:02:21 +04:00
return ret ;
}
2013-10-23 16:57:50 +04:00
static int tmio_mmc_resume ( struct device * dev )
2008-07-15 19:02:21 +04:00
{
2013-10-23 16:57:50 +04:00
struct platform_device * pdev = to_platform_device ( dev ) ;
const struct mfd_cell * cell = mfd_get_cell ( pdev ) ;
2008-07-15 19:02:21 +04:00
int ret = 0 ;
/* Tell the MFD core we are ready to be enabled */
2011-05-05 20:13:12 +04:00
if ( cell - > resume )
2013-10-23 16:57:50 +04:00
ret = cell - > resume ( pdev ) ;
2008-07-15 19:02:21 +04:00
2011-05-05 20:13:12 +04:00
if ( ! ret )
2014-08-25 14:28:20 +04:00
ret = pm_runtime_force_resume ( dev ) ;
2008-07-15 19:02:21 +04:00
return ret ;
}
# endif
2012-11-19 22:23:06 +04:00
static int tmio_mmc_probe ( struct platform_device * pdev )
2008-07-15 19:02:21 +04:00
{
2011-03-23 14:42:44 +03:00
const struct mfd_cell * cell = mfd_get_cell ( pdev ) ;
2009-06-04 22:12:31 +04:00
struct tmio_mmc_data * pdata ;
2008-07-15 19:02:21 +04:00
struct tmio_mmc_host * host ;
2013-11-20 12:30:55 +04:00
struct resource * res ;
2011-05-06 15:02:33 +04:00
int ret = - EINVAL , irq ;
2008-07-15 19:02:21 +04:00
2011-03-23 14:42:44 +03:00
if ( pdev - > num_resources ! = 2 )
2008-07-15 19:02:21 +04:00
goto out ;
2011-04-06 13:38:14 +04:00
pdata = pdev - > dev . platform_data ;
2009-06-04 22:12:34 +04:00
if ( ! pdata | | ! pdata - > hclk )
2009-06-04 22:12:31 +04:00
goto out ;
2009-06-04 22:12:34 +04:00
2011-05-06 15:02:33 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
ret = irq ;
goto out ;
}
2008-07-15 19:02:21 +04:00
/* Tell the MFD core we are ready to be enabled */
if ( cell - > enable ) {
2011-03-23 14:42:44 +03:00
ret = cell - > enable ( pdev ) ;
2008-07-15 19:02:21 +04:00
if ( ret )
2011-03-23 14:42:44 +03:00
goto out ;
2008-07-15 19:02:21 +04:00
}
2013-11-20 12:30:55 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2015-04-27 02:01:06 +03:00
if ( ! res ) {
ret = - EINVAL ;
goto cell_disable ;
}
2013-11-20 12:30:55 +04:00
2018-01-17 19:28:02 +03:00
host = tmio_mmc_host_alloc ( pdev , pdata ) ;
2018-01-17 19:28:01 +03:00
if ( IS_ERR ( host ) ) {
ret = PTR_ERR ( host ) ;
2010-02-17 10:38:23 +03:00
goto cell_disable ;
2018-01-17 19:28:01 +03:00
}
2008-07-15 19:02:21 +04:00
2015-01-13 07:58:20 +03:00
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
host - > bus_shift = resource_size ( res ) > > 10 ;
2018-08-23 07:44:16 +03:00
host - > set_clock = tmio_mmc_set_clock ;
2018-10-10 06:51:31 +03:00
host - > reset = tmio_mmc_reset ;
2015-01-13 07:58:20 +03:00
2018-01-17 19:28:02 +03:00
host - > mmc - > f_max = pdata - > hclk ;
host - > mmc - > f_min = pdata - > hclk / 512 ;
2018-01-17 19:28:04 +03:00
ret = tmio_mmc_host_probe ( host ) ;
2015-01-13 07:57:22 +03:00
if ( ret )
goto host_free ;
2015-04-27 02:01:36 +03:00
ret = devm_request_irq ( & pdev - > dev , irq , tmio_mmc_irq ,
2017-06-16 19:11:03 +03:00
IRQF_TRIGGER_FALLING ,
dev_name ( & pdev - > dev ) , host ) ;
2011-05-06 15:02:33 +04:00
if ( ret )
goto host_remove ;
2010-05-19 22:34:22 +04:00
pr_info ( " %s at 0x%08lx irq %d \n " , mmc_hostname ( host - > mmc ) ,
2011-05-06 15:02:33 +04:00
( unsigned long ) host - > ctl , irq ) ;
2008-07-15 19:02:21 +04:00
return 0 ;
2011-05-06 15:02:33 +04:00
host_remove :
tmio_mmc_host_remove ( host ) ;
2015-01-13 07:57:22 +03:00
host_free :
tmio_mmc_host_free ( host ) ;
2010-02-17 10:38:23 +03:00
cell_disable :
if ( cell - > disable )
2011-03-23 14:42:44 +03:00
cell - > disable ( pdev ) ;
2008-07-15 19:02:21 +04:00
out :
return ret ;
}
2012-11-19 22:26:03 +04:00
static int tmio_mmc_remove ( struct platform_device * pdev )
2008-07-15 19:02:21 +04:00
{
2011-03-23 14:42:44 +03:00
const struct mfd_cell * cell = mfd_get_cell ( pdev ) ;
2017-11-24 19:24:39 +03:00
struct tmio_mmc_host * host = platform_get_drvdata ( pdev ) ;
2008-07-15 19:02:21 +04:00
2017-11-24 19:24:39 +03:00
tmio_mmc_host_remove ( host ) ;
if ( cell - > disable )
cell - > disable ( pdev ) ;
2008-07-15 19:02:21 +04:00
return 0 ;
}
/* ------------------- device registration ----------------------- */
2013-10-23 16:57:50 +04:00
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( tmio_mmc_suspend , tmio_mmc_resume )
2014-12-04 02:34:11 +03:00
SET_RUNTIME_PM_OPS ( tmio_mmc_host_runtime_suspend ,
2017-06-16 19:11:03 +03:00
tmio_mmc_host_runtime_resume , NULL )
2013-10-23 16:57:50 +04:00
} ;
2008-07-15 19:02:21 +04:00
static struct platform_driver tmio_mmc_driver = {
. driver = {
. name = " tmio-mmc " ,
2013-10-23 16:57:50 +04:00
. pm = & tmio_mmc_dev_pm_ops ,
2008-07-15 19:02:21 +04:00
} ,
. probe = tmio_mmc_probe ,
2012-11-19 22:20:26 +04:00
. remove = tmio_mmc_remove ,
2008-07-15 19:02:21 +04:00
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( tmio_mmc_driver ) ;
2008-07-15 19:02:21 +04:00
MODULE_DESCRIPTION ( " Toshiba TMIO SD/MMC driver " ) ;
MODULE_AUTHOR ( " Ian Molton <spyro@f2s.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:tmio-mmc " ) ;