2019-05-20 20:08:10 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-09-07 11:23:16 +03:00
/*
* Freescale QorIQ AHCI SATA platform driver
*
* Copyright 2015 Freescale , Inc .
* Tang Yuantian < Yuantian . Tang @ freescale . com >
*/
2020-08-17 11:22:04 +03:00
# include <linux/acpi.h>
2015-09-07 11:23:16 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pm.h>
# include <linux/ahci_platform.h>
# include <linux/device.h>
# include <linux/of_address.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/libata.h>
# include "ahci.h"
# define DRV_NAME "ahci-qoriq"
/* port register definition */
# define PORT_PHY1 0xA8
# define PORT_PHY2 0xAC
# define PORT_PHY3 0xB0
# define PORT_PHY4 0xB4
# define PORT_PHY5 0xB8
2016-08-09 04:51:22 +03:00
# define PORT_AXICC 0xBC
2015-09-07 11:23:16 +03:00
# define PORT_TRANS 0xC8
/* port register default value */
# define AHCI_PORT_PHY_1_CFG 0xa003fffe
2017-12-04 12:01:20 +03:00
# define AHCI_PORT_PHY2_CFG 0x28184d1f
# define AHCI_PORT_PHY3_CFG 0x0e081509
2015-12-16 08:43:50 +03:00
# define AHCI_PORT_TRANS_CFG 0x08000029
2016-08-09 04:51:22 +03:00
# define AHCI_PORT_AXICC_CFG 0x3fffffff
2015-12-16 09:00:35 +03:00
/* for ls1021a */
# define LS1021A_PORT_PHY2 0x28183414
# define LS1021A_PORT_PHY3 0x0e080e06
# define LS1021A_PORT_PHY4 0x064a080b
# define LS1021A_PORT_PHY5 0x2aa86470
2016-08-09 04:51:22 +03:00
# define LS1021A_AXICC_ADDR 0xC0
2015-09-07 11:23:16 +03:00
# define SATA_ECC_DISABLE 0x00020000
2017-01-20 09:59:35 +03:00
# define ECC_DIS_ARMV8_CH2 0x80000000
2017-06-02 10:23:03 +03:00
# define ECC_DIS_LS1088A 0x40000000
2015-09-07 11:23:16 +03:00
enum ahci_qoriq_type {
AHCI_LS1021A ,
2019-03-11 10:20:14 +03:00
AHCI_LS1028A ,
2015-09-07 11:23:16 +03:00
AHCI_LS1043A ,
2015-10-29 09:22:15 +03:00
AHCI_LS2080A ,
2016-10-09 11:51:04 +03:00
AHCI_LS1046A ,
2017-06-02 10:23:03 +03:00
AHCI_LS1088A ,
2017-01-20 09:59:36 +03:00
AHCI_LS2088A ,
2019-03-12 04:50:19 +03:00
AHCI_LX2160A ,
2015-09-07 11:23:16 +03:00
} ;
struct ahci_qoriq_priv {
struct ccsr_ahci * reg_base ;
enum ahci_qoriq_type type ;
void __iomem * ecc_addr ;
2017-01-20 09:59:34 +03:00
bool is_dmacoherent ;
2015-09-07 11:23:16 +03:00
} ;
2019-03-12 04:50:19 +03:00
static bool ecc_initialized ;
2015-09-07 11:23:16 +03:00
static const struct of_device_id ahci_qoriq_of_match [ ] = {
{ . compatible = " fsl,ls1021a-ahci " , . data = ( void * ) AHCI_LS1021A } ,
2019-03-11 10:20:14 +03:00
{ . compatible = " fsl,ls1028a-ahci " , . data = ( void * ) AHCI_LS1028A } ,
2015-09-07 11:23:16 +03:00
{ . compatible = " fsl,ls1043a-ahci " , . data = ( void * ) AHCI_LS1043A } ,
2015-10-29 09:22:15 +03:00
{ . compatible = " fsl,ls2080a-ahci " , . data = ( void * ) AHCI_LS2080A } ,
2016-10-09 11:51:04 +03:00
{ . compatible = " fsl,ls1046a-ahci " , . data = ( void * ) AHCI_LS1046A } ,
2017-06-02 10:23:03 +03:00
{ . compatible = " fsl,ls1088a-ahci " , . data = ( void * ) AHCI_LS1088A } ,
2017-01-20 09:59:36 +03:00
{ . compatible = " fsl,ls2088a-ahci " , . data = ( void * ) AHCI_LS2088A } ,
2019-03-12 04:50:19 +03:00
{ . compatible = " fsl,lx2160a-ahci " , . data = ( void * ) AHCI_LX2160A } ,
2022-03-03 11:36:35 +03:00
{ /* sentinel */ }
2015-09-07 11:23:16 +03:00
} ;
MODULE_DEVICE_TABLE ( of , ahci_qoriq_of_match ) ;
2020-08-17 11:22:04 +03:00
static const struct acpi_device_id ahci_qoriq_acpi_match [ ] = {
{ " NXP0004 " , . driver_data = ( kernel_ulong_t ) AHCI_LX2160A } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , ahci_qoriq_acpi_match ) ;
2015-09-07 11:23:16 +03:00
static int ahci_qoriq_hardreset ( struct ata_link * link , unsigned int * class ,
unsigned long deadline )
{
const unsigned long * timing = sata_ehc_deb_timing ( & link - > eh_context ) ;
void __iomem * port_mmio = ahci_port_base ( link - > ap ) ;
u32 px_cmd , px_is , px_val ;
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
struct ahci_qoriq_priv * qoriq_priv = hpriv - > plat_data ;
u8 * d2h_fis = pp - > rx_fis + RX_FIS_D2H_REG ;
struct ata_taskfile tf ;
bool online ;
int rc ;
2015-10-14 11:46:52 +03:00
bool ls1021a_workaround = ( qoriq_priv - > type = = AHCI_LS1021A ) ;
2015-09-07 11:23:16 +03:00
2018-04-13 07:32:30 +03:00
hpriv - > stop_engine ( ap ) ;
2015-09-07 11:23:16 +03:00
/*
* There is a errata on ls1021a Rev1 .0 and Rev2 .0 which is :
* A - 00 9042 : The device detection initialization sequence
* mistakenly resets some registers .
*
* Workaround for this is :
* The software should read and store PxCMD and PxIS values
* before issuing the device detection initialization sequence .
* After the sequence is complete , software should restore the
* PxCMD and PxIS with the stored values .
*/
2015-10-14 11:46:52 +03:00
if ( ls1021a_workaround ) {
2015-09-07 11:23:16 +03:00
px_cmd = readl ( port_mmio + PORT_CMD ) ;
px_is = readl ( port_mmio + PORT_IRQ_STAT ) ;
}
/* clear D2H reception area to properly wait for D2H FIS */
ata_tf_init ( link - > device , & tf ) ;
2022-02-15 21:49:26 +03:00
tf . status = ATA_BUSY ;
2015-09-07 11:23:16 +03:00
ata_tf_to_fis ( & tf , 0 , 0 , d2h_fis ) ;
rc = sata_link_hardreset ( link , timing , deadline , & online ,
ahci_check_ready ) ;
/* restore the PxCMD and PxIS on ls1021 */
2015-10-14 11:46:52 +03:00
if ( ls1021a_workaround ) {
2015-09-07 11:23:16 +03:00
px_val = readl ( port_mmio + PORT_CMD ) ;
if ( px_val ! = px_cmd )
writel ( px_cmd , port_mmio + PORT_CMD ) ;
px_val = readl ( port_mmio + PORT_IRQ_STAT ) ;
if ( px_val ! = px_is )
writel ( px_is , port_mmio + PORT_IRQ_STAT ) ;
}
hpriv - > start_engine ( ap ) ;
if ( online )
* class = ahci_dev_classify ( ap ) ;
return rc ;
}
static struct ata_port_operations ahci_qoriq_ops = {
. inherits = & ahci_ops ,
. hardreset = ahci_qoriq_hardreset ,
} ;
2016-09-30 09:13:13 +03:00
static const struct ata_port_info ahci_qoriq_port_info = {
2015-09-07 11:23:16 +03:00
. flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ ,
. pio_mask = ATA_PIO4 ,
. udma_mask = ATA_UDMA6 ,
. port_ops = & ahci_qoriq_ops ,
} ;
static struct scsi_host_template ahci_qoriq_sht = {
AHCI_SHT ( DRV_NAME ) ,
} ;
static int ahci_qoriq_phy_init ( struct ahci_host_priv * hpriv )
{
struct ahci_qoriq_priv * qpriv = hpriv - > plat_data ;
void __iomem * reg_base = hpriv - > mmio ;
switch ( qpriv - > type ) {
case AHCI_LS1021A :
2019-03-12 04:50:19 +03:00
if ( ! ( qpriv - > ecc_addr | | ecc_initialized ) )
2017-01-20 09:59:35 +03:00
return - EINVAL ;
2019-03-12 04:50:19 +03:00
else if ( qpriv - > ecc_addr & & ! ecc_initialized )
writel ( SATA_ECC_DISABLE , qpriv - > ecc_addr ) ;
2015-09-07 11:23:16 +03:00
writel ( AHCI_PORT_PHY_1_CFG , reg_base + PORT_PHY1 ) ;
2015-12-16 09:00:35 +03:00
writel ( LS1021A_PORT_PHY2 , reg_base + PORT_PHY2 ) ;
writel ( LS1021A_PORT_PHY3 , reg_base + PORT_PHY3 ) ;
writel ( LS1021A_PORT_PHY4 , reg_base + PORT_PHY4 ) ;
writel ( LS1021A_PORT_PHY5 , reg_base + PORT_PHY5 ) ;
2015-09-07 11:23:16 +03:00
writel ( AHCI_PORT_TRANS_CFG , reg_base + PORT_TRANS ) ;
2017-01-20 09:59:34 +03:00
if ( qpriv - > is_dmacoherent )
writel ( AHCI_PORT_AXICC_CFG ,
reg_base + LS1021A_AXICC_ADDR ) ;
2015-09-07 11:23:16 +03:00
break ;
case AHCI_LS1043A :
2019-03-12 04:50:19 +03:00
if ( ! ( qpriv - > ecc_addr | | ecc_initialized ) )
2017-01-20 09:59:35 +03:00
return - EINVAL ;
2019-03-12 04:50:19 +03:00
else if ( qpriv - > ecc_addr & & ! ecc_initialized )
writel ( readl ( qpriv - > ecc_addr ) |
ECC_DIS_ARMV8_CH2 ,
qpriv - > ecc_addr ) ;
2015-12-16 08:43:49 +03:00
writel ( AHCI_PORT_PHY_1_CFG , reg_base + PORT_PHY1 ) ;
2017-12-04 12:01:20 +03:00
writel ( AHCI_PORT_PHY2_CFG , reg_base + PORT_PHY2 ) ;
writel ( AHCI_PORT_PHY3_CFG , reg_base + PORT_PHY3 ) ;
2015-12-16 08:43:49 +03:00
writel ( AHCI_PORT_TRANS_CFG , reg_base + PORT_TRANS ) ;
2017-01-20 09:59:34 +03:00
if ( qpriv - > is_dmacoherent )
writel ( AHCI_PORT_AXICC_CFG , reg_base + PORT_AXICC ) ;
2015-12-16 08:43:49 +03:00
break ;
2015-10-29 09:22:15 +03:00
case AHCI_LS2080A :
2015-09-07 11:23:16 +03:00
writel ( AHCI_PORT_PHY_1_CFG , reg_base + PORT_PHY1 ) ;
2017-12-04 12:01:20 +03:00
writel ( AHCI_PORT_PHY2_CFG , reg_base + PORT_PHY2 ) ;
writel ( AHCI_PORT_PHY3_CFG , reg_base + PORT_PHY3 ) ;
2015-12-16 08:43:50 +03:00
writel ( AHCI_PORT_TRANS_CFG , reg_base + PORT_TRANS ) ;
2017-01-20 09:59:34 +03:00
if ( qpriv - > is_dmacoherent )
writel ( AHCI_PORT_AXICC_CFG , reg_base + PORT_AXICC ) ;
2015-09-07 11:23:16 +03:00
break ;
2016-10-09 11:51:04 +03:00
case AHCI_LS1046A :
2019-03-12 04:50:19 +03:00
if ( ! ( qpriv - > ecc_addr | | ecc_initialized ) )
2017-01-20 09:59:35 +03:00
return - EINVAL ;
2019-03-12 04:50:19 +03:00
else if ( qpriv - > ecc_addr & & ! ecc_initialized )
writel ( readl ( qpriv - > ecc_addr ) |
ECC_DIS_ARMV8_CH2 ,
qpriv - > ecc_addr ) ;
2016-10-09 11:51:04 +03:00
writel ( AHCI_PORT_PHY_1_CFG , reg_base + PORT_PHY1 ) ;
2017-12-04 12:01:20 +03:00
writel ( AHCI_PORT_PHY2_CFG , reg_base + PORT_PHY2 ) ;
writel ( AHCI_PORT_PHY3_CFG , reg_base + PORT_PHY3 ) ;
2016-10-09 11:51:04 +03:00
writel ( AHCI_PORT_TRANS_CFG , reg_base + PORT_TRANS ) ;
2017-01-20 09:59:34 +03:00
if ( qpriv - > is_dmacoherent )
writel ( AHCI_PORT_AXICC_CFG , reg_base + PORT_AXICC ) ;
2016-10-09 11:51:04 +03:00
break ;
2017-01-20 09:59:36 +03:00
2019-03-11 10:20:14 +03:00
case AHCI_LS1028A :
2017-06-02 10:23:03 +03:00
case AHCI_LS1088A :
2019-03-12 04:50:19 +03:00
case AHCI_LX2160A :
if ( ! ( qpriv - > ecc_addr | | ecc_initialized ) )
2017-06-02 10:23:03 +03:00
return - EINVAL ;
2019-03-12 04:50:19 +03:00
else if ( qpriv - > ecc_addr & & ! ecc_initialized )
writel ( readl ( qpriv - > ecc_addr ) |
ECC_DIS_LS1088A ,
qpriv - > ecc_addr ) ;
2017-06-02 10:23:03 +03:00
writel ( AHCI_PORT_PHY_1_CFG , reg_base + PORT_PHY1 ) ;
2017-12-04 12:01:20 +03:00
writel ( AHCI_PORT_PHY2_CFG , reg_base + PORT_PHY2 ) ;
writel ( AHCI_PORT_PHY3_CFG , reg_base + PORT_PHY3 ) ;
2017-06-02 10:23:03 +03:00
writel ( AHCI_PORT_TRANS_CFG , reg_base + PORT_TRANS ) ;
if ( qpriv - > is_dmacoherent )
writel ( AHCI_PORT_AXICC_CFG , reg_base + PORT_AXICC ) ;
break ;
2017-01-20 09:59:36 +03:00
case AHCI_LS2088A :
writel ( AHCI_PORT_PHY_1_CFG , reg_base + PORT_PHY1 ) ;
2017-12-04 12:01:20 +03:00
writel ( AHCI_PORT_PHY2_CFG , reg_base + PORT_PHY2 ) ;
writel ( AHCI_PORT_PHY3_CFG , reg_base + PORT_PHY3 ) ;
2017-01-20 09:59:36 +03:00
writel ( AHCI_PORT_TRANS_CFG , reg_base + PORT_TRANS ) ;
if ( qpriv - > is_dmacoherent )
writel ( AHCI_PORT_AXICC_CFG , reg_base + PORT_AXICC ) ;
break ;
2015-09-07 11:23:16 +03:00
}
2019-03-12 04:50:19 +03:00
ecc_initialized = true ;
2015-09-07 11:23:16 +03:00
return 0 ;
}
static int ahci_qoriq_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
2020-08-17 11:22:04 +03:00
const struct acpi_device_id * acpi_id ;
2015-09-07 11:23:16 +03:00
struct device * dev = & pdev - > dev ;
struct ahci_host_priv * hpriv ;
struct ahci_qoriq_priv * qoriq_priv ;
const struct of_device_id * of_id ;
struct resource * res ;
int rc ;
2018-08-22 15:13:01 +03:00
hpriv = ahci_platform_get_resources ( pdev , 0 ) ;
2015-09-07 11:23:16 +03:00
if ( IS_ERR ( hpriv ) )
return PTR_ERR ( hpriv ) ;
of_id = of_match_node ( ahci_qoriq_of_match , np ) ;
2020-08-17 11:22:04 +03:00
acpi_id = acpi_match_device ( ahci_qoriq_acpi_match , & pdev - > dev ) ;
if ( ! ( of_id | | acpi_id ) )
2015-09-07 11:23:16 +03:00
return - ENODEV ;
qoriq_priv = devm_kzalloc ( dev , sizeof ( * qoriq_priv ) , GFP_KERNEL ) ;
if ( ! qoriq_priv )
return - ENOMEM ;
2020-08-17 11:22:04 +03:00
if ( of_id )
2022-10-14 05:03:49 +03:00
qoriq_priv - > type = ( unsigned long ) of_id - > data ;
2020-08-17 11:22:04 +03:00
else
qoriq_priv - > type = ( enum ahci_qoriq_type ) acpi_id - > driver_data ;
2015-09-07 11:23:16 +03:00
2019-03-12 04:50:19 +03:00
if ( unlikely ( ! ecc_initialized ) ) {
res = platform_get_resource_byname ( pdev ,
IORESOURCE_MEM ,
" sata-ecc " ) ;
if ( res ) {
qoriq_priv - > ecc_addr =
devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( qoriq_priv - > ecc_addr ) )
return PTR_ERR ( qoriq_priv - > ecc_addr ) ;
}
2015-09-07 11:23:16 +03:00
}
2019-03-12 04:50:19 +03:00
2020-08-17 11:22:04 +03:00
if ( device_get_dma_attr ( & pdev - > dev ) = = DEV_DMA_COHERENT )
qoriq_priv - > is_dmacoherent = true ;
2015-09-07 11:23:16 +03:00
rc = ahci_platform_enable_resources ( hpriv ) ;
if ( rc )
return rc ;
hpriv - > plat_data = qoriq_priv ;
rc = ahci_qoriq_phy_init ( hpriv ) ;
if ( rc )
goto disable_resources ;
rc = ahci_platform_init_host ( pdev , hpriv , & ahci_qoriq_port_info ,
& ahci_qoriq_sht ) ;
if ( rc )
goto disable_resources ;
return 0 ;
disable_resources :
ahci_platform_disable_resources ( hpriv ) ;
return rc ;
}
# ifdef CONFIG_PM_SLEEP
static int ahci_qoriq_resume ( struct device * dev )
{
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
int rc ;
rc = ahci_platform_enable_resources ( hpriv ) ;
if ( rc )
return rc ;
rc = ahci_qoriq_phy_init ( hpriv ) ;
if ( rc )
goto disable_resources ;
rc = ahci_platform_resume_host ( dev ) ;
if ( rc )
goto disable_resources ;
/* We resumed so update PM runtime state */
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
return 0 ;
disable_resources :
ahci_platform_disable_resources ( hpriv ) ;
return rc ;
}
# endif
static SIMPLE_DEV_PM_OPS ( ahci_qoriq_pm_ops , ahci_platform_suspend ,
ahci_qoriq_resume ) ;
static struct platform_driver ahci_qoriq_driver = {
. probe = ahci_qoriq_probe ,
. remove = ata_platform_remove_one ,
. driver = {
. name = DRV_NAME ,
. of_match_table = ahci_qoriq_of_match ,
2020-08-17 11:22:04 +03:00
. acpi_match_table = ahci_qoriq_acpi_match ,
2015-09-07 11:23:16 +03:00
. pm = & ahci_qoriq_pm_ops ,
} ,
} ;
module_platform_driver ( ahci_qoriq_driver ) ;
MODULE_DESCRIPTION ( " Freescale QorIQ AHCI SATA platform driver " ) ;
MODULE_AUTHOR ( " Tang Yuantian <Yuantian.Tang@freescale.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;