Skip to content

Commit

Permalink
ata: libahci_platform: support non-consecutive port numbers
Browse files Browse the repository at this point in the history
So far ahci_platform relied on number of child nodes in firmware to
allocate arrays and expected port numbers to start from 0 without holes.
This number of ports is then set in private structure for use when
configuring phys and regulators.

Some platforms may not use every port of an ahci controller.
E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading
to the following errors during boot:
[    1.719476] ahci f2540000.sata: invalid port number 1
[    1.724562] ahci f2540000.sata: No port enabled

Update all accessesors of ahci_host_priv phys and target_pwrs arrays to
support holes. Access is gated by hpriv->mask_port_map which has a bit
set for each enabled port.

Update ahci_platform_get_resources to ignore holes in the port numbers
and enable ports defined in firmware by their reg property only.

When firmware does not define children it is assumed that there is
exactly one port, using index 0.

Signed-off-by: Josua Mayer <josua@solid-run.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
  • Loading branch information
Josua Mayer authored and Damien Le Moal committed Jan 6, 2025
1 parent 7b64859 commit 8c87215
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 6 deletions.
3 changes: 3 additions & 0 deletions drivers/ata/ahci_brcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,

/* Re-initialize and calibrate the PHY */
for (i = 0; i < hpriv->nports; i++) {
if (!(hpriv->mask_port_map & (1 << i)))
continue;

rc = phy_init(hpriv->phys[i]);
if (rc)
goto disable_phys;
Expand Down
6 changes: 6 additions & 0 deletions drivers/ata/ahci_ceva.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
goto disable_clks;

for (i = 0; i < hpriv->nports; i++) {
if (!(hpriv->mask_port_map & (1 << i)))
continue;

rc = phy_init(hpriv->phys[i]);
if (rc)
goto disable_rsts;
Expand All @@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
ahci_platform_deassert_rsts(hpriv);

for (i = 0; i < hpriv->nports; i++) {
if (!(hpriv->mask_port_map & (1 << i)))
continue;

rc = phy_power_on(hpriv->phys[i]);
if (rc) {
phy_exit(hpriv->phys[i]);
Expand Down
40 changes: 34 additions & 6 deletions drivers/ata/libahci_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
int rc, i;

for (i = 0; i < hpriv->nports; i++) {
if (!(hpriv->mask_port_map & (1 << i)))
continue;

rc = phy_init(hpriv->phys[i]);
if (rc)
goto disable_phys;
Expand All @@ -70,6 +73,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)

disable_phys:
while (--i >= 0) {
if (!(hpriv->mask_port_map & (1 << i)))
continue;

phy_power_off(hpriv->phys[i]);
phy_exit(hpriv->phys[i]);
}
Expand All @@ -88,6 +94,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
int i;

for (i = 0; i < hpriv->nports; i++) {
if (!(hpriv->mask_port_map & (1 << i)))
continue;

phy_power_off(hpriv->phys[i]);
phy_exit(hpriv->phys[i]);
}
Expand Down Expand Up @@ -432,6 +441,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv,
return 0;
}

static u32 ahci_platform_find_max_port_id(struct device *dev)
{
u32 max_port = 0;

for_each_child_of_node_scoped(dev->of_node, child) {
u32 port;

if (!of_property_read_u32(child, "reg", &port))
max_port = max(max_port, port);
}

return max_port;
}

/**
* ahci_platform_get_resources - Get platform resources
* @pdev: platform device to get resources for
Expand All @@ -458,6 +481,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
u32 mask_port_map = 0;
u32 max_port;

if (!devres_open_group(dev, NULL, GFP_KERNEL))
return ERR_PTR(-ENOMEM);
Expand Down Expand Up @@ -549,15 +573,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
goto err_out;
}

/* find maximum port id for allocating structures */
max_port = ahci_platform_find_max_port_id(dev);
/*
* If no sub-node was found, we still need to set nports to
* one in order to be able to use the
* Set nports according to maximum port id. Clamp at
* AHCI_MAX_PORTS, warning message for invalid port id
* is generated later.
* When DT has no sub-nodes max_port is 0, nports is 1,
* in order to be able to use the
* ahci_platform_[en|dis]able_[phys|regulators] functions.
*/
if (child_nodes)
hpriv->nports = child_nodes;
else
hpriv->nports = 1;
hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1);

hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL);
if (!hpriv->phys) {
Expand Down Expand Up @@ -625,6 +651,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
* If no sub-node was found, keep this for device tree
* compatibility
*/
hpriv->mask_port_map |= BIT(0);

rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node);
if (rc)
goto err_out;
Expand Down

0 comments on commit 8c87215

Please sign in to comment.