2014-02-26 18:47:21 +04:00
/*
* Copyright ( C ) 2012 STMicroelectronics Limited
*
* Authors : Francesco Virlinzi < francesco . virlinzi @ st . com >
* Alexandre Torgue < alexandre . torgue @ st . com >
*
* 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 .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/export.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/of.h>
# include <linux/ahci_platform.h>
# include <linux/libata.h>
# include <linux/reset.h>
# include <linux/io.h>
# include <linux/dma-mapping.h>
# include "ahci.h"
2015-01-29 02:30:29 +03:00
# define DRV_NAME "st_ahci"
2014-02-26 18:47:21 +04:00
# define ST_AHCI_OOBR 0xbc
# define ST_AHCI_OOBR_WE BIT(31)
# define ST_AHCI_OOBR_CWMIN_SHIFT 24
# define ST_AHCI_OOBR_CWMAX_SHIFT 16
# define ST_AHCI_OOBR_CIMIN_SHIFT 8
# define ST_AHCI_OOBR_CIMAX_SHIFT 0
struct st_ahci_drv_data {
struct platform_device * ahci ;
struct reset_control * pwr ;
struct reset_control * sw_rst ;
struct reset_control * pwr_rst ;
} ;
static void st_ahci_configure_oob ( void __iomem * mmio )
{
unsigned long old_val , new_val ;
new_val = ( 0x02 < < ST_AHCI_OOBR_CWMIN_SHIFT ) |
( 0x04 < < ST_AHCI_OOBR_CWMAX_SHIFT ) |
( 0x08 < < ST_AHCI_OOBR_CIMIN_SHIFT ) |
( 0x0C < < ST_AHCI_OOBR_CIMAX_SHIFT ) ;
old_val = readl ( mmio + ST_AHCI_OOBR ) ;
writel ( old_val | ST_AHCI_OOBR_WE , mmio + ST_AHCI_OOBR ) ;
writel ( new_val | ST_AHCI_OOBR_WE , mmio + ST_AHCI_OOBR ) ;
writel ( new_val , mmio + ST_AHCI_OOBR ) ;
}
2015-04-20 16:41:05 +03:00
static int st_ahci_deassert_resets ( struct ahci_host_priv * hpriv ,
struct device * dev )
2014-02-26 18:47:21 +04:00
{
2015-04-20 16:41:05 +03:00
struct st_ahci_drv_data * drv_data = hpriv - > plat_data ;
2014-02-26 18:47:21 +04:00
int err ;
if ( drv_data - > pwr ) {
err = reset_control_deassert ( drv_data - > pwr ) ;
if ( err ) {
dev_err ( dev , " unable to bring out of pwrdwn \n " ) ;
return err ;
}
}
if ( drv_data - > sw_rst ) {
err = reset_control_deassert ( drv_data - > sw_rst ) ;
if ( err ) {
dev_err ( dev , " unable to bring out of sw-rst \n " ) ;
return err ;
}
}
if ( drv_data - > pwr_rst ) {
err = reset_control_deassert ( drv_data - > pwr_rst ) ;
if ( err ) {
dev_err ( dev , " unable to bring out of pwr-rst \n " ) ;
return err ;
}
}
return 0 ;
}
2014-03-14 22:21:59 +04:00
static void st_ahci_host_stop ( struct ata_host * host )
2014-03-12 16:39:40 +04:00
{
2014-03-14 22:21:59 +04:00
struct ahci_host_priv * hpriv = host - > private_data ;
2015-04-20 16:41:05 +03:00
struct st_ahci_drv_data * drv_data = hpriv - > plat_data ;
2014-03-14 22:21:59 +04:00
struct device * dev = host - > dev ;
2014-03-12 16:39:40 +04:00
int err ;
if ( drv_data - > pwr ) {
err = reset_control_assert ( drv_data - > pwr ) ;
if ( err )
2014-03-14 22:20:58 +04:00
dev_err ( dev , " unable to pwrdwn \n " ) ;
2014-03-12 16:39:40 +04:00
}
ahci_platform_disable_resources ( hpriv ) ;
}
2015-04-20 16:41:05 +03:00
static int st_ahci_probe_resets ( struct ahci_host_priv * hpriv ,
struct device * dev )
2014-02-26 18:47:21 +04:00
{
2015-04-20 16:41:05 +03:00
struct st_ahci_drv_data * drv_data = hpriv - > plat_data ;
2014-02-26 18:47:21 +04:00
2015-04-20 16:41:05 +03:00
drv_data - > pwr = devm_reset_control_get ( dev , " pwr-dwn " ) ;
2014-02-26 18:47:21 +04:00
if ( IS_ERR ( drv_data - > pwr ) ) {
2015-04-20 16:41:05 +03:00
dev_info ( dev , " power reset control not defined \n " ) ;
2014-02-26 18:47:21 +04:00
drv_data - > pwr = NULL ;
}
2015-04-20 16:41:05 +03:00
drv_data - > sw_rst = devm_reset_control_get ( dev , " sw-rst " ) ;
2014-02-26 18:47:21 +04:00
if ( IS_ERR ( drv_data - > sw_rst ) ) {
2015-04-20 16:41:05 +03:00
dev_info ( dev , " soft reset control not defined \n " ) ;
2014-02-26 18:47:21 +04:00
drv_data - > sw_rst = NULL ;
}
2015-04-20 16:41:05 +03:00
drv_data - > pwr_rst = devm_reset_control_get ( dev , " pwr-rst " ) ;
2014-02-26 18:47:21 +04:00
if ( IS_ERR ( drv_data - > pwr_rst ) ) {
2015-04-20 16:41:05 +03:00
dev_dbg ( dev , " power soft reset control not defined \n " ) ;
2014-02-26 18:47:21 +04:00
drv_data - > pwr_rst = NULL ;
}
2015-04-20 16:41:05 +03:00
return st_ahci_deassert_resets ( hpriv , dev ) ;
2014-02-26 18:47:21 +04:00
}
2014-03-14 22:21:59 +04:00
static struct ata_port_operations st_ahci_port_ops = {
. inherits = & ahci_platform_ops ,
. host_stop = st_ahci_host_stop ,
} ;
2014-02-26 18:47:21 +04:00
static const struct ata_port_info st_ahci_port_info = {
. flags = AHCI_FLAG_COMMON ,
. pio_mask = ATA_PIO4 ,
. udma_mask = ATA_UDMA6 ,
2014-03-14 22:21:59 +04:00
. port_ops = & st_ahci_port_ops ,
2014-02-26 18:47:21 +04:00
} ;
2015-01-29 02:30:29 +03:00
static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT ( DRV_NAME ) ,
} ;
2014-02-26 18:47:21 +04:00
static int st_ahci_probe ( struct platform_device * pdev )
{
2016-08-10 18:37:29 +03:00
struct device * dev = & pdev - > dev ;
2014-02-26 18:47:21 +04:00
struct st_ahci_drv_data * drv_data ;
struct ahci_host_priv * hpriv ;
int err ;
drv_data = devm_kzalloc ( & pdev - > dev , sizeof ( * drv_data ) , GFP_KERNEL ) ;
if ( ! drv_data )
return - ENOMEM ;
2018-08-22 15:13:01 +03:00
hpriv = ahci_platform_get_resources ( pdev , 0 ) ;
2014-02-26 18:47:21 +04:00
if ( IS_ERR ( hpriv ) )
return PTR_ERR ( hpriv ) ;
2015-04-20 16:41:05 +03:00
hpriv - > plat_data = drv_data ;
2014-02-26 18:47:21 +04:00
2015-04-20 16:41:05 +03:00
err = st_ahci_probe_resets ( hpriv , & pdev - > dev ) ;
2014-02-26 18:47:21 +04:00
if ( err )
return err ;
err = ahci_platform_enable_resources ( hpriv ) ;
if ( err )
return err ;
2015-04-20 16:41:05 +03:00
st_ahci_configure_oob ( hpriv - > mmio ) ;
2015-03-31 10:35:09 +03:00
2016-08-10 18:37:29 +03:00
of_property_read_u32 ( dev - > of_node ,
" ports-implemented " , & hpriv - > force_port_map ) ;
2015-01-29 02:30:29 +03:00
err = ahci_platform_init_host ( pdev , hpriv , & st_ahci_port_info ,
& ahci_platform_sht ) ;
2014-02-26 18:47:21 +04:00
if ( err ) {
ahci_platform_disable_resources ( hpriv ) ;
return err ;
}
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int st_ahci_suspend ( struct device * dev )
{
2015-04-20 16:41:05 +03:00
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
struct st_ahci_drv_data * drv_data = hpriv - > plat_data ;
2014-02-26 18:47:21 +04:00
int err ;
2014-03-14 22:20:58 +04:00
err = ahci_platform_suspend_host ( dev ) ;
if ( err )
return err ;
2014-03-12 16:39:42 +04:00
2014-02-26 18:47:21 +04:00
if ( drv_data - > pwr ) {
err = reset_control_assert ( drv_data - > pwr ) ;
if ( err ) {
dev_err ( dev , " unable to pwrdwn " ) ;
return err ;
}
}
ahci_platform_disable_resources ( hpriv ) ;
return 0 ;
}
static int st_ahci_resume ( struct device * dev )
{
2015-04-20 16:41:05 +03:00
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
2014-02-26 18:47:21 +04:00
int err ;
err = ahci_platform_enable_resources ( hpriv ) ;
if ( err )
return err ;
2015-04-20 16:41:05 +03:00
err = st_ahci_deassert_resets ( hpriv , dev ) ;
2014-02-26 18:47:21 +04:00
if ( err ) {
ahci_platform_disable_resources ( hpriv ) ;
return err ;
}
2015-04-20 16:41:05 +03:00
st_ahci_configure_oob ( hpriv - > mmio ) ;
2015-03-31 10:35:09 +03:00
2014-03-12 16:39:42 +04:00
return ahci_platform_resume_host ( dev ) ;
2014-02-26 18:47:21 +04:00
}
# endif
static SIMPLE_DEV_PM_OPS ( st_ahci_pm_ops , st_ahci_suspend , st_ahci_resume ) ;
2014-07-22 18:15:43 +04:00
static const struct of_device_id st_ahci_match [ ] = {
2014-02-26 18:47:21 +04:00
{ . compatible = " st,ahci " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , st_ahci_match ) ;
static struct platform_driver st_ahci_driver = {
. driver = {
2015-01-29 02:30:29 +03:00
. name = DRV_NAME ,
2014-02-26 18:47:21 +04:00
. pm = & st_ahci_pm_ops ,
. of_match_table = of_match_ptr ( st_ahci_match ) ,
} ,
. probe = st_ahci_probe ,
2014-03-12 16:39:40 +04:00
. remove = ata_platform_remove_one ,
2014-02-26 18:47:21 +04:00
} ;
module_platform_driver ( st_ahci_driver ) ;
MODULE_AUTHOR ( " Alexandre Torgue <alexandre.torgue@st.com> " ) ;
MODULE_AUTHOR ( " Francesco Virlinzi <francesco.virlinzi@st.com> " ) ;
2014-03-12 16:39:38 +04:00
MODULE_DESCRIPTION ( " STMicroelectronics SATA AHCI Driver " ) ;
2014-02-26 18:47:21 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;