2011-07-26 16:58:19 +02: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 11:25:06 +02:00
void __iomem * host_regs ;
2011-07-26 16:58:19 +02: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-03 23:38:58 -05:00
ata_dev_info ( dev , " configured for PIO \n " ) ;
2011-07-26 16:58:19 +02: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-21 13:19:58 -08:00
static int pata_imx_probe ( struct platform_device * pdev )
2011-07-26 16:58:19 +02:00
{
struct ata_host * host ;
struct ata_port * ap ;
struct pata_imx_priv * priv ;
int irq = 0 ;
struct resource * io_res ;
2013-04-04 11:25:05 +02:00
int ret ;
2011-07-26 16:58:19 +02:00
io_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( io_res = = NULL )
return - EINVAL ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < = 0 )
return - EINVAL ;
priv = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct pata_imx_priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2013-04-04 11:25:04 +02:00
priv - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2011-07-26 16:58:19 +02:00
if ( IS_ERR ( priv - > clk ) ) {
dev_err ( & pdev - > dev , " Failed to get clock \n " ) ;
return PTR_ERR ( priv - > clk ) ;
}
2012-07-07 19:34:05 -03:00
clk_prepare_enable ( priv - > clk ) ;
2011-07-26 16:58:19 +02:00
host = ata_host_alloc ( & pdev - > dev , 1 ) ;
2013-04-04 11:25:05 +02:00
if ( ! host ) {
ret = - ENOMEM ;
goto err ;
}
2011-07-26 16:58:19 +02: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 ;
priv - > host_regs = devm_ioremap ( & pdev - > dev , io_res - > start ,
resource_size ( io_res ) ) ;
if ( ! priv - > host_regs ) {
dev_err ( & pdev - > dev , " failed to map IO/CTL base \n " ) ;
2013-04-04 11:25:05 +02:00
ret = - EBUSY ;
goto err ;
2011-07-26 16:58:19 +02: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 11:25:05 +02:00
ret = ata_host_activate ( host , irq , ata_sff_interrupt , 0 ,
2011-07-26 16:58:19 +02:00
& pata_imx_sht ) ;
2013-04-04 11:25:05 +02:00
if ( ret )
goto err ;
return 0 ;
err :
2012-07-07 19:34:05 -03:00
clk_disable_unprepare ( priv - > clk ) ;
2013-04-04 11:25:04 +02:00
2013-04-04 11:25:05 +02:00
return ret ;
2011-07-26 16:58:19 +02:00
}
2012-12-21 13:19:58 -08:00
static int pata_imx_remove ( struct platform_device * pdev )
2011-07-26 16:58:19 +02:00
{
2013-05-23 19:41:21 +09:00
struct ata_host * host = platform_get_drvdata ( pdev ) ;
2011-07-26 16:58:19 +02: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-07 19:34:05 -03:00
clk_disable_unprepare ( priv - > clk ) ;
2011-07-26 16:58:19 +02:00
return 0 ;
}
# ifdef CONFIG_PM
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-07 19:34:05 -03:00
clk_disable_unprepare ( priv - > clk ) ;
2011-07-26 16:58:19 +02: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 ;
2012-07-07 19:34:05 -03:00
clk_prepare_enable ( priv - > clk ) ;
2011-07-26 16:58:19 +02: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 ;
}
static const struct dev_pm_ops pata_imx_pm_ops = {
. suspend = pata_imx_suspend ,
. resume = pata_imx_resume ,
} ;
# endif
2013-04-04 11:25:07 +02:00
static const struct of_device_id imx_pata_dt_ids [ ] = {
{
. compatible = " fsl,imx27-pata " ,
} , {
/* sentinel */
}
} ;
2013-07-29 09:38:38 +02:00
MODULE_DEVICE_TABLE ( of , imx_pata_dt_ids ) ;
2013-04-04 11:25:07 +02:00
2011-07-26 16:58:19 +02:00
static struct platform_driver pata_imx_driver = {
. probe = pata_imx_probe ,
2012-12-21 13:19:58 -08:00
. remove = pata_imx_remove ,
2011-07-26 16:58:19 +02:00
. driver = {
. name = DRV_NAME ,
2013-04-04 11:25:07 +02:00
. of_match_table = imx_pata_dt_ids ,
2011-07-26 16:58:19 +02:00
. owner = THIS_MODULE ,
# ifdef CONFIG_PM
. pm = & pata_imx_pm_ops ,
# endif
} ,
} ;
2011-11-27 14:44:26 +08:00
module_platform_driver ( pata_imx_driver ) ;
2011-07-26 16:58:19 +02: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 ) ;