2019-05-20 20:08:10 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-03-25 22:51:39 +04:00
/*
* AHCI SATA platform library
*
* Copyright 2004 - 2005 Red Hat , Inc .
* Jeff Garzik < jgarzik @ pobox . com >
* Copyright 2010 MontaVista Software , LLC .
* Anton Vorontsov < avorontsov @ ru . mvista . com >
*/
# include <linux/clk.h>
# include <linux/kernel.h>
# include <linux/gfp.h>
# include <linux/module.h>
# include <linux/pm.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/libata.h>
# include <linux/ahci_platform.h>
# include <linux/phy/phy.h>
# include <linux/pm_runtime.h>
2023-07-14 20:40:50 +03:00
# include <linux/of.h>
2015-01-15 17:09:37 +03:00
# include <linux/of_platform.h>
2018-08-22 15:13:02 +03:00
# include <linux/reset.h>
2014-03-25 22:51:39 +04:00
# include "ahci.h"
static void ahci_host_stop ( struct ata_host * host ) ;
struct ata_port_operations ahci_platform_ops = {
. inherits = & ahci_ops ,
. host_stop = ahci_host_stop ,
} ;
EXPORT_SYMBOL_GPL ( ahci_platform_ops ) ;
2014-07-30 22:13:57 +04:00
/**
* ahci_platform_enable_phys - Enable PHYs
* @ hpriv : host private area to store config values
*
* This function enables all the PHYs found in hpriv - > phys , if any .
* If a PHY fails to be enabled , it disables all the PHYs already
* enabled in reverse order and returns an error .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
2019-12-10 21:53:44 +03:00
int ahci_platform_enable_phys ( struct ahci_host_priv * hpriv )
2014-07-30 22:13:57 +04:00
{
int rc , i ;
for ( i = 0 ; i < hpriv - > nports ; i + + ) {
rc = phy_init ( hpriv - > phys [ i ] ) ;
if ( rc )
goto disable_phys ;
2018-12-04 22:28:25 +03:00
rc = phy_set_mode ( hpriv - > phys [ i ] , PHY_MODE_SATA ) ;
if ( rc ) {
phy_exit ( hpriv - > phys [ i ] ) ;
goto disable_phys ;
}
2014-07-30 22:13:57 +04:00
rc = phy_power_on ( hpriv - > phys [ i ] ) ;
2022-02-04 00:44:42 +03:00
if ( rc ) {
2014-07-30 22:13:57 +04:00
phy_exit ( hpriv - > phys [ i ] ) ;
goto disable_phys ;
}
}
return 0 ;
disable_phys :
while ( - - i > = 0 ) {
phy_power_off ( hpriv - > phys [ i ] ) ;
phy_exit ( hpriv - > phys [ i ] ) ;
}
return rc ;
}
2019-12-10 21:53:44 +03:00
EXPORT_SYMBOL_GPL ( ahci_platform_enable_phys ) ;
2014-07-30 22:13:57 +04:00
/**
* ahci_platform_disable_phys - Disable PHYs
* @ hpriv : host private area to store config values
*
* This function disables all PHYs found in hpriv - > phys .
*/
2019-12-10 21:53:44 +03:00
void ahci_platform_disable_phys ( struct ahci_host_priv * hpriv )
2014-07-30 22:13:57 +04:00
{
int i ;
for ( i = 0 ; i < hpriv - > nports ; i + + ) {
phy_power_off ( hpriv - > phys [ i ] ) ;
phy_exit ( hpriv - > phys [ i ] ) ;
}
}
2019-12-10 21:53:44 +03:00
EXPORT_SYMBOL_GPL ( ahci_platform_disable_phys ) ;
2014-07-30 22:13:57 +04:00
2022-09-09 22:36:16 +03:00
/**
* ahci_platform_find_clk - Find platform clock
* @ hpriv : host private area to store config values
* @ con_id : clock connection ID
*
* This function returns a pointer to the clock descriptor of the clock with
* the passed ID .
*
* RETURNS :
* Pointer to the clock descriptor on success otherwise NULL
*/
struct clk * ahci_platform_find_clk ( struct ahci_host_priv * hpriv , const char * con_id )
{
int i ;
for ( i = 0 ; i < hpriv - > n_clks ; i + + ) {
2022-12-06 11:34:16 +03:00
if ( hpriv - > clks [ i ] . id & & ! strcmp ( hpriv - > clks [ i ] . id , con_id ) )
2022-09-09 22:36:16 +03:00
return hpriv - > clks [ i ] . clk ;
}
return NULL ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_find_clk ) ;
2014-03-25 22:51:39 +04:00
/**
* ahci_platform_enable_clks - Enable platform clocks
* @ hpriv : host private area to store config values
*
2022-09-09 22:36:05 +03:00
* This function enables all the clks found for the AHCI device .
2014-03-25 22:51:39 +04:00
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_enable_clks ( struct ahci_host_priv * hpriv )
{
2022-09-09 22:36:05 +03:00
return clk_bulk_prepare_enable ( hpriv - > n_clks , hpriv - > clks ) ;
2014-03-25 22:51:39 +04:00
}
EXPORT_SYMBOL_GPL ( ahci_platform_enable_clks ) ;
/**
* ahci_platform_disable_clks - Disable platform clocks
* @ hpriv : host private area to store config values
*
2022-09-09 22:36:05 +03:00
* This function disables all the clocks enabled before
* ( bulk - clocks - disable function is supposed to do that in reverse
* from the enabling procedure order ) .
2014-03-25 22:51:39 +04:00
*/
void ahci_platform_disable_clks ( struct ahci_host_priv * hpriv )
{
2022-09-09 22:36:05 +03:00
clk_bulk_disable_unprepare ( hpriv - > n_clks , hpriv - > clks ) ;
2014-03-25 22:51:39 +04:00
}
EXPORT_SYMBOL_GPL ( ahci_platform_disable_clks ) ;
2022-09-09 22:36:08 +03:00
/**
* ahci_platform_deassert_rsts - Deassert / trigger platform resets
* @ hpriv : host private area to store config values
*
* This function deasserts or triggers all the reset lines found for
* the AHCI device .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_deassert_rsts ( struct ahci_host_priv * hpriv )
{
if ( hpriv - > f_rsts & AHCI_PLATFORM_RST_TRIGGER )
return reset_control_reset ( hpriv - > rsts ) ;
return reset_control_deassert ( hpriv - > rsts ) ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_deassert_rsts ) ;
/**
* ahci_platform_assert_rsts - Assert / rearm platform resets
* @ hpriv : host private area to store config values
*
* This function asserts or rearms ( for self - deasserting resets ) all
* the reset controls found for the AHCI device .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_assert_rsts ( struct ahci_host_priv * hpriv )
{
if ( hpriv - > f_rsts & AHCI_PLATFORM_RST_TRIGGER )
return reset_control_rearm ( hpriv - > rsts ) ;
return reset_control_assert ( hpriv - > rsts ) ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_assert_rsts ) ;
2015-01-15 17:09:37 +03:00
/**
* ahci_platform_enable_regulators - Enable regulators
* @ hpriv : host private area to store config values
*
2018-09-03 13:01:54 +03:00
* This function enables all the regulators found in controller and
2015-01-15 17:09:37 +03:00
* hpriv - > target_pwrs , if any . If a regulator fails to be enabled , it
* disables all the regulators already enabled in reverse order and
* returns an error .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_enable_regulators ( struct ahci_host_priv * hpriv )
{
int rc , i ;
2019-10-16 13:51:05 +03:00
rc = regulator_enable ( hpriv - > ahci_regulator ) ;
if ( rc )
return rc ;
2018-09-03 13:01:54 +03:00
2019-10-16 13:51:05 +03:00
rc = regulator_enable ( hpriv - > phy_regulator ) ;
if ( rc )
goto disable_ahci_pwrs ;
2018-09-03 13:01:56 +03:00
2015-01-15 17:09:37 +03:00
for ( i = 0 ; i < hpriv - > nports ; i + + ) {
if ( ! hpriv - > target_pwrs [ i ] )
continue ;
rc = regulator_enable ( hpriv - > target_pwrs [ i ] ) ;
if ( rc )
goto disable_target_pwrs ;
}
return 0 ;
disable_target_pwrs :
while ( - - i > = 0 )
if ( hpriv - > target_pwrs [ i ] )
regulator_disable ( hpriv - > target_pwrs [ i ] ) ;
2019-10-16 13:51:05 +03:00
regulator_disable ( hpriv - > phy_regulator ) ;
2018-09-03 13:01:56 +03:00
disable_ahci_pwrs :
2019-10-16 13:51:05 +03:00
regulator_disable ( hpriv - > ahci_regulator ) ;
2015-01-15 17:09:37 +03:00
return rc ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_enable_regulators ) ;
/**
* ahci_platform_disable_regulators - Disable regulators
* @ hpriv : host private area to store config values
*
2018-09-03 13:01:54 +03:00
* This function disables all regulators found in hpriv - > target_pwrs and
* AHCI controller .
2015-01-15 17:09:37 +03:00
*/
void ahci_platform_disable_regulators ( struct ahci_host_priv * hpriv )
{
int i ;
for ( i = 0 ; i < hpriv - > nports ; i + + ) {
if ( ! hpriv - > target_pwrs [ i ] )
continue ;
regulator_disable ( hpriv - > target_pwrs [ i ] ) ;
}
2018-09-03 13:01:54 +03:00
2019-10-16 13:51:05 +03:00
regulator_disable ( hpriv - > ahci_regulator ) ;
regulator_disable ( hpriv - > phy_regulator ) ;
2015-01-15 17:09:37 +03:00
}
EXPORT_SYMBOL_GPL ( ahci_platform_disable_regulators ) ;
2014-03-25 22:51:39 +04:00
/**
* ahci_platform_enable_resources - Enable platform resources
* @ hpriv : host private area to store config values
*
* This function enables all ahci_platform managed resources in the
* following order :
* 1 ) Regulator
* 2 ) Clocks ( through ahci_platform_enable_clks )
2018-08-22 15:13:02 +03:00
* 3 ) Resets
* 4 ) Phys
2014-03-25 22:51:39 +04:00
*
* If resource enabling fails at any point the previous enabled resources
* are disabled in reverse order .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_enable_resources ( struct ahci_host_priv * hpriv )
{
int rc ;
2015-01-15 17:09:37 +03:00
rc = ahci_platform_enable_regulators ( hpriv ) ;
if ( rc )
return rc ;
2014-03-25 22:51:39 +04:00
rc = ahci_platform_enable_clks ( hpriv ) ;
if ( rc )
goto disable_regulator ;
2022-09-09 22:36:08 +03:00
rc = ahci_platform_deassert_rsts ( hpriv ) ;
2018-03-23 04:30:53 +03:00
if ( rc )
2018-04-09 04:32:34 +03:00
goto disable_clks ;
2018-03-23 04:30:53 +03:00
2018-08-22 15:13:02 +03:00
rc = ahci_platform_enable_phys ( hpriv ) ;
if ( rc )
2022-09-09 22:36:08 +03:00
goto disable_rsts ;
2018-08-22 15:13:02 +03:00
2014-03-25 22:51:39 +04:00
return 0 ;
2022-09-09 22:36:08 +03:00
disable_rsts :
ahci_platform_assert_rsts ( hpriv ) ;
2018-08-22 15:13:02 +03:00
2014-03-25 22:51:39 +04:00
disable_clks :
ahci_platform_disable_clks ( hpriv ) ;
disable_regulator :
2015-01-15 17:09:37 +03:00
ahci_platform_disable_regulators ( hpriv ) ;
2014-03-25 22:51:39 +04:00
return rc ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_enable_resources ) ;
/**
* ahci_platform_disable_resources - Disable platform resources
* @ hpriv : host private area to store config values
*
* This function disables all ahci_platform managed resources in the
* following order :
2014-07-30 22:13:57 +04:00
* 1 ) Phys
2018-08-22 15:13:02 +03:00
* 2 ) Resets
* 3 ) Clocks ( through ahci_platform_disable_clks )
* 4 ) Regulator
2014-03-25 22:51:39 +04:00
*/
void ahci_platform_disable_resources ( struct ahci_host_priv * hpriv )
{
2014-07-30 22:13:57 +04:00
ahci_platform_disable_phys ( hpriv ) ;
2014-03-25 22:51:39 +04:00
2022-09-09 22:36:08 +03:00
ahci_platform_assert_rsts ( hpriv ) ;
2018-08-22 15:13:02 +03:00
2014-03-25 22:51:39 +04:00
ahci_platform_disable_clks ( hpriv ) ;
2015-01-15 17:09:37 +03:00
ahci_platform_disable_regulators ( hpriv ) ;
2014-03-25 22:51:39 +04:00
}
EXPORT_SYMBOL_GPL ( ahci_platform_disable_resources ) ;
static void ahci_platform_put_resources ( struct device * dev , void * res )
{
struct ahci_host_priv * hpriv = res ;
int c ;
if ( hpriv - > got_runtime_pm ) {
2018-08-06 20:22:35 +03:00
pm_runtime_put_sync ( dev ) ;
2014-03-25 22:51:39 +04:00
pm_runtime_disable ( dev ) ;
}
2015-01-15 17:09:37 +03:00
/*
* The regulators are tied to child node device and not to the
* SATA device itself . So we can ' t use devm for automatically
* releasing them . We have to do it manually here .
*/
for ( c = 0 ; c < hpriv - > nports ; c + + )
if ( hpriv - > target_pwrs & & hpriv - > target_pwrs [ c ] )
regulator_put ( hpriv - > target_pwrs [ c ] ) ;
2018-07-17 13:49:35 +03:00
kfree ( hpriv - > target_pwrs ) ;
2015-01-15 17:09:37 +03:00
}
static int ahci_platform_get_phy ( struct ahci_host_priv * hpriv , u32 port ,
struct device * dev , struct device_node * node )
{
int rc ;
hpriv - > phys [ port ] = devm_of_phy_get ( dev , node , NULL ) ;
if ( ! IS_ERR ( hpriv - > phys [ port ] ) )
return 0 ;
rc = PTR_ERR ( hpriv - > phys [ port ] ) ;
switch ( rc ) {
case - ENOSYS :
/* No PHY support. Check if PHY is required. */
2023-03-10 17:47:00 +03:00
if ( of_property_present ( node , " phys " ) ) {
2015-01-15 17:09:37 +03:00
dev_err ( dev ,
2018-08-28 04:52:10 +03:00
" couldn't get PHY in node %pOFn: ENOSYS \n " ,
node ) ;
2015-01-15 17:09:37 +03:00
break ;
}
2020-08-24 01:36:59 +03:00
fallthrough ;
2015-01-15 17:09:37 +03:00
case - ENODEV :
/* continue normally */
hpriv - > phys [ port ] = NULL ;
rc = 0 ;
break ;
2019-07-31 15:26:51 +03:00
case - EPROBE_DEFER :
/* Do not complain yet */
break ;
2015-01-15 17:09:37 +03:00
default :
dev_err ( dev ,
2018-08-28 04:52:10 +03:00
" couldn't get PHY in node %pOFn: %d \n " ,
node , rc ) ;
2015-01-15 17:09:37 +03:00
break ;
}
return rc ;
}
static int ahci_platform_get_regulator ( struct ahci_host_priv * hpriv , u32 port ,
struct device * dev )
{
struct regulator * target_pwr ;
int rc = 0 ;
2019-10-16 13:51:05 +03:00
target_pwr = regulator_get ( dev , " target " ) ;
2015-01-15 17:09:37 +03:00
if ( ! IS_ERR ( target_pwr ) )
hpriv - > target_pwrs [ port ] = target_pwr ;
else
rc = PTR_ERR ( target_pwr ) ;
return rc ;
2014-03-25 22:51:39 +04:00
}
2022-09-09 22:36:14 +03:00
static int ahci_platform_get_firmware ( struct ahci_host_priv * hpriv ,
struct device * dev )
{
struct device_node * child ;
u32 port ;
if ( ! of_property_read_u32 ( dev - > of_node , " hba-cap " , & hpriv - > saved_cap ) )
hpriv - > saved_cap & = ( HOST_CAP_SSS | HOST_CAP_MPS ) ;
of_property_read_u32 ( dev - > of_node ,
" ports-implemented " , & hpriv - > saved_port_map ) ;
for_each_child_of_node ( dev - > of_node , child ) {
if ( ! of_device_is_available ( child ) )
continue ;
if ( of_property_read_u32 ( child , " reg " , & port ) ) {
of_node_put ( child ) ;
return - EINVAL ;
}
if ( ! of_property_read_u32 ( child , " hba-port-cap " , & hpriv - > saved_port_cap [ port ] ) )
hpriv - > saved_port_cap [ port ] & = PORT_CMD_CAP ;
}
return 0 ;
}
2014-03-25 22:51:39 +04:00
/**
* ahci_platform_get_resources - Get platform resources
* @ pdev : platform device to get resources for
2018-08-22 15:13:01 +03:00
* @ flags : bitmap representing the resource to get
2014-03-25 22:51:39 +04:00
*
* This function allocates an ahci_host_priv struct , and gets the following
* resources , storing a reference to them inside the returned struct :
*
* 1 ) mmio registers ( IORESOURCE_MEM 0 , mandatory )
* 2 ) regulator for controlling the targets power ( optional )
2018-09-03 13:01:54 +03:00
* regulator for controlling the AHCI controller ( optional )
2022-09-09 22:36:05 +03:00
* 3 ) all clocks specified in the devicetree node , or a single
* clock for non - OF platforms ( optional )
2018-08-22 15:13:02 +03:00
* 4 ) resets , if flags has AHCI_PLATFORM_GET_RESETS ( optional )
* 5 ) phys ( optional )
2014-03-25 22:51:39 +04:00
*
* RETURNS :
* The allocated ahci_host_priv on success , otherwise an ERR_PTR value
*/
2018-08-22 15:13:01 +03:00
struct ahci_host_priv * ahci_platform_get_resources ( struct platform_device * pdev ,
unsigned int flags )
2014-03-25 22:51:39 +04:00
{
2022-09-09 22:36:05 +03:00
int child_nodes , rc = - ENOMEM , enabled_ports = 0 ;
2014-03-25 22:51:39 +04:00
struct device * dev = & pdev - > dev ;
struct ahci_host_priv * hpriv ;
2014-07-30 22:13:57 +04:00
struct device_node * child ;
u32 mask_port_map = 0 ;
2014-03-25 22:51:39 +04:00
if ( ! devres_open_group ( dev , NULL , GFP_KERNEL ) )
return ERR_PTR ( - ENOMEM ) ;
hpriv = devres_alloc ( ahci_platform_put_resources , sizeof ( * hpriv ) ,
GFP_KERNEL ) ;
if ( ! hpriv )
goto err_out ;
devres_add ( dev , hpriv ) ;
2022-09-09 22:36:04 +03:00
/*
* If the DT provided an " ahci " named resource , use it . Otherwise ,
* fallback to using the default first resource for the device node .
*/
if ( platform_get_resource_byname ( pdev , IORESOURCE_MEM , " ahci " ) )
hpriv - > mmio = devm_platform_ioremap_resource_byname ( pdev , " ahci " ) ;
else
hpriv - > mmio = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-03-25 22:51:39 +04:00
if ( IS_ERR ( hpriv - > mmio ) ) {
rc = PTR_ERR ( hpriv - > mmio ) ;
goto err_out ;
}
2022-09-09 22:36:05 +03:00
/*
* Bulk clocks getting procedure can fail to find any clock due to
* running on a non - OF platform or due to the clocks being defined in
* bypass of the DT firmware ( like da850 , spear13xx ) . In that case we
* fallback to getting a single clock source right from the dev clocks
* list .
*/
rc = devm_clk_bulk_get_all ( dev , & hpriv - > clks ) ;
if ( rc < 0 )
goto err_out ;
if ( rc > 0 ) {
/* Got clocks in bulk */
hpriv - > n_clks = rc ;
} else {
2014-03-25 22:51:39 +04:00
/*
2022-09-09 22:36:05 +03:00
* No clock bulk found : fallback to manually getting
* the optional clock .
2014-03-25 22:51:39 +04:00
*/
2022-09-09 22:36:05 +03:00
hpriv - > clks = devm_kzalloc ( dev , sizeof ( * hpriv - > clks ) , GFP_KERNEL ) ;
if ( ! hpriv - > clks ) {
rc = - ENOMEM ;
goto err_out ;
}
hpriv - > clks - > clk = devm_clk_get_optional ( dev , NULL ) ;
if ( IS_ERR ( hpriv - > clks - > clk ) ) {
rc = PTR_ERR ( hpriv - > clks - > clk ) ;
goto err_out ;
} else if ( hpriv - > clks - > clk ) {
hpriv - > clks - > id = " ahci " ;
hpriv - > n_clks = 1 ;
2014-03-25 22:51:39 +04:00
}
}
2019-10-16 13:51:05 +03:00
hpriv - > ahci_regulator = devm_regulator_get ( dev , " ahci " ) ;
2018-09-03 13:01:54 +03:00
if ( IS_ERR ( hpriv - > ahci_regulator ) ) {
rc = PTR_ERR ( hpriv - > ahci_regulator ) ;
2019-10-16 13:51:05 +03:00
if ( rc ! = 0 )
2018-09-03 13:01:54 +03:00
goto err_out ;
}
2019-10-16 13:51:05 +03:00
hpriv - > phy_regulator = devm_regulator_get ( dev , " phy " ) ;
2018-09-03 13:01:56 +03:00
if ( IS_ERR ( hpriv - > phy_regulator ) ) {
rc = PTR_ERR ( hpriv - > phy_regulator ) ;
2021-10-13 09:16:31 +03:00
goto err_out ;
2018-09-03 13:01:56 +03:00
}
2018-08-22 15:13:02 +03:00
if ( flags & AHCI_PLATFORM_GET_RESETS ) {
hpriv - > rsts = devm_reset_control_array_get_optional_shared ( dev ) ;
if ( IS_ERR ( hpriv - > rsts ) ) {
rc = PTR_ERR ( hpriv - > rsts ) ;
goto err_out ;
}
2022-09-09 22:36:08 +03:00
hpriv - > f_rsts = flags & AHCI_PLATFORM_RST_TRIGGER ;
2018-08-22 15:13:02 +03:00
}
2022-09-09 22:36:06 +03:00
/*
* Too many sub - nodes most likely means having something wrong with
* the firmware .
*/
child_nodes = of_get_child_count ( dev - > of_node ) ;
if ( child_nodes > AHCI_MAX_PORTS ) {
rc = - EINVAL ;
goto err_out ;
}
2014-07-30 22:13:57 +04:00
2015-01-15 17:09:37 +03:00
/*
* If no sub - node was found , we still need to set nports to
* one in order to be able to use the
* ahci_platform_ [ en | dis ] able_ [ phys | regulators ] functions .
*/
2022-09-09 22:36:06 +03:00
if ( child_nodes )
hpriv - > nports = child_nodes ;
else
2015-01-15 17:09:37 +03:00
hpriv - > nports = 1 ;
2018-07-12 14:41:30 +03:00
hpriv - > phys = devm_kcalloc ( dev , hpriv - > nports , sizeof ( * hpriv - > phys ) , GFP_KERNEL ) ;
2015-01-15 17:09:37 +03:00
if ( ! hpriv - > phys ) {
rc = - ENOMEM ;
goto err_out ;
}
2018-07-17 13:49:35 +03:00
/*
* We cannot use devm_ here , since ahci_platform_put_resources ( ) uses
* target_pwrs after devm_ have freed memory
*/
hpriv - > target_pwrs = kcalloc ( hpriv - > nports , sizeof ( * hpriv - > target_pwrs ) , GFP_KERNEL ) ;
2015-01-15 17:09:37 +03:00
if ( ! hpriv - > target_pwrs ) {
rc = - ENOMEM ;
goto err_out ;
}
2014-07-30 22:13:57 +04:00
2015-01-15 17:09:37 +03:00
if ( child_nodes ) {
2014-07-30 22:13:57 +04:00
for_each_child_of_node ( dev - > of_node , child ) {
u32 port ;
2015-01-31 21:36:22 +03:00
struct platform_device * port_dev __maybe_unused ;
2014-07-30 22:13:57 +04:00
if ( ! of_device_is_available ( child ) )
continue ;
if ( of_property_read_u32 ( child , " reg " , & port ) ) {
rc = - EINVAL ;
2019-08-15 09:00:14 +03:00
of_node_put ( child ) ;
2014-06-17 16:07:55 +04:00
goto err_out ;
}
2014-03-25 22:51:39 +04:00
2014-07-30 22:13:57 +04:00
if ( port > = hpriv - > nports ) {
dev_warn ( dev , " invalid port number %d \n " , port ) ;
continue ;
}
mask_port_map | = BIT ( port ) ;
2015-01-31 21:36:22 +03:00
# ifdef CONFIG_OF_ADDRESS
2015-01-15 17:09:37 +03:00
of_platform_device_create ( child , NULL , NULL ) ;
port_dev = of_find_device_by_node ( child ) ;
if ( port_dev ) {
rc = ahci_platform_get_regulator ( hpriv , port ,
& port_dev - > dev ) ;
2019-08-15 09:00:14 +03:00
if ( rc = = - EPROBE_DEFER ) {
of_node_put ( child ) ;
2015-01-15 17:09:37 +03:00
goto err_out ;
2019-08-15 09:00:14 +03:00
}
2014-07-30 22:13:57 +04:00
}
2015-01-31 21:36:22 +03:00
# endif
2014-03-25 22:51:39 +04:00
2015-01-15 17:09:37 +03:00
rc = ahci_platform_get_phy ( hpriv , port , dev , child ) ;
2019-08-15 09:00:14 +03:00
if ( rc ) {
of_node_put ( child ) ;
2015-01-15 17:09:37 +03:00
goto err_out ;
2019-08-15 09:00:14 +03:00
}
2015-01-15 17:09:37 +03:00
2014-07-30 22:13:57 +04:00
enabled_ports + + ;
}
if ( ! enabled_ports ) {
dev_warn ( dev , " No port enabled \n " ) ;
rc = - ENODEV ;
2014-03-25 22:51:39 +04:00
goto err_out ;
}
2014-07-30 22:13:57 +04:00
if ( ! hpriv - > mask_port_map )
hpriv - > mask_port_map = mask_port_map ;
} else {
/*
* If no sub - node was found , keep this for device tree
* compatibility
*/
2015-01-15 17:09:37 +03:00
rc = ahci_platform_get_phy ( hpriv , 0 , dev , dev - > of_node ) ;
if ( rc )
goto err_out ;
2014-07-30 22:13:57 +04:00
2015-01-15 17:09:37 +03:00
rc = ahci_platform_get_regulator ( hpriv , 0 , dev ) ;
if ( rc = = - EPROBE_DEFER )
goto err_out ;
2014-03-25 22:51:39 +04:00
}
2022-09-09 22:36:14 +03:00
/*
* Retrieve firmware - specific flags which then will be used to set
* the HW - init fields of HBA and its ports
*/
rc = ahci_platform_get_firmware ( hpriv , dev ) ;
if ( rc )
goto err_out ;
2014-03-25 22:51:39 +04:00
pm_runtime_enable ( dev ) ;
2018-08-06 20:22:35 +03:00
pm_runtime_get_sync ( dev ) ;
2014-03-25 22:51:39 +04:00
hpriv - > got_runtime_pm = true ;
devres_remove_group ( dev , NULL ) ;
return hpriv ;
err_out :
devres_release_group ( dev , NULL ) ;
return ERR_PTR ( rc ) ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_get_resources ) ;
/**
* ahci_platform_init_host - Bring up an ahci - platform host
* @ pdev : platform device pointer for the host
* @ hpriv : ahci - host private data for the host
* @ pi_template : template for the ata_port_info to use
2015-01-29 02:30:29 +03:00
* @ sht : scsi_host_template to use when registering
2014-03-25 22:51:39 +04:00
*
* This function does all the usual steps needed to bring up an
2014-07-30 22:13:57 +04:00
* ahci - platform host , note any necessary resources ( ie clks , phys , etc . )
2014-03-25 22:51:39 +04:00
* must be initialized / enabled before calling this .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_init_host ( struct platform_device * pdev ,
struct ahci_host_priv * hpriv ,
2015-01-29 02:30:29 +03:00
const struct ata_port_info * pi_template ,
2023-03-22 22:53:59 +03:00
const struct scsi_host_template * sht )
2014-03-25 22:51:39 +04:00
{
struct device * dev = & pdev - > dev ;
struct ata_port_info pi = * pi_template ;
const struct ata_port_info * ppi [ ] = { & pi , NULL } ;
struct ata_host * host ;
int i , irq , n_ports , rc ;
irq = platform_get_irq ( pdev , 0 ) ;
2021-12-17 14:28:32 +03:00
if ( irq < 0 )
2017-05-16 15:06:12 +03:00
return irq ;
2021-03-15 23:15:06 +03:00
if ( ! irq )
return - EINVAL ;
2014-03-25 22:51:39 +04:00
2015-05-31 14:55:18 +03:00
hpriv - > irq = irq ;
2014-03-25 22:51:39 +04:00
/* prepare host */
2014-08-01 18:30:37 +04:00
pi . private_data = ( void * ) ( unsigned long ) hpriv - > flags ;
2014-03-25 22:51:39 +04:00
2014-07-30 22:13:56 +04:00
ahci_save_initial_config ( dev , hpriv ) ;
2014-03-25 22:51:39 +04:00
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 )
return - ENOMEM ;
host - > private_data = hpriv ;
if ( ! ( hpriv - > cap & HOST_CAP_SSS ) | | ahci_ignore_sss )
host - > flags | = ATA_HOST_PARALLEL_SCAN ;
else
dev_info ( dev , " 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 " ,
platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ) ;
ata_port_desc ( ap , " port 0x%x " , 0x100 + ap - > port_no * 0x80 ) ;
/* set enclosure management message type */
if ( ap - > flags & ATA_FLAG_EM )
ap - > em_message_type = hpriv - > em_msg_type ;
/* disabled/not-implemented port */
if ( ! ( hpriv - > port_map & ( 1 < < i ) ) )
ap - > ops = & ata_dummy_port_ops ;
}
2014-06-12 21:40:23 +04:00
if ( hpriv - > cap & HOST_CAP_64 ) {
rc = dma_coerce_mask_and_coherent ( dev , DMA_BIT_MASK ( 64 ) ) ;
if ( rc ) {
2021-12-09 17:59:37 +03:00
dev_err ( dev , " Failed to enable 64-bit DMA. \n " ) ;
return rc ;
2014-06-12 21:40:23 +04:00
}
}
2014-03-25 22:51:39 +04:00
rc = ahci_reset_controller ( host ) ;
if ( rc )
return rc ;
ahci_init_controller ( host ) ;
ahci_print_info ( host , " platform " ) ;
2015-05-31 14:55:18 +03:00
return ahci_host_activate ( host , sht ) ;
2014-03-25 22:51:39 +04:00
}
EXPORT_SYMBOL_GPL ( ahci_platform_init_host ) ;
static void ahci_host_stop ( struct ata_host * host )
{
struct ahci_host_priv * hpriv = host - > private_data ;
ahci_platform_disable_resources ( hpriv ) ;
}
2017-07-20 22:26:24 +03:00
/**
* ahci_platform_shutdown - Disable interrupts and stop DMA for host ports
2018-07-12 14:41:29 +03:00
* @ pdev : platform device pointer for the host
2017-07-20 22:26:24 +03:00
*
* This function is called during system shutdown and performs the minimal
* deconfiguration required to ensure that an ahci_platform host cannot
* corrupt or otherwise interfere with a new kernel being started with kexec .
*/
void ahci_platform_shutdown ( struct platform_device * pdev )
{
struct ata_host * host = platform_get_drvdata ( pdev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
int i ;
for ( i = 0 ; i < host - > n_ports ; i + + ) {
struct ata_port * ap = host - > ports [ i ] ;
/* Disable port interrupts */
if ( ap - > ops - > freeze )
ap - > ops - > freeze ( ap ) ;
/* Stop the port DMA engines */
if ( ap - > ops - > port_stop )
ap - > ops - > port_stop ( ap ) ;
}
/* Disable and clear host interrupts */
writel ( readl ( mmio + HOST_CTL ) & ~ HOST_IRQ_EN , mmio + HOST_CTL ) ;
readl ( mmio + HOST_CTL ) ; /* flush */
writel ( GENMASK ( host - > n_ports , 0 ) , mmio + HOST_IRQ_STAT ) ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_shutdown ) ;
2014-03-25 22:51:39 +04:00
# ifdef CONFIG_PM_SLEEP
/**
* ahci_platform_suspend_host - Suspend an ahci - platform host
* @ dev : device pointer for the host
*
* This function does all the usual steps needed to suspend an
2014-07-30 22:13:57 +04:00
* ahci - platform host , note any necessary resources ( ie clks , phys , etc . )
2014-03-25 22:51:39 +04:00
* must be disabled after calling this .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_suspend_host ( struct device * dev )
{
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
u32 ctl ;
if ( hpriv - > flags & AHCI_HFLAG_NO_SUSPEND ) {
dev_err ( dev , " firmware update required for suspend/resume \n " ) ;
return - EIO ;
}
/*
* AHCI spec rev1 .1 section 8.3 .3 :
* Software must disable interrupts prior to requesting a
* transition of the HBA to D3 state .
*/
ctl = readl ( mmio + HOST_CTL ) ;
ctl & = ~ HOST_IRQ_EN ;
writel ( ctl , mmio + HOST_CTL ) ;
readl ( mmio + HOST_CTL ) ; /* flush */
2018-12-04 22:28:25 +03:00
if ( hpriv - > flags & AHCI_HFLAG_SUSPEND_PHYS )
ahci_platform_disable_phys ( hpriv ) ;
2022-02-02 23:58:21 +03:00
ata_host_suspend ( host , PMSG_SUSPEND ) ;
return 0 ;
2014-03-25 22:51:39 +04:00
}
EXPORT_SYMBOL_GPL ( ahci_platform_suspend_host ) ;
/**
* ahci_platform_resume_host - Resume an ahci - platform host
* @ dev : device pointer for the host
*
* This function does all the usual steps needed to resume an ahci - platform
2014-07-30 22:13:57 +04:00
* host , note any necessary resources ( ie clks , phys , etc . ) must be
2014-03-25 22:51:39 +04:00
* initialized / enabled before calling this .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_resume_host ( struct device * dev )
{
struct ata_host * host = dev_get_drvdata ( dev ) ;
2018-12-04 22:28:25 +03:00
struct ahci_host_priv * hpriv = host - > private_data ;
2014-03-25 22:51:39 +04:00
int rc ;
if ( dev - > power . power_state . event = = PM_EVENT_SUSPEND ) {
rc = ahci_reset_controller ( host ) ;
if ( rc )
return rc ;
ahci_init_controller ( host ) ;
}
2018-12-04 22:28:25 +03:00
if ( hpriv - > flags & AHCI_HFLAG_SUSPEND_PHYS )
ahci_platform_enable_phys ( hpriv ) ;
2014-03-25 22:51:39 +04:00
ata_host_resume ( host ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_resume_host ) ;
/**
* ahci_platform_suspend - Suspend an ahci - platform device
* @ dev : the platform device to suspend
*
* This function suspends the host associated with the device , followed by
* disabling all the resources of the device .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_suspend ( struct device * dev )
{
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
int rc ;
2018-08-06 20:22:35 +03:00
rc = ahci_platform_suspend_host ( dev ) ;
2014-03-25 22:51:39 +04:00
if ( rc )
return rc ;
2018-08-06 20:22:35 +03:00
ahci_platform_disable_resources ( hpriv ) ;
2014-03-25 22:51:39 +04:00
return 0 ;
}
2018-08-06 20:22:35 +03:00
EXPORT_SYMBOL_GPL ( ahci_platform_suspend ) ;
2014-03-25 22:51:39 +04:00
/**
* ahci_platform_resume - Resume an ahci - platform device
* @ dev : the platform device to resume
*
* This function enables all the resources of the device followed by
* resuming the host associated with the device .
*
* RETURNS :
* 0 on success otherwise a negative error code
*/
int ahci_platform_resume ( struct device * dev )
{
2018-08-06 20:22:35 +03:00
struct ata_host * host = dev_get_drvdata ( dev ) ;
struct ahci_host_priv * hpriv = host - > private_data ;
2014-03-25 22:51:39 +04:00
int rc ;
2018-08-06 20:22:35 +03:00
rc = ahci_platform_enable_resources ( hpriv ) ;
2014-03-25 22:51:39 +04:00
if ( rc )
return rc ;
2018-08-06 20:22:35 +03:00
rc = ahci_platform_resume_host ( dev ) ;
if ( rc )
goto disable_resources ;
2014-03-25 22:51:39 +04:00
/* We resumed so update PM runtime state */
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
return 0 ;
2018-05-29 13:06:12 +03:00
2018-08-06 20:22:35 +03:00
disable_resources :
ahci_platform_disable_resources ( hpriv ) ;
2018-05-29 13:06:12 +03:00
2018-08-06 20:22:35 +03:00
return rc ;
}
EXPORT_SYMBOL_GPL ( ahci_platform_resume ) ;
2014-03-25 22:51:39 +04:00
# endif
MODULE_DESCRIPTION ( " AHCI SATA platform library " ) ;
MODULE_AUTHOR ( " Anton Vorontsov <avorontsov@ru.mvista.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;