lkml.org 
[lkml]   [2014]   [Dec]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [PATCH 3/3] ata: libahci: Allow to use multiple regulators
Hi,

On 27-12-14 13:58, Hans de Goede wrote:
> Hi Gregory,
>
> Thanks for working on this. Overall the patch-set / concept looks good,
> you can add "Acked-by: Hans de Goede <hdegoede@redhat.com>" to the first
> 2 patches.
>
> I've some comments on this patch, see below.
>
> On 27-12-14 11:34, Gregory CLEMENT wrote:
>> The current implementation of the libahci allows using multiple PHYs
>> but not multiple regulators. This patch adds the support of multiple
>> regulators. Until now it was mandatory to have a PHY under a subnode,
>> now a port subnode can contain either a regulator or a PHY (or both).
>>
>> There was only one driver which used directly the regulator field of
>> the ahci_host_priv structure. To preserve the bisectability the change
>> in the ahci_imx driver was done in the same patch.
>>
>> This patch also depend of a patch of the regulator framework:
>> "regulator: core: Add the device tree version to the regulator_get
>> family"
>>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/ata/ahci.h | 2 +-
>> drivers/ata/ahci_imx.c | 14 +--
>> drivers/ata/libahci_platform.c | 206 ++++++++++++++++++++++++++++-------------
>> include/linux/ahci_platform.h | 2 +
>> 4 files changed, 151 insertions(+), 73 deletions(-)
>>
>> diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
>> index 40f0e34f17af..275358ae0b3f 100644
>> --- a/drivers/ata/ahci.h
>> +++ b/drivers/ata/ahci.h
>> @@ -333,7 +333,7 @@ struct ahci_host_priv {
>> u32 em_msg_type; /* EM message type */
>> bool got_runtime_pm; /* Did we do pm_runtime_get? */
>> struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
>> - struct regulator *target_pwr; /* Optional */
>> + struct regulator **target_pwrs; /* Optional */
>> /*
>> * If platform uses PHYs. There is a 1:1 relation between the port number and
>> * the PHY position in this array.
>> diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c
>> index 35d51c59a370..41632e57d46f 100644
>> --- a/drivers/ata/ahci_imx.c
>> +++ b/drivers/ata/ahci_imx.c
>> @@ -221,11 +221,9 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
>> if (imxpriv->no_device)
>> return 0;
>>
>> - if (hpriv->target_pwr) {
>> - ret = regulator_enable(hpriv->target_pwr);
>> - if (ret)
>> - return ret;
>> - }
>> + ret = ahci_platform_enable_regulators(hpriv);
>> + if (ret)
>> + return ret;
>>
>> ret = clk_prepare_enable(imxpriv->sata_ref_clk);
>> if (ret < 0)
>> @@ -270,8 +268,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
>> disable_clk:
>> clk_disable_unprepare(imxpriv->sata_ref_clk);
>> disable_regulator:
>> - if (hpriv->target_pwr)
>> - regulator_disable(hpriv->target_pwr);
>> + ahci_platform_disable_regulators(hpriv);
>>
>> return ret;
>> }
>> @@ -291,8 +288,7 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv)
>>
>> clk_disable_unprepare(imxpriv->sata_ref_clk);
>>
>> - if (hpriv->target_pwr)
>> - regulator_disable(hpriv->target_pwr);
>> + ahci_platform_disable_regulators(hpriv);
>> }
>>
>> static void ahci_imx_error_handler(struct ata_port *ap)
>> diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
>> index a147aaadca85..f3bf4311152d 100644
>> --- a/drivers/ata/libahci_platform.c
>> +++ b/drivers/ata/libahci_platform.c
>> @@ -138,6 +138,59 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
>> EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
>>
>> /**
>> + * ahci_platform_enable_regulators - Enable regulators
>> + * @hpriv: host private area to store config values
>> + *
>> + * This function enables all the regulators found in
>> + * 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;
>> +
>> + 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])
>
> I'm pretty sure you want:
>
> if (hpriv->target_pwrs[i])
>
> here, so without the '!' .
>
>> + regulator_disable(hpriv->target_pwrs[i]);
>> +
>> + return rc;
>> +}
>> +EXPORT_SYMBOL_GPL(ahci_platform_enable_regulators);
>> +
>> +/**
>> + * ahci_platform_disable_regulators - Disable regulators
>> + * @hpriv: host private area to store config values
>> + *
>> + * This function disables all regulators found in hpriv->target_pwrs.
>> + */
>> +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]);
>> + }
>> +}
>> +EXPORT_SYMBOL_GPL(ahci_platform_disable_regulators);
>> +/**
>> * ahci_platform_enable_resources - Enable platform resources
>> * @hpriv: host private area to store config values
>> *
>> @@ -157,11 +210,9 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
>> {
>> int rc;
>>
>> - if (hpriv->target_pwr) {
>> - rc = regulator_enable(hpriv->target_pwr);
>> - if (rc)
>> - return rc;
>> - }
>> + rc = ahci_platform_enable_regulators(hpriv);
>> + if (rc)
>> + return rc;
>>
>> rc = ahci_platform_enable_clks(hpriv);
>> if (rc)
>> @@ -177,8 +228,8 @@ disable_clks:
>> ahci_platform_disable_clks(hpriv);
>>
>> disable_regulator:
>> - if (hpriv->target_pwr)
>> - regulator_disable(hpriv->target_pwr);
>> + ahci_platform_disable_regulators(hpriv);
>> +
>> return rc;
>> }
>> EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
>> @@ -199,8 +250,7 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
>>
>> ahci_platform_disable_clks(hpriv);
>>
>> - if (hpriv->target_pwr)
>> - regulator_disable(hpriv->target_pwr);
>> + ahci_platform_disable_regulators(hpriv);
>> }
>> EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
>>
>> @@ -218,6 +268,59 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
>> clk_put(hpriv->clks[c]);
>> }
>>
>> +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. */
>> + if (of_find_property(node, "phys", NULL)) {
>> + dev_err(dev,
>> + "couldn't get PHY in node %s: ENOSYS\n",
>> + node->name);
>> + break;
>> + }
>> + case -ENODEV:
>> + /* continue normally */
>> + hpriv->phys[port] = NULL;
>> + rc = 0;
>> + break;
>> +
>> + default:
>> + dev_err(dev,
>> + "couldn't get PHY in node %s: %d\n",
>> + node->name, rc);
>> +
>> + break;
>> + }
>> +
>> + return rc;
>> +}
>> +
>> +static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port,
>> + struct device *dev, struct device_node *node)
>> +{
>> + struct regulator *target_pwr;
>> + int rc = 0;
>> +
>> + target_pwr = devm_of_regulator_get_optional(dev, "target", node);
>> +
>> + if (!IS_ERR(target_pwr))
>> + hpriv->target_pwrs[port] = target_pwr;
>> + else
>> + rc = PTR_ERR(target_pwr);
>> +
>> + return rc;
>> +}
>> +
>> /**
>> * ahci_platform_get_resources - Get platform resources
>> * @pdev: platform device to get resources for
>> @@ -240,7 +343,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
>> struct ahci_host_priv *hpriv;
>> struct clk *clk;
>> struct device_node *child;
>> - int i, enabled_ports = 0, rc = -ENOMEM;
>> + int i, sz, enabled_ports = 0, rc = -ENOMEM;
>> u32 mask_port_map = 0;
>>
>> if (!devres_open_group(dev, NULL, GFP_KERNEL))
>> @@ -261,14 +364,6 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
>> goto err_out;
>> }
>>
>> - hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
>> - if (IS_ERR(hpriv->target_pwr)) {
>> - rc = PTR_ERR(hpriv->target_pwr);
>> - if (rc == -EPROBE_DEFER)
>> - goto err_out;
>> - hpriv->target_pwr = NULL;
>> - }
>> -
>> for (i = 0; i < AHCI_MAX_CLKS; i++) {
>> /*
>> * For now we must use clk_get(dev, NULL) for the first clock,
>> @@ -292,15 +387,24 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
>>
>> hpriv->nports = of_get_child_count(dev->of_node);
>>
>> - if (hpriv->nports) {
>> - hpriv->phys = devm_kzalloc(dev,
>> - hpriv->nports * sizeof(*hpriv->phys),
>> - GFP_KERNEL);
>> - if (!hpriv->phys) {
>> - rc = -ENOMEM;
>> - goto err_out;
>> - }
>> + /* If no sub-node was found, keep this for device tree compatibility */
>> + if (!hpriv->nports)
>> + hpriv->nports = 1;
>
> So now nports is always >= 1.
>
>> +
>> + sz = hpriv->nports * sizeof(*hpriv->phys);
>> + hpriv->phys = devm_kzalloc(dev, sz, GFP_KERNEL);
>> + if (!hpriv->phys) {
>> + rc = -ENOMEM;
>> + goto err_out;
>> + }
>> + sz = hpriv->nports * sizeof(*hpriv->target_pwrs);
>> + hpriv->target_pwrs = devm_kzalloc(dev, sz, GFP_KERNEL);
>> + if (!hpriv->target_pwrs) {
>> + rc = -ENOMEM;
>> + goto err_out;
>> + }
>>
>> + if (hpriv->nports) {
>
> And this is always true,
>
>> for_each_child_of_node(dev->of_node, child) {
>> u32 port;
>>
>> @@ -319,14 +423,14 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
>>
>> mask_port_map |= BIT(port);
>>
>> - hpriv->phys[port] = devm_of_phy_get(dev, child, NULL);
>> - if (IS_ERR(hpriv->phys[port])) {
>> - rc = PTR_ERR(hpriv->phys[port]);
>> - dev_err(dev,
>> - "couldn't get PHY in node %s: %d\n",
>> - child->name, rc);
>> + rc = ahci_platform_get_regulator(hpriv, port,
>> + dev, child);
>> + if (rc == -EPROBE_DEFER)
>> + goto err_out;
>> +
>> + rc = ahci_platform_get_phy(hpriv, port, dev, child);
>> + if (rc)
>> goto err_out;
>> - }
>>
>> enabled_ports++;
>> }
>> @@ -343,38 +447,14 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
>> * If no sub-node was found, keep this for device tree
>> * compatibility
>> */
>
> And thus the code below (which is in the else) never gets executed, and you've
> broken the case where there are no subnodes.
>
> I think it is best to fix this by keeping nports == 0 when there are no subnodes
> (because we really do not know nports then, so advertising 1 is sorta wrong anyways),

Erm, scrap that we were already setting nports = 1 later on in the no child nodes
case to make ahci_platform_[en|dis]able_phys do the right thing.


> and use something like this to calculate the array sizes:
>
> sz = (nports ? nports : 1) * sizeof(*hpriv->phys);
> ...
> sz = (nports ? nports : 1) * sizeof(*hpriv->target_pwrs);

Still using the above and setting nports = 1 in the "else" case is
an option, but I think that adding a local child_nodes var and doing:

hpriv->nports = child_nodes = of_get_child_count(dev->of_node);

And changing the "if (hpriv->nports)" into "if (child_nodes)",
is a better solution.

Regards,

Hans


>
>> - struct phy *phy = devm_phy_get(dev, "sata-phy");
>> - if (!IS_ERR(phy)) {
>> - hpriv->phys = devm_kzalloc(dev, sizeof(*hpriv->phys),
>> - GFP_KERNEL);
>> - if (!hpriv->phys) {
>> - rc = -ENOMEM;
>> - goto err_out;
>> - }
>> -
>> - hpriv->phys[0] = phy;
>> - hpriv->nports = 1;
>> - } else {
>> - rc = PTR_ERR(phy);
>> - switch (rc) {
>> - case -ENOSYS:
>> - /* No PHY support. Check if PHY is required. */
>> - if (of_find_property(dev->of_node, "phys", NULL)) {
>> - dev_err(dev, "couldn't get sata-phy: ENOSYS\n");
>> - goto err_out;
>> - }
>> - case -ENODEV:
>> - /* continue normally */
>> - hpriv->phys = NULL;
>> - break;
>> -
>> - default:
>> - goto err_out;
>> + rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node);
>> + if (rc)
>> + goto err_out;
>>
>> - }
>> - }
>> + rc = ahci_platform_get_regulator(hpriv, 0, dev, dev->of_node);
>> + if (rc == -EPROBE_DEFER)
>> + goto err_out;
>> }
>> -
>> pm_runtime_enable(dev);
>> pm_runtime_get_sync(dev);
>> hpriv->got_runtime_pm = true;
>> diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h
>> index 642d6ae4030c..f65b33809170 100644
>> --- a/include/linux/ahci_platform.h
>> +++ b/include/linux/ahci_platform.h
>> @@ -24,6 +24,8 @@ struct platform_device;
>>
>> int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
>> void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
>> +int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv);
>> +void ahci_platform_disable_regulators(struct ahci_host_priv *hpriv);
>> int ahci_platform_enable_resources(struct ahci_host_priv *hpriv);
>> void ahci_platform_disable_resources(struct ahci_host_priv *hpriv);
>> struct ahci_host_priv *ahci_platform_get_resources(
>>
>
> Regards,
>
> Hans
> --
> 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


\
 
 \ /
  Last update: 2014-12-27 14:21    [W:0.069 / U:1.004 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site