2011-07-26 18:58:19 +04:00
/*
* Freescale iMX PATA driver
*
* Copyright ( C ) 2011 Arnaud Patard < arnaud . patard @ rtp - net . org >
*
* Based on pata_platform - Copyright ( C ) 2006 - 2007 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* TODO :
* - dmaengine support
* - check if timing stuff needed
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/blkdev.h>
# include <scsi/scsi_host.h>
# include <linux/ata.h>
# include <linux/libata.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# define DRV_NAME "pata_imx"
# define PATA_IMX_ATA_CONTROL 0x24
# define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7)
# define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6)
# define PATA_IMX_ATA_CTRL_IORDY_EN (1<<0)
# define PATA_IMX_ATA_INT_EN 0x2C
# define PATA_IMX_ATA_INTR_ATA_INTRQ2 (1<<3)
# define PATA_IMX_DRIVE_DATA 0xA0
# define PATA_IMX_DRIVE_CONTROL 0xD8
struct pata_imx_priv {
struct clk * clk ;
/* timings/interrupt/control regs */
2013-04-04 13:25:06 +04:00
void __iomem * host_regs ;
2011-07-26 18:58:19 +04:00
u32 ata_ctl ;
} ;
static int pata_imx_set_mode ( struct ata_link * link , struct ata_device * * unused )
{
struct ata_device * dev ;
struct ata_port * ap = link - > ap ;
struct pata_imx_priv * priv = ap - > host - > private_data ;
u32 val ;
ata_for_each_dev ( dev , link , ENABLED ) {
dev - > pio_mode = dev - > xfer_mode = XFER_PIO_0 ;
dev - > xfer_shift = ATA_SHIFT_PIO ;
dev - > flags | = ATA_DFLAG_PIO ;
val = __raw_readl ( priv - > host_regs + PATA_IMX_ATA_CONTROL ) ;
if ( ata_pio_need_iordy ( dev ) )
val | = PATA_IMX_ATA_CTRL_IORDY_EN ;
else
val & = ~ PATA_IMX_ATA_CTRL_IORDY_EN ;
__raw_writel ( val , priv - > host_regs + PATA_IMX_ATA_CONTROL ) ;
2012-12-04 08:38:58 +04:00
ata_dev_info ( dev , " configured for PIO \n " ) ;
2011-07-26 18:58:19 +04:00
}
return 0 ;
}
static struct scsi_host_template pata_imx_sht = {
ATA_PIO_SHT ( DRV_NAME ) ,
} ;
static struct ata_port_operations pata_imx_port_ops = {
. inherits = & ata_sff_port_ops ,
. sff_data_xfer = ata_sff_data_xfer_noirq ,
. cable_detect = ata_cable_unknown ,
. set_mode = pata_imx_set_mode ,
} ;
static void pata_imx_setup_port ( struct ata_ioports * ioaddr )
{
/* Fixup the port shift for platforms that need it */
ioaddr - > data_addr = ioaddr - > cmd_addr + ( ATA_REG_DATA < < 2 ) ;
ioaddr - > error_addr = ioaddr - > cmd_addr + ( ATA_REG_ERR < < 2 ) ;
ioaddr - > feature_addr = ioaddr - > cmd_addr + ( ATA_REG_FEATURE < < 2 ) ;
ioaddr - > nsect_addr = ioaddr - > cmd_addr + ( ATA_REG_NSECT < < 2 ) ;
ioaddr - > lbal_addr = ioaddr - > cmd_addr + ( ATA_REG_LBAL < < 2 ) ;
ioaddr - > lbam_addr = ioaddr - > cmd_addr + ( ATA_REG_LBAM < < 2 ) ;
ioaddr - > lbah_addr = ioaddr - > cmd_addr + ( ATA_REG_LBAH < < 2 ) ;
ioaddr - > device_addr = ioaddr - > cmd_addr + ( ATA_REG_DEVICE < < 2 ) ;
ioaddr - > status_addr = ioaddr - > cmd_addr + ( ATA_REG_STATUS < < 2 ) ;
ioaddr - > command_addr = ioaddr - > cmd_addr + ( ATA_REG_CMD < < 2 ) ;
}
2012-12-22 01:19:58 +04:00
static int pata_imx_probe ( struct platform_device * pdev )
2011-07-26 18:58:19 +04:00
{
struct ata_host * host ;
struct ata_port * ap ;
struct pata_imx_priv * priv ;
int irq = 0 ;
struct resource * io_res ;
2013-04-04 13:25:05 +04:00
int ret ;
2011-07-26 18:58:19 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
2014-02-18 02:05:57 +04:00
if ( irq < 0 )
return irq ;
2011-07-26 18:58:19 +04:00
priv = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct pata_imx_priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2013-04-04 13:25:04 +04:00
priv - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2011-07-26 18:58:19 +04:00
if ( IS_ERR ( priv - > clk ) ) {
dev_err ( & pdev - > dev , " Failed to get clock \n " ) ;
return PTR_ERR ( priv - > clk ) ;
}
2014-01-26 01:46:23 +04:00
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret )
return ret ;
2011-07-26 18:58:19 +04:00
host = ata_host_alloc ( & pdev - > dev , 1 ) ;
2013-04-04 13:25:05 +04:00
if ( ! host ) {
ret = - ENOMEM ;
goto err ;
}
2011-07-26 18:58:19 +04:00
host - > private_data = priv ;
ap = host - > ports [ 0 ] ;
ap - > ops = & pata_imx_port_ops ;
ap - > pio_mask = ATA_PIO0 ;
ap - > flags | = ATA_FLAG_SLAVE_POSS ;
2014-02-18 02:05:56 +04:00
io_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
priv - > host_regs = devm_ioremap_resource ( & pdev - > dev , io_res ) ;
2014-03-14 21:33:13 +04:00
if ( IS_ERR ( priv - > host_regs ) ) {
ret = PTR_ERR ( priv - > host_regs ) ;
2013-04-04 13:25:05 +04:00
goto err ;
2011-07-26 18:58:19 +04:00
}
ap - > ioaddr . cmd_addr = priv - > host_regs + PATA_IMX_DRIVE_DATA ;
ap - > ioaddr . ctl_addr = priv - > host_regs + PATA_IMX_DRIVE_CONTROL ;
ap - > ioaddr . altstatus_addr = ap - > ioaddr . ctl_addr ;
pata_imx_setup_port ( & ap - > ioaddr ) ;
ata_port_desc ( ap , " cmd 0x%llx ctl 0x%llx " ,
( unsigned long long ) io_res - > start + PATA_IMX_DRIVE_DATA ,
( unsigned long long ) io_res - > start + PATA_IMX_DRIVE_CONTROL ) ;
/* deassert resets */
__raw_writel ( PATA_IMX_ATA_CTRL_FIFO_RST_B |
PATA_IMX_ATA_CTRL_ATA_RST_B ,
priv - > host_regs + PATA_IMX_ATA_CONTROL ) ;
/* enable interrupts */
__raw_writel ( PATA_IMX_ATA_INTR_ATA_INTRQ2 ,
priv - > host_regs + PATA_IMX_ATA_INT_EN ) ;
/* activate */
2013-04-04 13:25:05 +04:00
ret = ata_host_activate ( host , irq , ata_sff_interrupt , 0 ,
2011-07-26 18:58:19 +04:00
& pata_imx_sht ) ;
2013-04-04 13:25:05 +04:00
if ( ret )
goto err ;
return 0 ;
err :
2012-07-08 02:34:05 +04:00
clk_disable_unprepare ( priv - > clk ) ;
2013-04-04 13:25:04 +04:00
2013-04-04 13:25:05 +04:00
return ret ;
2011-07-26 18:58:19 +04:00
}
2012-12-22 01:19:58 +04:00
static int pata_imx_remove ( struct platform_device * pdev )
2011-07-26 18:58:19 +04:00
{
2013-05-23 14:41:21 +04:00
struct ata_host * host = platform_get_drvdata ( pdev ) ;
2011-07-26 18:58:19 +04:00
struct pata_imx_priv * priv = host - > private_data ;
ata_host_detach ( host ) ;
__raw_writel ( 0 , priv - > host_regs + PATA_IMX_ATA_INT_EN ) ;
2012-07-08 02:34:05 +04:00
clk_disable_unprepare ( priv - > clk ) ;
2011-07-26 18:58:19 +04:00
return 0 ;
}
2014-05-07 19:17:44 +04:00
# ifdef CONFIG_PM_SLEEP
2011-07-26 18:58:19 +04:00
static int pata_imx_suspend ( struct device * dev )
{
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct pata_imx_priv * priv = host - > private_data ;
int ret ;
ret = ata_host_suspend ( host , PMSG_SUSPEND ) ;
if ( ! ret ) {
__raw_writel ( 0 , priv - > host_regs + PATA_IMX_ATA_INT_EN ) ;
priv - > ata_ctl =
__raw_readl ( priv - > host_regs + PATA_IMX_ATA_CONTROL ) ;
2012-07-08 02:34:05 +04:00
clk_disable_unprepare ( priv - > clk ) ;
2011-07-26 18:58:19 +04:00
}
return ret ;
}
static int pata_imx_resume ( struct device * dev )
{
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct pata_imx_priv * priv = host - > private_data ;
2014-01-26 01:46:23 +04:00
int ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret )
return ret ;
2011-07-26 18:58:19 +04:00
__raw_writel ( priv - > ata_ctl , priv - > host_regs + PATA_IMX_ATA_CONTROL ) ;
__raw_writel ( PATA_IMX_ATA_INTR_ATA_INTRQ2 ,
priv - > host_regs + PATA_IMX_ATA_INT_EN ) ;
ata_host_resume ( host ) ;
return 0 ;
}
# endif
2014-10-04 16:03:06 +04:00
static SIMPLE_DEV_PM_OPS ( pata_imx_pm_ops , pata_imx_suspend , pata_imx_resume ) ;
2013-04-04 13:25:07 +04:00
static const struct of_device_id imx_pata_dt_ids [ ] = {
{
. compatible = " fsl,imx27-pata " ,
} , {
/* sentinel */
}
} ;
2013-07-29 11:38:38 +04:00
MODULE_DEVICE_TABLE ( of , imx_pata_dt_ids ) ;
2013-04-04 13:25:07 +04:00
2011-07-26 18:58:19 +04:00
static struct platform_driver pata_imx_driver = {
. probe = pata_imx_probe ,
2012-12-22 01:19:58 +04:00
. remove = pata_imx_remove ,
2011-07-26 18:58:19 +04:00
. driver = {
. name = DRV_NAME ,
2013-04-04 13:25:07 +04:00
. of_match_table = imx_pata_dt_ids ,
2011-07-26 18:58:19 +04:00
. pm = & pata_imx_pm_ops ,
} ,
} ;
2011-11-27 10:44:26 +04:00
module_platform_driver ( pata_imx_driver ) ;
2011-07-26 18:58:19 +04:00
MODULE_AUTHOR ( " Arnaud Patard <arnaud.patard@rtp-net.org> " ) ;
MODULE_DESCRIPTION ( " low-level driver for iMX PATA " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;