2010-03-04 20:06:06 +03:00
/*
* AHCI SATA platform driver
*
* Copyright 2004 - 2005 Red Hat , Inc .
* Jeff Garzik < jgarzik @ pobox . com >
* Copyright 2010 MontaVista Software , LLC .
* Anton Vorontsov < avorontsov @ ru . mvista . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*/
# include <linux/kernel.h>
2010-03-29 21:52:43 +04:00
# include <linux/gfp.h>
2010-03-04 20:06:06 +03:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/libata.h>
# include <linux/ahci_platform.h>
# include "ahci.h"
2011-09-28 11:41:54 +04:00
enum ahci_type {
AHCI , /* standard platform ahci */
IMX53_AHCI , /* ahci on i.mx53 */
} ;
static struct platform_device_id ahci_devtype [ ] = {
{
. name = " ahci " ,
. driver_data = AHCI ,
} , {
. name = " imx53-ahci " ,
. driver_data = IMX53_AHCI ,
} , {
/* sentinel */
}
} ;
MODULE_DEVICE_TABLE ( platform , ahci_devtype ) ;
static const struct ata_port_info ahci_port_info [ ] = {
/* by features */
[ AHCI ] = {
. flags = AHCI_FLAG_COMMON ,
. pio_mask = ATA_PIO4 ,
. udma_mask = ATA_UDMA6 ,
. port_ops = & ahci_ops ,
} ,
[ IMX53_AHCI ] = {
. flags = AHCI_FLAG_COMMON ,
. pio_mask = ATA_PIO4 ,
. udma_mask = ATA_UDMA6 ,
. port_ops = & ahci_pmp_retry_srst_ops ,
} ,
} ;
2010-09-21 11:25:48 +04:00
static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT ( " ahci_platform " ) ,
} ;
2010-03-04 20:06:06 +03:00
static int __init ahci_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2011-10-31 17:20:10 +04:00
struct ahci_platform_data * pdata = dev_get_platdata ( dev ) ;
2011-09-28 11:41:54 +04:00
const struct platform_device_id * id = platform_get_device_id ( pdev ) ;
2011-11-16 07:00:56 +04:00
struct ata_port_info pi = ahci_port_info [ id ? id - > driver_data : 0 ] ;
2010-03-04 20:06:06 +03:00
const struct ata_port_info * ppi [ ] = { & pi , NULL } ;
struct ahci_host_priv * hpriv ;
struct ata_host * host ;
struct resource * mem ;
int irq ;
int n_ports ;
int i ;
int rc ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! mem ) {
dev_err ( dev , " no mmio space \n " ) ;
return - EINVAL ;
}
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < = 0 ) {
dev_err ( dev , " no irq \n " ) ;
return - EINVAL ;
}
if ( pdata & & pdata - > ata_port_info )
pi = * pdata - > ata_port_info ;
hpriv = devm_kzalloc ( dev , sizeof ( * hpriv ) , GFP_KERNEL ) ;
if ( ! hpriv ) {
2010-06-25 13:21:19 +04:00
dev_err ( dev , " can't alloc ahci_host_priv \n " ) ;
return - ENOMEM ;
2010-03-04 20:06:06 +03:00
}
hpriv - > flags | = ( unsigned long ) pi . private_data ;
hpriv - > mmio = devm_ioremap ( dev , mem - > start , resource_size ( mem ) ) ;
if ( ! hpriv - > mmio ) {
dev_err ( dev , " can't map %pR \n " , mem ) ;
2010-06-25 13:21:19 +04:00
return - ENOMEM ;
}
/*
* Some platforms might need to prepare for mmio region access ,
* which could be done in the following init call . So , the mmio
* region shouldn ' t be accessed before init ( if provided ) has
* returned successfully .
*/
if ( pdata & & pdata - > init ) {
rc = pdata - > init ( dev , hpriv - > mmio ) ;
if ( rc )
return rc ;
2010-03-04 20:06:06 +03:00
}
ahci_save_initial_config ( dev , hpriv ,
pdata ? pdata - > force_port_map : 0 ,
pdata ? pdata - > mask_port_map : 0 ) ;
/* prepare host */
if ( hpriv - > cap & HOST_CAP_NCQ )
pi . flags | = ATA_FLAG_NCQ ;
if ( hpriv - > cap & HOST_CAP_PMP )
pi . flags | = ATA_FLAG_PMP ;
ahci_set_em_messages ( hpriv , & pi ) ;
/* CAP.NP sometimes indicate the index of the last enabled
* port , at other times , that of the last possible port , so
* determining the maximum port number requires looking at
* both CAP . NP and port_map .
*/
n_ports = max ( ahci_nr_ports ( hpriv - > cap ) , fls ( hpriv - > port_map ) ) ;
host = ata_host_alloc_pinfo ( dev , ppi , n_ports ) ;
if ( ! host ) {
rc = - ENOMEM ;
goto err0 ;
}
host - > private_data = hpriv ;
if ( ! ( hpriv - > cap & HOST_CAP_SSS ) | | ahci_ignore_sss )
host - > flags | = ATA_HOST_PARALLEL_SCAN ;
else
printk ( KERN_INFO " ahci: SSS flag set, parallel bus scan disabled \n " ) ;
if ( pi . flags & ATA_FLAG_EM )
ahci_reset_em ( host ) ;
for ( i = 0 ; i < host - > n_ports ; i + + ) {
struct ata_port * ap = host - > ports [ i ] ;
ata_port_desc ( ap , " mmio %pR " , mem ) ;
ata_port_desc ( ap , " port 0x%x " , 0x100 + ap - > port_no * 0x80 ) ;
/* set enclosure management message type */
if ( ap - > flags & ATA_FLAG_EM )
2010-05-15 01:23:37 +04:00
ap - > em_message_type = hpriv - > em_msg_type ;
2010-03-04 20:06:06 +03:00
/* disabled/not-implemented port */
if ( ! ( hpriv - > port_map & ( 1 < < i ) ) )
ap - > ops = & ata_dummy_port_ops ;
}
rc = ahci_reset_controller ( host ) ;
if ( rc )
goto err0 ;
ahci_init_controller ( host ) ;
ahci_print_info ( host , " platform " ) ;
rc = ata_host_activate ( host , irq , ahci_interrupt , IRQF_SHARED ,
2010-09-21 11:25:48 +04:00
& ahci_platform_sht ) ;
2010-03-04 20:06:06 +03:00
if ( rc )
goto err0 ;
return 0 ;
err0 :
if ( pdata & & pdata - > exit )
pdata - > exit ( dev ) ;
return rc ;
}
static int __devexit ahci_remove ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2011-10-31 17:20:10 +04:00
struct ahci_platform_data * pdata = dev_get_platdata ( dev ) ;
2010-03-04 20:06:06 +03:00
struct ata_host * host = dev_get_drvdata ( dev ) ;
ata_host_detach ( host ) ;
if ( pdata & & pdata - > exit )
pdata - > exit ( dev ) ;
return 0 ;
}
2010-11-04 05:04:59 +03:00
static const struct of_device_id ahci_of_match [ ] = {
{ . compatible = " calxeda,hb-ahci " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ahci_of_match ) ;
2010-03-04 20:06:06 +03:00
static struct platform_driver ahci_driver = {
. remove = __devexit_p ( ahci_remove ) ,
. driver = {
. name = " ahci " ,
. owner = THIS_MODULE ,
2010-11-04 05:04:59 +03:00
. of_match_table = ahci_of_match ,
2010-03-04 20:06:06 +03:00
} ,
2011-09-28 11:41:54 +04:00
. id_table = ahci_devtype ,
2010-03-04 20:06:06 +03:00
} ;
static int __init ahci_init ( void )
{
return platform_driver_probe ( & ahci_driver , ahci_probe ) ;
}
module_init ( ahci_init ) ;
static void __exit ahci_exit ( void )
{
platform_driver_unregister ( & ahci_driver ) ;
}
module_exit ( ahci_exit ) ;
MODULE_DESCRIPTION ( " AHCI SATA platform driver " ) ;
MODULE_AUTHOR ( " Anton Vorontsov <avorontsov@ru.mvista.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:ahci " ) ;