Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ata: libahci: allow to use multiple PHYs

The current implementation of the libahci does not allow to use multiple
PHYs. This patch adds the support of multiple PHYs by the libahci while
keeping the old bindings valid for device tree compatibility.

This introduce a new way of defining SATA ports in the device tree, with
one port per sub-node. This as the advantage of allowing a per port
configuration. Because some ports may be accessible but disabled in the
device tree, the port_map mask is computed automatically when using
this.

Signed-off-by: Antoine Ténart <antoine.tenart@free-electrons.com>
Acked-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

Antoine Ténart and committed by
Tejun Heo
b1a9edbd 725c7b57

+157 -39
+6 -1
drivers/ata/ahci.h
··· 334 334 bool got_runtime_pm; /* Did we do pm_runtime_get? */ 335 335 struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ 336 336 struct regulator *target_pwr; /* Optional */ 337 - struct phy *phy; /* If platform uses phy */ 337 + /* 338 + * If platform uses PHYs. There is a 1:1 relation between the port number and 339 + * the PHY position in this array. 340 + */ 341 + struct phy **phys; 342 + unsigned nports; /* Number of ports */ 338 343 void *plat_data; /* Other platform data */ 339 344 /* 340 345 * Optional ahci_start_engine override, if not set this gets set to the
+151 -38
drivers/ata/libahci_platform.c
··· 39 39 }; 40 40 41 41 /** 42 + * ahci_platform_enable_phys - Enable PHYs 43 + * @hpriv: host private area to store config values 44 + * 45 + * This function enables all the PHYs found in hpriv->phys, if any. 46 + * If a PHY fails to be enabled, it disables all the PHYs already 47 + * enabled in reverse order and returns an error. 48 + * 49 + * RETURNS: 50 + * 0 on success otherwise a negative error code 51 + */ 52 + int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) 53 + { 54 + int rc, i; 55 + 56 + for (i = 0; i < hpriv->nports; i++) { 57 + if (!hpriv->phys[i]) 58 + continue; 59 + 60 + rc = phy_init(hpriv->phys[i]); 61 + if (rc) 62 + goto disable_phys; 63 + 64 + rc = phy_power_on(hpriv->phys[i]); 65 + if (rc) { 66 + phy_exit(hpriv->phys[i]); 67 + goto disable_phys; 68 + } 69 + } 70 + 71 + return 0; 72 + 73 + disable_phys: 74 + while (--i >= 0) { 75 + phy_power_off(hpriv->phys[i]); 76 + phy_exit(hpriv->phys[i]); 77 + } 78 + return rc; 79 + } 80 + EXPORT_SYMBOL_GPL(ahci_platform_enable_phys); 81 + 82 + /** 83 + * ahci_platform_disable_phys - Disable PHYs 84 + * @hpriv: host private area to store config values 85 + * 86 + * This function disables all PHYs found in hpriv->phys. 87 + */ 88 + void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) 89 + { 90 + int i; 91 + 92 + for (i = 0; i < hpriv->nports; i++) { 93 + if (!hpriv->phys[i]) 94 + continue; 95 + 96 + phy_power_off(hpriv->phys[i]); 97 + phy_exit(hpriv->phys[i]); 98 + } 99 + } 100 + EXPORT_SYMBOL_GPL(ahci_platform_disable_phys); 101 + 102 + /** 42 103 * ahci_platform_enable_clks - Enable platform clocks 43 104 * @hpriv: host private area to store config values 44 105 * ··· 153 92 * following order: 154 93 * 1) Regulator 155 94 * 2) Clocks (through ahci_platform_enable_clks) 156 - * 3) Phy 95 + * 3) Phys 157 96 * 158 97 * If resource enabling fails at any point the previous enabled resources 159 98 * are disabled in reverse order. ··· 175 114 if (rc) 176 115 goto disable_regulator; 177 116 178 - if (hpriv->phy) { 179 - rc = phy_init(hpriv->phy); 180 - if (rc) 181 - goto disable_clks; 182 - 183 - rc = phy_power_on(hpriv->phy); 184 - if (rc) { 185 - phy_exit(hpriv->phy); 186 - goto disable_clks; 187 - } 188 - } 117 + rc = ahci_platform_enable_phys(hpriv); 118 + if (rc) 119 + goto disable_clks; 189 120 190 121 return 0; 191 122 ··· 197 144 * 198 145 * This function disables all ahci_platform managed resources in the 199 146 * following order: 200 - * 1) Phy 147 + * 1) Phys 201 148 * 2) Clocks (through ahci_platform_disable_clks) 202 149 * 3) Regulator 203 150 */ 204 151 void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) 205 152 { 206 - if (hpriv->phy) { 207 - phy_power_off(hpriv->phy); 208 - phy_exit(hpriv->phy); 209 - } 153 + ahci_platform_disable_phys(hpriv); 210 154 211 155 ahci_platform_disable_clks(hpriv); 212 156 ··· 237 187 * 2) regulator for controlling the targets power (optional) 238 188 * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, 239 189 * or for non devicetree enabled platforms a single clock 240 - * 4) phy (optional) 190 + * 4) phys (optional) 241 191 * 242 192 * RETURNS: 243 193 * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ··· 247 197 struct device *dev = &pdev->dev; 248 198 struct ahci_host_priv *hpriv; 249 199 struct clk *clk; 250 - int i, rc = -ENOMEM; 200 + struct device_node *child; 201 + int i, enabled_ports = 0, rc = -ENOMEM; 202 + u32 mask_port_map = 0; 251 203 252 204 if (!devres_open_group(dev, NULL, GFP_KERNEL)) 253 205 return ERR_PTR(-ENOMEM); ··· 298 246 hpriv->clks[i] = clk; 299 247 } 300 248 301 - hpriv->phy = devm_phy_get(dev, "sata-phy"); 302 - if (IS_ERR(hpriv->phy)) { 303 - rc = PTR_ERR(hpriv->phy); 304 - switch (rc) { 305 - case -ENOSYS: 306 - /* No PHY support. Check if PHY is required. */ 307 - if (of_find_property(dev->of_node, "phys", NULL)) { 308 - dev_err(dev, "couldn't get sata-phy: ENOSYS\n"); 249 + hpriv->nports = of_get_child_count(dev->of_node); 250 + 251 + if (hpriv->nports) { 252 + hpriv->phys = devm_kzalloc(dev, 253 + hpriv->nports * sizeof(*hpriv->phys), 254 + GFP_KERNEL); 255 + if (!hpriv->phys) { 256 + rc = -ENOMEM; 257 + goto err_out; 258 + } 259 + 260 + for_each_child_of_node(dev->of_node, child) { 261 + u32 port; 262 + 263 + if (!of_device_is_available(child)) 264 + continue; 265 + 266 + if (of_property_read_u32(child, "reg", &port)) { 267 + rc = -EINVAL; 309 268 goto err_out; 310 269 } 311 - case -ENODEV: 312 - /* continue normally */ 313 - hpriv->phy = NULL; 314 - break; 315 270 316 - case -EPROBE_DEFER: 317 - goto err_out; 271 + if (port >= hpriv->nports) { 272 + dev_warn(dev, "invalid port number %d\n", port); 273 + continue; 274 + } 318 275 319 - default: 320 - dev_err(dev, "couldn't get sata-phy\n"); 276 + mask_port_map |= BIT(port); 277 + 278 + hpriv->phys[port] = devm_of_phy_get(dev, child, NULL); 279 + if (IS_ERR(hpriv->phys[port])) { 280 + rc = PTR_ERR(hpriv->phys[port]); 281 + dev_err(dev, 282 + "couldn't get PHY in node %s: %d\n", 283 + child->name, rc); 284 + goto err_out; 285 + } 286 + 287 + enabled_ports++; 288 + } 289 + if (!enabled_ports) { 290 + dev_warn(dev, "No port enabled\n"); 291 + rc = -ENODEV; 321 292 goto err_out; 293 + } 294 + 295 + if (!hpriv->mask_port_map) 296 + hpriv->mask_port_map = mask_port_map; 297 + } else { 298 + /* 299 + * If no sub-node was found, keep this for device tree 300 + * compatibility 301 + */ 302 + struct phy *phy = devm_phy_get(dev, "sata-phy"); 303 + if (!IS_ERR(phy)) { 304 + hpriv->phys = devm_kzalloc(dev, sizeof(*hpriv->phys), 305 + GFP_KERNEL); 306 + if (!hpriv->phys) { 307 + rc = -ENOMEM; 308 + goto err_out; 309 + } 310 + 311 + hpriv->phys[0] = phy; 312 + hpriv->nports = 1; 313 + } else { 314 + rc = PTR_ERR(phy); 315 + switch (rc) { 316 + case -ENOSYS: 317 + /* No PHY support. Check if PHY is required. */ 318 + if (of_find_property(dev->of_node, "phys", NULL)) { 319 + dev_err(dev, "couldn't get sata-phy: ENOSYS\n"); 320 + goto err_out; 321 + } 322 + case -ENODEV: 323 + /* continue normally */ 324 + hpriv->phys = NULL; 325 + break; 326 + 327 + default: 328 + goto err_out; 329 + 330 + } 322 331 } 323 332 } 324 333 ··· 403 290 * @pi_template: template for the ata_port_info to use 404 291 * 405 292 * This function does all the usual steps needed to bring up an 406 - * ahci-platform host, note any necessary resources (ie clks, phy, etc.) 293 + * ahci-platform host, note any necessary resources (ie clks, phys, etc.) 407 294 * must be initialized / enabled before calling this. 408 295 * 409 296 * RETURNS: ··· 518 405 * @dev: device pointer for the host 519 406 * 520 407 * This function does all the usual steps needed to suspend an 521 - * ahci-platform host, note any necessary resources (ie clks, phy, etc.) 408 + * ahci-platform host, note any necessary resources (ie clks, phys, etc.) 522 409 * must be disabled after calling this. 523 410 * 524 411 * RETURNS: ··· 555 442 * @dev: device pointer for the host 556 443 * 557 444 * This function does all the usual steps needed to resume an ahci-platform 558 - * host, note any necessary resources (ie clks, phy, etc.) must be 445 + * host, note any necessary resources (ie clks, phys, etc.) must be 559 446 * initialized / enabled before calling this. 560 447 * 561 448 * RETURNS: