diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-09 19:40:42 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-09 19:40:42 -0800 |
commit | 3e8c04eb117445d67ae2b83e08bec4005129356a (patch) | |
tree | bef05c66adb15c581a5d08ac89103f4ff9867cd8 /drivers | |
parent | 44dbf058de9f46cf112518c99bb1352e8350ceb3 (diff) | |
parent | b12aa1f25e1da7e23fa44f646ac46e41366cfc6c (diff) |
Merge branch 'for-3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
Pull libata changes from Tejun Heo:
"Mostly driver-specific changes. Nothing too noteworthy.
This pull request contains three merges from for-3.19-fixes. The
first two are to pull ahci_xgene and sata_dwc_460ex fix commits which
are depended upon by later changes. The last one is to pull in a fix
patch which missed the v3.19-rc window"
* 'for-3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: (24 commits)
ahci_xgene: Fix the dma state machine lockup for the ATA_CMD_SMART PIO mode command.
ata: libahci: Use of_platform_device_create only if supported
sata_mv: Delete unnecessary checks before the function call "phy_power_off"
ata: Delete unnecessary checks before the function call "pci_dev_put"
ata: pata_platform: fix owner module reference mismatch for scsi host
ata: ahci_platform: fix owner module reference mismatch for scsi host
pata_pdc2027x: Use 64-bit timekeeping
ata: libahci: Fix devres cleanup on failure
ata: libahci: Allow using multiple regulators
Documentation: bindings: Add the regulator property to the sub-nodes AHCI bindings
ata: libahci: Clean-up the ahci_platform_en/disable_phys functions
sata_rcar: extend PM methods
sata_dwc_460ex: disable compilation on ARM and ARM64
ata: libata-core: Remove unused function
sata_dwc_460ex: convert to devm_kzalloc in ->probe()
sata_dwc_460ex: remove extra message
sata_dwc_460ex: use np local variable in ->probe()
sata_dwc_460ex: fix most of the sparse warnings
sata_dwc_460ex: enable COMPILE_TEST for the driver
sata_dwc_460ex: remove redundant dev_set_drvdata
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/Kconfig | 2 | ||||
-rw-r--r-- | drivers/ata/ahci.h | 6 | ||||
-rw-r--r-- | drivers/ata/ahci_da850.c | 11 | ||||
-rw-r--r-- | drivers/ata/ahci_imx.c | 25 | ||||
-rw-r--r-- | drivers/ata/ahci_mvebu.c | 11 | ||||
-rw-r--r-- | drivers/ata/ahci_platform.c | 11 | ||||
-rw-r--r-- | drivers/ata/ahci_st.c | 11 | ||||
-rw-r--r-- | drivers/ata/ahci_sunxi.c | 11 | ||||
-rw-r--r-- | drivers/ata/ahci_tegra.c | 11 | ||||
-rw-r--r-- | drivers/ata/ahci_xgene.c | 203 | ||||
-rw-r--r-- | drivers/ata/libahci_platform.c | 249 | ||||
-rw-r--r-- | drivers/ata/libata-core.c | 27 | ||||
-rw-r--r-- | drivers/ata/libata-eh.c | 1 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 4 | ||||
-rw-r--r-- | drivers/ata/libata.h | 1 | ||||
-rw-r--r-- | drivers/ata/pata_cs5530.c | 6 | ||||
-rw-r--r-- | drivers/ata/pata_of_platform.c | 10 | ||||
-rw-r--r-- | drivers/ata/pata_pdc2027x.c | 10 | ||||
-rw-r--r-- | drivers/ata/pata_platform.c | 8 | ||||
-rw-r--r-- | drivers/ata/sata_dwc_460ex.c | 116 | ||||
-rw-r--r-- | drivers/ata/sata_mv.c | 6 | ||||
-rw-r--r-- | drivers/ata/sata_rcar.c | 25 |
22 files changed, 541 insertions, 224 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 5f601553b9b0..e7f338a3a3c2 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -269,7 +269,7 @@ config ATA_PIIX config SATA_DWC tristate "DesignWare Cores SATA support" - depends on 460EX + depends on 460EX || (COMPILE_TEST && !(ARM || ARM64)) help This option enables support for the on-chip SATA controller of the AppliedMicro processor 460EX. diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 40f0e34f17af..71262e08648e 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. @@ -354,6 +354,10 @@ extern int ahci_ignore_sss; extern struct device_attribute *ahci_shost_attrs[]; extern struct device_attribute *ahci_sdev_attrs[]; +/* + * This must be instantiated by the edge drivers. Read the comments + * for ATA_BASE_SHT + */ #define AHCI_SHT(drv_name) \ ATA_NCQ_SHT(drv_name), \ .can_queue = AHCI_MAX_CMDS - 1, \ diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index ce8a7a6d6c7f..267a3d3e79f4 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -16,6 +16,8 @@ #include <linux/ahci_platform.h> #include "ahci.h" +#define DRV_NAME "ahci_da850" + /* SATA PHY Control Register offset from AHCI base */ #define SATA_P0PHYCR_REG 0x178 @@ -59,6 +61,10 @@ static const struct ata_port_info ahci_da850_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_da850_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -85,7 +91,8 @@ static int ahci_da850_probe(struct platform_device *pdev) da850_sata_init(dev, pwrdn_reg, hpriv->mmio); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -102,7 +109,7 @@ static struct platform_driver ahci_da850_driver = { .probe = ahci_da850_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci_da850", + .name = DRV_NAME, .pm = &ahci_da850_pm_ops, }, }; diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 35d51c59a370..3f3a7db208ae 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -28,6 +28,8 @@ #include <linux/libata.h> #include "ahci.h" +#define DRV_NAME "ahci-imx" + enum { /* Timer 1-ms Register */ IMX_TIMER1MS = 0x00e0, @@ -221,11 +223,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 +270,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 +290,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) @@ -524,6 +522,10 @@ static u32 imx_ahci_parse_props(struct device *dev, return reg_value; } +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int imx_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -620,7 +622,8 @@ static int imx_ahci_probe(struct platform_device *pdev) reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; writel(reg_val, hpriv->mmio + IMX_TIMER1MS); - ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, + &ahci_platform_sht); if (ret) goto disable_sata; @@ -678,7 +681,7 @@ static struct platform_driver imx_ahci_driver = { .probe = imx_ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci-imx", + .name = DRV_NAME, .of_match_table = imx_ahci_of_match, .pm = &ahci_imx_pm_ops, }, diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index 64bb08432b69..23716dd8a7ec 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -19,6 +19,8 @@ #include <linux/platform_device.h> #include "ahci.h" +#define DRV_NAME "ahci-mvebu" + #define AHCI_VENDOR_SPECIFIC_0_ADDR 0xa0 #define AHCI_VENDOR_SPECIFIC_0_DATA 0xa4 @@ -67,6 +69,10 @@ static const struct ata_port_info ahci_mvebu_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_mvebu_probe(struct platform_device *pdev) { struct ahci_host_priv *hpriv; @@ -88,7 +94,8 @@ static int ahci_mvebu_probe(struct platform_device *pdev) ahci_mvebu_mbus_config(hpriv, dram); ahci_mvebu_regret_option(hpriv); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -114,7 +121,7 @@ static struct platform_driver ahci_mvebu_driver = { .probe = ahci_mvebu_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci-mvebu", + .name = DRV_NAME, .of_match_table = ahci_mvebu_of_match, }, }; diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 18d539837045..78d6ae0b90c4 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -22,6 +22,8 @@ #include <linux/ahci_platform.h> #include "ahci.h" +#define DRV_NAME "ahci" + static const struct ata_port_info ahci_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, @@ -29,6 +31,10 @@ static const struct ata_port_info ahci_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -46,7 +52,8 @@ static int ahci_probe(struct platform_device *pdev) if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -75,7 +82,7 @@ static struct platform_driver ahci_driver = { .probe = ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci", + .name = DRV_NAME, .of_match_table = ahci_of_match, .pm = &ahci_pm_ops, }, diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 2f9e8317cc16..bc971af262e7 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -23,6 +23,8 @@ #include "ahci.h" +#define DRV_NAME "st_ahci" + #define ST_AHCI_OOBR 0xbc #define ST_AHCI_OOBR_WE BIT(31) #define ST_AHCI_OOBR_CWMIN_SHIFT 24 @@ -140,6 +142,10 @@ static const struct ata_port_info st_ahci_port_info = { .port_ops = &st_ahci_port_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int st_ahci_probe(struct platform_device *pdev) { struct st_ahci_drv_data *drv_data; @@ -166,7 +172,8 @@ static int st_ahci_probe(struct platform_device *pdev) if (err) return err; - err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info); + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, + &ahci_platform_sht); if (err) { ahci_platform_disable_resources(hpriv); return err; @@ -229,7 +236,7 @@ MODULE_DEVICE_TABLE(of, st_ahci_match); static struct platform_driver st_ahci_driver = { .driver = { - .name = "st_ahci", + .name = DRV_NAME, .pm = &st_ahci_pm_ops, .of_match_table = of_match_ptr(st_ahci_match), }, diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c index e2e0da539a2f..b26437430163 100644 --- a/drivers/ata/ahci_sunxi.c +++ b/drivers/ata/ahci_sunxi.c @@ -27,6 +27,8 @@ #include <linux/regulator/consumer.h> #include "ahci.h" +#define DRV_NAME "ahci-sunxi" + /* Insmod parameters */ static bool enable_pmp; module_param(enable_pmp, bool, 0); @@ -169,6 +171,10 @@ static const struct ata_port_info ahci_sunxi_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_sunxi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -200,7 +206,8 @@ static int ahci_sunxi_probe(struct platform_device *pdev) if (!enable_pmp) hpriv->flags |= AHCI_HFLAG_NO_PMP; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -251,7 +258,7 @@ static struct platform_driver ahci_sunxi_driver = { .probe = ahci_sunxi_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci-sunxi", + .name = DRV_NAME, .of_match_table = ahci_sunxi_of_match, .pm = &ahci_sunxi_pm_ops, }, diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 032904402c95..3a62eb246d80 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -31,6 +31,8 @@ #include "ahci.h" +#define DRV_NAME "tegra-ahci" + #define SATA_CONFIGURATION_0 0x180 #define SATA_CONFIGURATION_EN_FPCI BIT(0) @@ -289,6 +291,10 @@ static const struct of_device_id tegra_ahci_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int tegra_ahci_probe(struct platform_device *pdev) { struct ahci_host_priv *hpriv; @@ -354,7 +360,8 @@ static int tegra_ahci_probe(struct platform_device *pdev) if (ret) return ret; - ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, + &ahci_platform_sht); if (ret) goto deinit_controller; @@ -370,7 +377,7 @@ static struct platform_driver tegra_ahci_driver = { .probe = tegra_ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "tegra-ahci", + .name = DRV_NAME, .of_match_table = tegra_ahci_of_match, }, /* LP0 suspend support not implemented */ diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index cbcd20810355..2e8bb603e447 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -30,6 +30,8 @@ #include <linux/phy/phy.h> #include "ahci.h" +#define DRV_NAME "xgene-ahci" + /* Max # of disk per a controller */ #define MAX_AHCI_CHN_PERCTR 2 @@ -85,6 +87,7 @@ struct xgene_ahci_context { struct ahci_host_priv *hpriv; struct device *dev; u8 last_cmd[MAX_AHCI_CHN_PERCTR]; /* tracking the last command issued*/ + u32 class[MAX_AHCI_CHN_PERCTR]; /* tracking the class of device */ void __iomem *csr_core; /* Core CSR address of IP */ void __iomem *csr_diag; /* Diag CSR address of IP */ void __iomem *csr_axi; /* AXI CSR address of IP */ @@ -105,17 +108,69 @@ static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) } /** + * xgene_ahci_poll_reg_val- Poll a register on a specific value. + * @ap : ATA port of interest. + * @reg : Register of interest. + * @val : Value to be attained. + * @interval : waiting interval for polling. + * @timeout : timeout for achieving the value. + */ +static int xgene_ahci_poll_reg_val(struct ata_port *ap, + void __iomem *reg, unsigned + int val, unsigned long interval, + unsigned long timeout) +{ + unsigned long deadline; + unsigned int tmp; + + tmp = ioread32(reg); + deadline = ata_deadline(jiffies, timeout); + + while (tmp != val && time_before(jiffies, deadline)) { + ata_msleep(ap, interval); + tmp = ioread32(reg); + } + + return tmp; +} + +/** * xgene_ahci_restart_engine - Restart the dma engine. * @ap : ATA port of interest * - * Restarts the dma engine inside the controller. + * Waits for completion of multiple commands and restarts + * the DMA engine inside the controller. */ static int xgene_ahci_restart_engine(struct ata_port *ap) { struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; + + /* + * In case of PMP multiple IDENTIFY DEVICE commands can be + * issued inside PxCI. So need to poll PxCI for the + * completion of outstanding IDENTIFY DEVICE commands before + * we restart the DMA engine. + */ + if (xgene_ahci_poll_reg_val(ap, port_mmio + + PORT_CMD_ISSUE, 0x0, 1, 100)) + return -EBUSY; ahci_stop_engine(ap); ahci_start_fis_rx(ap); + + /* + * Enable the PxFBS.FBS_EN bit as it + * gets cleared due to stopping the engine. + */ + if (pp->fbs_supported) { + fbs = readl(port_mmio + PORT_FBS); + writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + } + hpriv->start_engine(ap); return 0; @@ -125,11 +180,17 @@ static int xgene_ahci_restart_engine(struct ata_port *ap) * xgene_ahci_qc_issue - Issue commands to the device * @qc: Command to issue * - * Due to Hardware errata for IDENTIFY DEVICE command and PACKET - * command of ATAPI protocol set, the controller cannot clear the BSY bit - * after receiving the PIO setup FIS. This results in the DMA state machine - * going into the CMFatalErrorUpdate state and locks up. By restarting the - * DMA engine, it removes the controller out of lock up state. + * Due to Hardware errata for IDENTIFY DEVICE command, the controller cannot + * clear the BSY bit after receiving the PIO setup FIS. This results in the dma + * state machine goes into the CMFatalErrorUpdate state and locks up. By + * restarting the dma engine, it removes the controller out of lock up state. + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The + * workaround is to write the pmp value to PxFBS.DEV field before issuing + * any command to PMP. */ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) { @@ -137,9 +198,23 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) struct ahci_host_priv *hpriv = ap->host->private_data; struct xgene_ahci_context *ctx = hpriv->plat_data; int rc = 0; + u32 port_fbs; + void *port_mmio = ahci_port_base(ap); + + /* + * Write the pmp value to PxFBS.DEV + * for case of Port Mulitplier. + */ + if (ctx->class[ap->port_no] == ATA_DEV_PMP) { + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + } if (unlikely((ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA) || - (ctx->last_cmd[ap->port_no] == ATA_CMD_PACKET))) + (ctx->last_cmd[ap->port_no] == ATA_CMD_PACKET) || + (ctx->last_cmd[ap->port_no] == ATA_CMD_SMART))) xgene_ahci_restart_engine(ap); rc = ahci_qc_issue(qc); @@ -365,16 +440,119 @@ static void xgene_ahci_host_stop(struct ata_host *host) ahci_platform_disable_resources(hpriv); } +/** + * xgene_ahci_pmp_softreset - Issue the softreset to the drives connected + * to Port Multiplier. + * @link: link to reset + * @class: Return value to indicate class of device + * @deadline: deadline jiffies for the operation + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The workaround + * is to write the pmp value to PxFBS.DEV field before issuing any command + * to PMP. + */ +static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + struct ata_port *ap = link->ap; + u32 rc; + void *port_mmio = ahci_port_base(ap); + u32 port_fbs; + + /* + * Set PxFBS.DEV field with pmp + * value. + */ + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + + rc = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); + + return rc; +} + +/** + * xgene_ahci_softreset - Issue the softreset to the drive. + * @link: link to reset + * @class: Return value to indicate class of device + * @deadline: deadline jiffies for the operation + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The workaround + * is to write the pmp value to PxFBS.DEV field before issuing any command + * to PMP. Here is the algorithm to detect PMP : + * + * 1. Save the PxFBS value + * 2. Program PxFBS.DEV with pmp value send by framework. Framework sends + * 0xF for both PMP/NON-PMP initially + * 3. Issue softreset + * 4. If signature class is PMP goto 6 + * 5. restore the original PxFBS and goto 3 + * 6. return + */ +static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct xgene_ahci_context *ctx = hpriv->plat_data; + void *port_mmio = ahci_port_base(ap); + u32 port_fbs; + u32 port_fbs_save; + u32 retry = 1; + u32 rc; + + port_fbs_save = readl(port_mmio + PORT_FBS); + + /* + * Set PxFBS.DEV field with pmp + * value. + */ + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + +softreset_retry: + rc = ahci_do_softreset(link, class, pmp, + deadline, ahci_check_ready); + + ctx->class[ap->port_no] = *class; + if (*class != ATA_DEV_PMP) { + /* + * Retry for normal drives without + * setting PxFBS.DEV field with pmp value. + */ + if (retry--) { + writel(port_fbs_save, port_mmio + PORT_FBS); + goto softreset_retry; + } + } + + return rc; +} + static struct ata_port_operations xgene_ahci_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, .hardreset = xgene_ahci_hardreset, .read_id = xgene_ahci_read_id, .qc_issue = xgene_ahci_qc_issue, + .softreset = xgene_ahci_softreset, + .pmp_softreset = xgene_ahci_pmp_softreset }; static const struct ata_port_info xgene_ahci_port_info = { - .flags = AHCI_FLAG_COMMON, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &xgene_ahci_ops, @@ -446,6 +624,10 @@ static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx) return val & CFG_SATA_ENET_SELECT_MASK ? -1 : 0; } +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int xgene_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -523,7 +705,8 @@ static int xgene_ahci_probe(struct platform_device *pdev) skip_clk_phy: hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -545,7 +728,7 @@ static struct platform_driver xgene_ahci_driver = { .probe = xgene_ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "xgene-ahci", + .name = DRV_NAME, .of_match_table = xgene_ahci_of_match, }, }; diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 0b03f9056692..d89305d289f6 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -24,6 +24,7 @@ #include <linux/ahci_platform.h> #include <linux/phy/phy.h> #include <linux/pm_runtime.h> +#include <linux/of_platform.h> #include "ahci.h" static void ahci_host_stop(struct ata_host *host); @@ -34,10 +35,6 @@ struct ata_port_operations ahci_platform_ops = { }; EXPORT_SYMBOL_GPL(ahci_platform_ops); -static struct scsi_host_template ahci_platform_sht = { - AHCI_SHT("ahci_platform"), -}; - /** * ahci_platform_enable_phys - Enable PHYs * @hpriv: host private area to store config values @@ -54,9 +51,6 @@ static 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; @@ -89,9 +83,6 @@ static 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]); } @@ -144,6 +135,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]) + 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 * @@ -163,11 +207,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) @@ -183,8 +225,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); @@ -205,8 +247,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); @@ -222,6 +263,69 @@ static void ahci_platform_put_resources(struct device *dev, void *res) for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) clk_put(hpriv->clks[c]); + /* + * The regulators are tied to child node device and not to the + * SATA device itself. So we can't use devm for automatically + * releasing them. We have to do it manually here. + */ + for (c = 0; c < hpriv->nports; c++) + if (hpriv->target_pwrs && hpriv->target_pwrs[c]) + regulator_put(hpriv->target_pwrs[c]); + + kfree(hpriv->target_pwrs); +} + +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 regulator *target_pwr; + int rc = 0; + + target_pwr = regulator_get_optional(dev, "target"); + + if (!IS_ERR(target_pwr)) + hpriv->target_pwrs[port] = target_pwr; + else + rc = PTR_ERR(target_pwr); + + return rc; } /** @@ -246,7 +350,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, child_nodes; u32 mask_port_map = 0; if (!devres_open_group(dev, NULL, GFP_KERNEL)) @@ -267,14 +371,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, @@ -296,19 +392,33 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) hpriv->clks[i] = clk; } - hpriv- |