|
@@ -38,6 +38,67 @@ static struct scsi_host_template ahci_platform_sht = {
|
|
AHCI_SHT("ahci_platform"),
|
|
AHCI_SHT("ahci_platform"),
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * 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
|
|
|
|
+ */
|
|
|
|
+int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
|
|
|
|
+{
|
|
|
|
+ int rc, i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hpriv->nports; i++) {
|
|
|
|
+ if (!hpriv->phys[i])
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ rc = phy_init(hpriv->phys[i]);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto disable_phys;
|
|
|
|
+
|
|
|
|
+ rc = phy_power_on(hpriv->phys[i]);
|
|
|
|
+ if (rc) {
|
|
|
|
+ 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;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(ahci_platform_enable_phys);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * ahci_platform_disable_phys - Disable PHYs
|
|
|
|
+ * @hpriv: host private area to store config values
|
|
|
|
+ *
|
|
|
|
+ * This function disables all PHYs found in hpriv->phys.
|
|
|
|
+ */
|
|
|
|
+void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hpriv->nports; i++) {
|
|
|
|
+ if (!hpriv->phys[i])
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ phy_power_off(hpriv->phys[i]);
|
|
|
|
+ phy_exit(hpriv->phys[i]);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(ahci_platform_disable_phys);
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* ahci_platform_enable_clks - Enable platform clocks
|
|
* ahci_platform_enable_clks - Enable platform clocks
|
|
* @hpriv: host private area to store config values
|
|
* @hpriv: host private area to store config values
|
|
@@ -92,7 +153,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
|
|
* following order:
|
|
* following order:
|
|
* 1) Regulator
|
|
* 1) Regulator
|
|
* 2) Clocks (through ahci_platform_enable_clks)
|
|
* 2) Clocks (through ahci_platform_enable_clks)
|
|
- * 3) Phy
|
|
|
|
|
|
+ * 3) Phys
|
|
*
|
|
*
|
|
* If resource enabling fails at any point the previous enabled resources
|
|
* If resource enabling fails at any point the previous enabled resources
|
|
* are disabled in reverse order.
|
|
* are disabled in reverse order.
|
|
@@ -114,17 +175,9 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
|
|
if (rc)
|
|
if (rc)
|
|
goto disable_regulator;
|
|
goto disable_regulator;
|
|
|
|
|
|
- if (hpriv->phy) {
|
|
|
|
- rc = phy_init(hpriv->phy);
|
|
|
|
- if (rc)
|
|
|
|
- goto disable_clks;
|
|
|
|
-
|
|
|
|
- rc = phy_power_on(hpriv->phy);
|
|
|
|
- if (rc) {
|
|
|
|
- phy_exit(hpriv->phy);
|
|
|
|
- goto disable_clks;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ rc = ahci_platform_enable_phys(hpriv);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto disable_clks;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -144,16 +197,13 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
|
|
*
|
|
*
|
|
* This function disables all ahci_platform managed resources in the
|
|
* This function disables all ahci_platform managed resources in the
|
|
* following order:
|
|
* following order:
|
|
- * 1) Phy
|
|
|
|
|
|
+ * 1) Phys
|
|
* 2) Clocks (through ahci_platform_disable_clks)
|
|
* 2) Clocks (through ahci_platform_disable_clks)
|
|
* 3) Regulator
|
|
* 3) Regulator
|
|
*/
|
|
*/
|
|
void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
|
|
void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
|
|
{
|
|
{
|
|
- if (hpriv->phy) {
|
|
|
|
- phy_power_off(hpriv->phy);
|
|
|
|
- phy_exit(hpriv->phy);
|
|
|
|
- }
|
|
|
|
|
|
+ ahci_platform_disable_phys(hpriv);
|
|
|
|
|
|
ahci_platform_disable_clks(hpriv);
|
|
ahci_platform_disable_clks(hpriv);
|
|
|
|
|
|
@@ -187,7 +237,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
|
|
* 2) regulator for controlling the targets power (optional)
|
|
* 2) regulator for controlling the targets power (optional)
|
|
* 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
|
|
* 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
|
|
* or for non devicetree enabled platforms a single clock
|
|
* or for non devicetree enabled platforms a single clock
|
|
- * 4) phy (optional)
|
|
|
|
|
|
+ * 4) phys (optional)
|
|
*
|
|
*
|
|
* RETURNS:
|
|
* RETURNS:
|
|
* The allocated ahci_host_priv on success, otherwise an ERR_PTR value
|
|
* The allocated ahci_host_priv on success, otherwise an ERR_PTR value
|
|
@@ -197,7 +247,9 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
|
|
struct device *dev = &pdev->dev;
|
|
struct device *dev = &pdev->dev;
|
|
struct ahci_host_priv *hpriv;
|
|
struct ahci_host_priv *hpriv;
|
|
struct clk *clk;
|
|
struct clk *clk;
|
|
- int i, rc = -ENOMEM;
|
|
|
|
|
|
+ struct device_node *child;
|
|
|
|
+ int i, enabled_ports = 0, rc = -ENOMEM;
|
|
|
|
+ u32 mask_port_map = 0;
|
|
|
|
|
|
if (!devres_open_group(dev, NULL, GFP_KERNEL))
|
|
if (!devres_open_group(dev, NULL, GFP_KERNEL))
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
@@ -246,28 +298,89 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
|
|
hpriv->clks[i] = clk;
|
|
hpriv->clks[i] = clk;
|
|
}
|
|
}
|
|
|
|
|
|
- hpriv->phy = devm_phy_get(dev, "sata-phy");
|
|
|
|
- if (IS_ERR(hpriv->phy)) {
|
|
|
|
- rc = PTR_ERR(hpriv->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");
|
|
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for_each_child_of_node(dev->of_node, child) {
|
|
|
|
+ u32 port;
|
|
|
|
+
|
|
|
|
+ if (!of_device_is_available(child))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (of_property_read_u32(child, "reg", &port)) {
|
|
|
|
+ rc = -EINVAL;
|
|
goto err_out;
|
|
goto err_out;
|
|
}
|
|
}
|
|
- case -ENODEV:
|
|
|
|
- /* continue normally */
|
|
|
|
- hpriv->phy = NULL;
|
|
|
|
- break;
|
|
|
|
|
|
|
|
- case -EPROBE_DEFER:
|
|
|
|
- goto err_out;
|
|
|
|
|
|
+ if (port >= hpriv->nports) {
|
|
|
|
+ dev_warn(dev, "invalid port number %d\n", port);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ goto err_out;
|
|
|
|
+ }
|
|
|
|
|
|
- default:
|
|
|
|
- dev_err(dev, "couldn't get sata-phy\n");
|
|
|
|
|
|
+ enabled_ports++;
|
|
|
|
+ }
|
|
|
|
+ if (!enabled_ports) {
|
|
|
|
+ dev_warn(dev, "No port enabled\n");
|
|
|
|
+ rc = -ENODEV;
|
|
goto err_out;
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ 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
|
|
|
|
+ */
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
pm_runtime_enable(dev);
|
|
pm_runtime_enable(dev);
|
|
@@ -290,7 +403,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
|
|
* @pi_template: template for the ata_port_info to use
|
|
* @pi_template: template for the ata_port_info to use
|
|
*
|
|
*
|
|
* This function does all the usual steps needed to bring up an
|
|
* This function does all the usual steps needed to bring up an
|
|
- * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
|
|
|
|
|
|
+ * ahci-platform host, note any necessary resources (ie clks, phys, etc.)
|
|
* must be initialized / enabled before calling this.
|
|
* must be initialized / enabled before calling this.
|
|
*
|
|
*
|
|
* RETURNS:
|
|
* RETURNS:
|
|
@@ -405,7 +518,7 @@ static void ahci_host_stop(struct ata_host *host)
|
|
* @dev: device pointer for the host
|
|
* @dev: device pointer for the host
|
|
*
|
|
*
|
|
* This function does all the usual steps needed to suspend an
|
|
* This function does all the usual steps needed to suspend an
|
|
- * ahci-platform host, note any necessary resources (ie clks, phy, etc.)
|
|
|
|
|
|
+ * ahci-platform host, note any necessary resources (ie clks, phys, etc.)
|
|
* must be disabled after calling this.
|
|
* must be disabled after calling this.
|
|
*
|
|
*
|
|
* RETURNS:
|
|
* RETURNS:
|
|
@@ -442,7 +555,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
|
|
* @dev: device pointer for the host
|
|
* @dev: device pointer for the host
|
|
*
|
|
*
|
|
* This function does all the usual steps needed to resume an ahci-platform
|
|
* This function does all the usual steps needed to resume an ahci-platform
|
|
- * host, note any necessary resources (ie clks, phy, etc.) must be
|
|
|
|
|
|
+ * host, note any necessary resources (ie clks, phys, etc.) must be
|
|
* initialized / enabled before calling this.
|
|
* initialized / enabled before calling this.
|
|
*
|
|
*
|
|
* RETURNS:
|
|
* RETURNS:
|