diff mbox

[v2,2/3] ata: ahci_tegra: Add support to disable features

Message ID 1479973418-21351-3-git-send-email-pchandru@nvidia.com
State Not Applicable
Delegated to: David Miller
Headers show

Commit Message

Preetham Chandru Ramchandra Nov. 24, 2016, 7:43 a.m. UTC
From: Preetham Chandru R <pchandru@nvidia.com>

Add support to disable DIPM, HIPM, DevSlp, partial, slumber and NCQ
features from DT. By default these features are enabled.

Signed-off-by: Preetham Chandru R <pchandru@nvidia.com>
---
v2:
* This change was created by seperating
   "ata: ahci_tegra: add support for tegra210" from v1

 drivers/ata/ahci_tegra.c | 107 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 82 insertions(+), 25 deletions(-)

Comments

Mikko Perttunen Nov. 28, 2016, 12:38 p.m. UTC | #1
On 24.11.2016 09:43, PREETHAM RAMACHANDRA wrote:
> From: Preetham Chandru R <pchandru@nvidia.com>
>
> Add support to disable DIPM, HIPM, DevSlp, partial, slumber and NCQ
> features from DT. By default these features are enabled.
>

Why are these features disabled? Are they broken on all Tegra210 chips, 
broken on specific boards or do they require some support from the 
driver that is not there?

Most likely we should hardcode the disabled features into the driver 
instead of reading them from the device tree.

> Signed-off-by: Preetham Chandru R <pchandru@nvidia.com>
> ---
> v2:
> * This change was created by seperating
>    "ata: ahci_tegra: add support for tegra210" from v1
>
>  drivers/ata/ahci_tegra.c | 107 ++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 82 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c
> index d12e2a9..443c3e8 100644
> --- a/drivers/ata/ahci_tegra.c
> +++ b/drivers/ata/ahci_tegra.c
> @@ -329,7 +329,7 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)
>  	return ret;
>  }
>
> -static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)
> +static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv)
>  {
>  	struct tegra_ahci_priv *tegra = hpriv->plat_data;
>
> @@ -345,6 +345,85 @@ static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)
>  	regulator_bulk_disable(tegra->soc_data->num_supplies, tegra->supplies);
>  }
>
> +static void tegra_ahci_host_stop(struct ata_host *host)
> +{
> +	struct ahci_host_priv *hpriv = host->private_data;
> +
> +	tegra_ahci_controller_deinit(hpriv);
> +}
> +
> +static struct ata_port_operations ahci_tegra_port_ops = {
> +	.inherits = &ahci_ops,
> +	.host_stop = tegra_ahci_host_stop,
> +};
> +
> +static struct ata_port_info ahci_tegra_port_info = {
> +	.flags = AHCI_FLAG_COMMON,
> +	.pio_mask = ATA_PIO4,
> +	.udma_mask = ATA_UDMA6,
> +	.port_ops = &ahci_tegra_port_ops,
> +};
> +
> +static void tegra_ahci_disable_devslp(struct tegra_ahci_priv *tegra)
> +{
> +	tegra_ahci_aux_update(tegra, 0, SDS_SUPPORT, SATA_AUX_MISC_CNTL_1_0);
> +}
> +
> +static void tegra_ahci_disable_hipm(struct tegra_ahci_priv *tegra)
> +{
> +	tegra_ahci_scfg_update(tegra, 0, T_SATA0_AHCI_HBA_CAP_BKDR_SALP,
> +			       T_SATA0_AHCI_HBA_CAP_BKDR);
> +}
> +
> +static void tegra_ahci_disable_partial(struct tegra_ahci_priv *tegra)
> +{
> +	tegra_ahci_scfg_update(tegra, 0,
> +			       T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP,
> +			       T_SATA0_AHCI_HBA_CAP_BKDR);
> +}
> +
> +static void tegra_ahci_disable_slumber(struct tegra_ahci_priv *tegra)
> +{
> +	tegra_ahci_scfg_update(tegra, 0,
> +			       T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP,
> +			       T_SATA0_AHCI_HBA_CAP_BKDR);
> +}
> +
> +static void tegra_ahci_disable_ncq(struct tegra_ahci_priv *tegra)
> +{
> +	tegra_ahci_scfg_update(tegra, 0, T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ,
> +			       T_SATA0_AHCI_HBA_CAP_BKDR);
> +}

These probably don't need their own functions.

> +
> +static void tegra_ahci_disable_features(struct ahci_host_priv *hpriv)
> +{
> +	struct tegra_ahci_priv *tegra = hpriv->plat_data;
> +	struct platform_device *pdev = tegra->pdev;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct property *prop;
> +	const char *feature;
> +
> +	if (of_property_count_strings(np, "nvidia,disable-features") <= 0)
> +		return;
> +
> +	of_property_for_each_string(np, "nvidia,disable-features", prop,
> +				    feature) {
> +		if (!strcmp(feature, "devslp"))
> +			tegra_ahci_disable_devslp(tegra);
> +		else if (!strcmp(feature, "hipm"))
> +			tegra_ahci_disable_hipm(tegra);
> +		else if (!strcmp(feature, "ncq"))
> +			tegra_ahci_disable_ncq(tegra);
> +		else if (!strcmp(feature, "dipm"))
> +			ahci_tegra_port_info.flags |= ATA_FLAG_NO_DIPM;
> +		else if (!strcmp(feature, "partial"))
> +			tegra_ahci_disable_partial(tegra);
> +		else if (!strcmp(feature, "slumber"))
> +			tegra_ahci_disable_slumber(tegra);
> +	}
> +}
> +
>  static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)
>  {
>  	struct tegra_ahci_priv *tegra = hpriv->plat_data;
> @@ -458,36 +537,14 @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)
>  	tegra_ahci_sata_update(tegra, 0, SATA_CONFIGURATION_CLK_OVERRIDE,
>  			       SATA_CONFIGURATION_0);
>
> +	tegra_ahci_disable_features(hpriv);
> +
>  	tegra_ahci_sata_update(tegra, IP_INT_MASK, IP_INT_MASK,
>  			       SATA_INTR_MASK_0);
>
>  	return 0;
>  }
>
> -static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv)
> -{
> -	tegra_ahci_power_off(hpriv);
> -}
> -
> -static void tegra_ahci_host_stop(struct ata_host *host)
> -{
> -	struct ahci_host_priv *hpriv = host->private_data;
> -
> -	tegra_ahci_controller_deinit(hpriv);
> -}
> -
> -static struct ata_port_operations ahci_tegra_port_ops = {
> -	.inherits = &ahci_ops,
> -	.host_stop = tegra_ahci_host_stop,
> -};
> -
> -static const struct ata_port_info ahci_tegra_port_info = {
> -	.flags = AHCI_FLAG_COMMON,
> -	.pio_mask = ATA_PIO4,
> -	.udma_mask = ATA_UDMA6,
> -	.port_ops = &ahci_tegra_port_ops,
> -};
> -
>  static const struct of_device_id tegra_ahci_of_match[] = {
>  	{
>  	 .compatible = "nvidia,tegra124-ahci",
>
>

Thanks,
Mikko.
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Preetham Chandru Ramchandra Dec. 21, 2016, 9:12 a.m. UTC | #2
>-----Original Message-----

>From: Mikko Perttunen

>Sent: Monday, November 28, 2016 6:09 PM

>To: Preetham Chandru; tj@kernel.org; swarren@wwwdotorg.org;

>thierry.reding@gmail.com; preetham260@gmail.com

>Cc: Laxman Dewangan; linux-ide@vger.kernel.org; Venu Byravarasu; Pavan

>Kunapuli; linux-tegra@vger.kernel.org

>Subject: Re: [v2,2/3] ata: ahci_tegra: Add support to disable features

>

>On 24.11.2016 09:43, PREETHAM RAMACHANDRA wrote:

>> From: Preetham Chandru R <pchandru@nvidia.com>

>>

>> Add support to disable DIPM, HIPM, DevSlp, partial, slumber and NCQ

>> features from DT. By default these features are enabled.

>>

>

>Why are these features disabled? Are they broken on all Tegra210 chips,

>broken on specific boards or do they require some support from the driver

>that is not there?

>

>Most likely we should hardcode the disabled features into the driver instead

>of reading them from the device tree.

>


Yes, currently DevSlp and DIPM features are broken for t210 and t124 but devslp is fixed for tegra186. 
 Also I thought it would be nice to provide similar options for other features as well.
Since we do not support hotplug this would help us in debugging if we face issues with certain drives during kernel boot.
We can disable features we think are causing issues and retry.

>> Signed-off-by: Preetham Chandru R <pchandru@nvidia.com>

>> ---

>> v2:

>> * This change was created by seperating

>>    "ata: ahci_tegra: add support for tegra210" from v1

>>

>>  drivers/ata/ahci_tegra.c | 107

>> ++++++++++++++++++++++++++++++++++++-----------

>>  1 file changed, 82 insertions(+), 25 deletions(-)

>>

>> diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index

>> d12e2a9..443c3e8 100644

>> --- a/drivers/ata/ahci_tegra.c

>> +++ b/drivers/ata/ahci_tegra.c

>> @@ -329,7 +329,7 @@ static int tegra_ahci_power_on(struct

>ahci_host_priv *hpriv)

>>  	return ret;

>>  }

>>

>> -static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)

>> +static void tegra_ahci_controller_deinit(struct ahci_host_priv

>> +*hpriv)

>>  {

>>  	struct tegra_ahci_priv *tegra = hpriv->plat_data;

>>

>> @@ -345,6 +345,85 @@ static void tegra_ahci_power_off(struct

>ahci_host_priv *hpriv)

>>  	regulator_bulk_disable(tegra->soc_data->num_supplies,

>> tegra->supplies);  }

>>

>> +static void tegra_ahci_host_stop(struct ata_host *host) {

>> +	struct ahci_host_priv *hpriv = host->private_data;

>> +

>> +	tegra_ahci_controller_deinit(hpriv);

>> +}

>> +

>> +static struct ata_port_operations ahci_tegra_port_ops = {

>> +	.inherits = &ahci_ops,

>> +	.host_stop = tegra_ahci_host_stop,

>> +};

>> +

>> +static struct ata_port_info ahci_tegra_port_info = {

>> +	.flags = AHCI_FLAG_COMMON,

>> +	.pio_mask = ATA_PIO4,

>> +	.udma_mask = ATA_UDMA6,

>> +	.port_ops = &ahci_tegra_port_ops,

>> +};

>> +

>> +static void tegra_ahci_disable_devslp(struct tegra_ahci_priv *tegra)

>> +{

>> +	tegra_ahci_aux_update(tegra, 0, SDS_SUPPORT,

>> +SATA_AUX_MISC_CNTL_1_0); }

>> +

>> +static void tegra_ahci_disable_hipm(struct tegra_ahci_priv *tegra) {

>> +	tegra_ahci_scfg_update(tegra, 0,

>T_SATA0_AHCI_HBA_CAP_BKDR_SALP,

>> +			       T_SATA0_AHCI_HBA_CAP_BKDR); }

>> +

>> +static void tegra_ahci_disable_partial(struct tegra_ahci_priv *tegra)

>> +{

>> +	tegra_ahci_scfg_update(tegra, 0,

>> +

>T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP,

>> +			       T_SATA0_AHCI_HBA_CAP_BKDR); }

>> +

>> +static void tegra_ahci_disable_slumber(struct tegra_ahci_priv *tegra)

>> +{

>> +	tegra_ahci_scfg_update(tegra, 0,

>> +

>T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP,

>> +			       T_SATA0_AHCI_HBA_CAP_BKDR); }

>> +

>> +static void tegra_ahci_disable_ncq(struct tegra_ahci_priv *tegra) {

>> +	tegra_ahci_scfg_update(tegra, 0,

>T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ,

>> +			       T_SATA0_AHCI_HBA_CAP_BKDR); }

>

>These probably don't need their own functions.

>

Ok

>> +

>> +static void tegra_ahci_disable_features(struct ahci_host_priv *hpriv)

>> +{

>> +	struct tegra_ahci_priv *tegra = hpriv->plat_data;

>> +	struct platform_device *pdev = tegra->pdev;

>> +	struct device *dev = &pdev->dev;

>> +	struct device_node *np = dev->of_node;

>> +	struct property *prop;

>> +	const char *feature;

>> +

>> +	if (of_property_count_strings(np, "nvidia,disable-features") <= 0)

>> +		return;

>> +

>> +	of_property_for_each_string(np, "nvidia,disable-features", prop,

>> +				    feature) {

>> +		if (!strcmp(feature, "devslp"))

>> +			tegra_ahci_disable_devslp(tegra);

>> +		else if (!strcmp(feature, "hipm"))

>> +			tegra_ahci_disable_hipm(tegra);

>> +		else if (!strcmp(feature, "ncq"))

>> +			tegra_ahci_disable_ncq(tegra);

>> +		else if (!strcmp(feature, "dipm"))

>> +			ahci_tegra_port_info.flags |= ATA_FLAG_NO_DIPM;

>> +		else if (!strcmp(feature, "partial"))

>> +			tegra_ahci_disable_partial(tegra);

>> +		else if (!strcmp(feature, "slumber"))

>> +			tegra_ahci_disable_slumber(tegra);

>> +	}

>> +}

>> +

>>  static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)

>> {

>>  	struct tegra_ahci_priv *tegra = hpriv->plat_data; @@ -458,36

>+537,14

>> @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)

>>  	tegra_ahci_sata_update(tegra, 0,

>SATA_CONFIGURATION_CLK_OVERRIDE,

>>  			       SATA_CONFIGURATION_0);

>>

>> +	tegra_ahci_disable_features(hpriv);

>> +

>>  	tegra_ahci_sata_update(tegra, IP_INT_MASK, IP_INT_MASK,

>>  			       SATA_INTR_MASK_0);

>>

>>  	return 0;

>>  }

>>

>> -static void tegra_ahci_controller_deinit(struct ahci_host_priv

>> *hpriv) -{

>> -	tegra_ahci_power_off(hpriv);

>> -}

>> -

>> -static void tegra_ahci_host_stop(struct ata_host *host) -{

>> -	struct ahci_host_priv *hpriv = host->private_data;

>> -

>> -	tegra_ahci_controller_deinit(hpriv);

>> -}

>> -

>> -static struct ata_port_operations ahci_tegra_port_ops = {

>> -	.inherits = &ahci_ops,

>> -	.host_stop = tegra_ahci_host_stop,

>> -};

>> -

>> -static const struct ata_port_info ahci_tegra_port_info = {

>> -	.flags = AHCI_FLAG_COMMON,

>> -	.pio_mask = ATA_PIO4,

>> -	.udma_mask = ATA_UDMA6,

>> -	.port_ops = &ahci_tegra_port_ops,

>> -};

>> -

>>  static const struct of_device_id tegra_ahci_of_match[] = {

>>  	{

>>  	 .compatible = "nvidia,tegra124-ahci",

>>

>>

>

>Thanks,

>Mikko.


Thanks,
Preetham.
Mikko Perttunen Dec. 21, 2016, 9:31 a.m. UTC | #3
On 21.12.2016 11:12, Preetham Chandru wrote:
>
>
>> -----Original Message-----
>> From: Mikko Perttunen
>> Sent: Monday, November 28, 2016 6:09 PM
>> To: Preetham Chandru; tj@kernel.org; swarren@wwwdotorg.org;
>> thierry.reding@gmail.com; preetham260@gmail.com
>> Cc: Laxman Dewangan; linux-ide@vger.kernel.org; Venu Byravarasu; Pavan
>> Kunapuli; linux-tegra@vger.kernel.org
>> Subject: Re: [v2,2/3] ata: ahci_tegra: Add support to disable features
>>
>> On 24.11.2016 09:43, PREETHAM RAMACHANDRA wrote:
>>> From: Preetham Chandru R <pchandru@nvidia.com>
>>>
>>> Add support to disable DIPM, HIPM, DevSlp, partial, slumber and NCQ
>>> features from DT. By default these features are enabled.
>>>
>>
>> Why are these features disabled? Are they broken on all Tegra210 chips,
>> broken on specific boards or do they require some support from the driver
>> that is not there?
>>
>> Most likely we should hardcode the disabled features into the driver instead
>> of reading them from the device tree.
>>
>
> Yes, currently DevSlp and DIPM features are broken for t210 and t124 but devslp is fixed for tegra186.
>  Also I thought it would be nice to provide similar options for other features as well.
> Since we do not support hotplug this would help us in debugging if we face issues with certain drives during kernel boot.
> We can disable features we think are causing issues and retry.

We should put this data into the soc_data struct in the driver, since it 
is easily determined from just the compatibility string; for example

struct tegra_ahci_soc {
	...
	uint32_t quirks;
};

then

.quirks = NO_DEVSLP | NO_DIPM

or the other way around and list the features that we want to enable. 
This is also quite easy to change to test.

The device tree should also only contain information that describes the 
hardware itself, and nothing related to any specific driver's operation; 
so if there's a possibility that some feature is not working due to a 
driver issue, then the device tree definitely should not contain any 
entry disabling that feature.

(The downstream kernel can of course carry such features as patches on 
top, if required for some reason - but naturally it increases 
maintenance effort)

Thanks,
Mikko.
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Preetham Chandru Ramchandra Dec. 21, 2016, 9:37 a.m. UTC | #4
>-----Original Message-----

>From: Mikko Perttunen [mailto:cyndis@kapsi.fi]

>Sent: Wednesday, December 21, 2016 3:01 PM

>To: Preetham Chandru; Mikko Perttunen; tj@kernel.org;

>swarren@wwwdotorg.org; thierry.reding@gmail.com;

>preetham260@gmail.com

>Cc: Laxman Dewangan; linux-ide@vger.kernel.org; Venu Byravarasu; Pavan

>Kunapuli; linux-tegra@vger.kernel.org

>Subject: Re: [v2,2/3] ata: ahci_tegra: Add support to disable features

>

>On 21.12.2016 11:12, Preetham Chandru wrote:

>>

>>

>>> -----Original Message-----

>>> From: Mikko Perttunen

>>> Sent: Monday, November 28, 2016 6:09 PM

>>> To: Preetham Chandru; tj@kernel.org; swarren@wwwdotorg.org;

>>> thierry.reding@gmail.com; preetham260@gmail.com

>>> Cc: Laxman Dewangan; linux-ide@vger.kernel.org; Venu Byravarasu;

>>> Pavan Kunapuli; linux-tegra@vger.kernel.org

>>> Subject: Re: [v2,2/3] ata: ahci_tegra: Add support to disable

>>> features

>>>

>>> On 24.11.2016 09:43, PREETHAM RAMACHANDRA wrote:

>>>> From: Preetham Chandru R <pchandru@nvidia.com>

>>>>

>>>> Add support to disable DIPM, HIPM, DevSlp, partial, slumber and NCQ

>>>> features from DT. By default these features are enabled.

>>>>

>>>

>>> Why are these features disabled? Are they broken on all Tegra210

>>> chips, broken on specific boards or do they require some support from

>>> the driver that is not there?

>>>

>>> Most likely we should hardcode the disabled features into the driver

>>> instead of reading them from the device tree.

>>>

>>

>> Yes, currently DevSlp and DIPM features are broken for t210 and t124 but

>devslp is fixed for tegra186.

>>  Also I thought it would be nice to provide similar options for other

>features as well.

>> Since we do not support hotplug this would help us in debugging if we face

>issues with certain drives during kernel boot.

>> We can disable features we think are causing issues and retry.

>

>We should put this data into the soc_data struct in the driver, since it is

>easily determined from just the compatibility string; for example

>

>struct tegra_ahci_soc {

>	...

>	uint32_t quirks;

>};

>

>then

>

>.quirks = NO_DEVSLP | NO_DIPM

>

>or the other way around and list the features that we want to enable.

>This is also quite easy to change to test.

>

>The device tree should also only contain information that describes the

>hardware itself, and nothing related to any specific driver's operation; so if

>there's a possibility that some feature is not working due to a driver issue,

>then the device tree definitely should not contain any entry disabling that

>feature.

>

>(The downstream kernel can of course carry such features as patches on top,

>if required for some reason - but naturally it increases maintenance effort)

>


Ok, sounds good to me. Will move it to soc_data structure.

>Thanks,

>Mikko.
diff mbox

Patch

diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c
index d12e2a9..443c3e8 100644
--- a/drivers/ata/ahci_tegra.c
+++ b/drivers/ata/ahci_tegra.c
@@ -329,7 +329,7 @@  static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)
 	return ret;
 }
 
-static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)
+static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv)
 {
 	struct tegra_ahci_priv *tegra = hpriv->plat_data;
 
@@ -345,6 +345,85 @@  static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)
 	regulator_bulk_disable(tegra->soc_data->num_supplies, tegra->supplies);
 }
 
+static void tegra_ahci_host_stop(struct ata_host *host)
+{
+	struct ahci_host_priv *hpriv = host->private_data;
+
+	tegra_ahci_controller_deinit(hpriv);
+}
+
+static struct ata_port_operations ahci_tegra_port_ops = {
+	.inherits = &ahci_ops,
+	.host_stop = tegra_ahci_host_stop,
+};
+
+static struct ata_port_info ahci_tegra_port_info = {
+	.flags = AHCI_FLAG_COMMON,
+	.pio_mask = ATA_PIO4,
+	.udma_mask = ATA_UDMA6,
+	.port_ops = &ahci_tegra_port_ops,
+};
+
+static void tegra_ahci_disable_devslp(struct tegra_ahci_priv *tegra)
+{
+	tegra_ahci_aux_update(tegra, 0, SDS_SUPPORT, SATA_AUX_MISC_CNTL_1_0);
+}
+
+static void tegra_ahci_disable_hipm(struct tegra_ahci_priv *tegra)
+{
+	tegra_ahci_scfg_update(tegra, 0, T_SATA0_AHCI_HBA_CAP_BKDR_SALP,
+			       T_SATA0_AHCI_HBA_CAP_BKDR);
+}
+
+static void tegra_ahci_disable_partial(struct tegra_ahci_priv *tegra)
+{
+	tegra_ahci_scfg_update(tegra, 0,
+			       T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP,
+			       T_SATA0_AHCI_HBA_CAP_BKDR);
+}
+
+static void tegra_ahci_disable_slumber(struct tegra_ahci_priv *tegra)
+{
+	tegra_ahci_scfg_update(tegra, 0,
+			       T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP,
+			       T_SATA0_AHCI_HBA_CAP_BKDR);
+}
+
+static void tegra_ahci_disable_ncq(struct tegra_ahci_priv *tegra)
+{
+	tegra_ahci_scfg_update(tegra, 0, T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ,
+			       T_SATA0_AHCI_HBA_CAP_BKDR);
+}
+
+static void tegra_ahci_disable_features(struct ahci_host_priv *hpriv)
+{
+	struct tegra_ahci_priv *tegra = hpriv->plat_data;
+	struct platform_device *pdev = tegra->pdev;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct property *prop;
+	const char *feature;
+
+	if (of_property_count_strings(np, "nvidia,disable-features") <= 0)
+		return;
+
+	of_property_for_each_string(np, "nvidia,disable-features", prop,
+				    feature) {
+		if (!strcmp(feature, "devslp"))
+			tegra_ahci_disable_devslp(tegra);
+		else if (!strcmp(feature, "hipm"))
+			tegra_ahci_disable_hipm(tegra);
+		else if (!strcmp(feature, "ncq"))
+			tegra_ahci_disable_ncq(tegra);
+		else if (!strcmp(feature, "dipm"))
+			ahci_tegra_port_info.flags |= ATA_FLAG_NO_DIPM;
+		else if (!strcmp(feature, "partial"))
+			tegra_ahci_disable_partial(tegra);
+		else if (!strcmp(feature, "slumber"))
+			tegra_ahci_disable_slumber(tegra);
+	}
+}
+
 static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)
 {
 	struct tegra_ahci_priv *tegra = hpriv->plat_data;
@@ -458,36 +537,14 @@  static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)
 	tegra_ahci_sata_update(tegra, 0, SATA_CONFIGURATION_CLK_OVERRIDE,
 			       SATA_CONFIGURATION_0);
 
+	tegra_ahci_disable_features(hpriv);
+
 	tegra_ahci_sata_update(tegra, IP_INT_MASK, IP_INT_MASK,
 			       SATA_INTR_MASK_0);
 
 	return 0;
 }
 
-static void tegra_ahci_controller_deinit(struct ahci_host_priv *hpriv)
-{
-	tegra_ahci_power_off(hpriv);
-}
-
-static void tegra_ahci_host_stop(struct ata_host *host)
-{
-	struct ahci_host_priv *hpriv = host->private_data;
-
-	tegra_ahci_controller_deinit(hpriv);
-}
-
-static struct ata_port_operations ahci_tegra_port_ops = {
-	.inherits = &ahci_ops,
-	.host_stop = tegra_ahci_host_stop,
-};
-
-static const struct ata_port_info ahci_tegra_port_info = {
-	.flags = AHCI_FLAG_COMMON,
-	.pio_mask = ATA_PIO4,
-	.udma_mask = ATA_UDMA6,
-	.port_ops = &ahci_tegra_port_ops,
-};
-
 static const struct of_device_id tegra_ahci_of_match[] = {
 	{
 	 .compatible = "nvidia,tegra124-ahci",