From 76884cb2f7da5282019bf5c90e8f804429150742 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 26 Feb 2014 14:47:21 +0000 Subject: ahci: st: Add support for ST's SATA IP Acked-by: Alexandre Torgue Signed-off-by: Lee Jones Signed-off-by: Tejun Heo --- drivers/ata/ahci_st.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 drivers/ata/ahci_st.c (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c new file mode 100644 index 000000000000..2f951332227f --- /dev/null +++ b/drivers/ata/ahci_st.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2012 STMicroelectronics Limited + * + * Authors: Francesco Virlinzi + * Alexandre Torgue + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ahci.h" + +#define ST_AHCI_OOBR 0xbc +#define ST_AHCI_OOBR_WE BIT(31) +#define ST_AHCI_OOBR_CWMIN_SHIFT 24 +#define ST_AHCI_OOBR_CWMAX_SHIFT 16 +#define ST_AHCI_OOBR_CIMIN_SHIFT 8 +#define ST_AHCI_OOBR_CIMAX_SHIFT 0 + +struct st_ahci_drv_data { + struct platform_device *ahci; + struct phy *phy; + struct reset_control *pwr; + struct reset_control *sw_rst; + struct reset_control *pwr_rst; + struct ahci_host_priv *hpriv; +}; + +static void st_ahci_configure_oob(void __iomem *mmio) +{ + unsigned long old_val, new_val; + + new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) | + (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) | + (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) | + (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT); + + old_val = readl(mmio + ST_AHCI_OOBR); + writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR); + writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR); + writel(new_val, mmio + ST_AHCI_OOBR); +} + +static int st_ahci_deassert_resets(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + int err; + + if (drv_data->pwr) { + err = reset_control_deassert(drv_data->pwr); + if (err) { + dev_err(dev, "unable to bring out of pwrdwn\n"); + return err; + } + } + + st_ahci_configure_oob(drv_data->hpriv->mmio); + + if (drv_data->sw_rst) { + err = reset_control_deassert(drv_data->sw_rst); + if (err) { + dev_err(dev, "unable to bring out of sw-rst\n"); + return err; + } + } + + if (drv_data->pwr_rst) { + err = reset_control_deassert(drv_data->pwr_rst); + if (err) { + dev_err(dev, "unable to bring out of pwr-rst\n"); + return err; + } + } + + return 0; +} + +static int st_ahci_probe_resets(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev); + + drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn"); + if (IS_ERR(drv_data->pwr)) { + dev_info(&pdev->dev, "power reset control not defined\n"); + drv_data->pwr = NULL; + } + + drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst"); + if (IS_ERR(drv_data->sw_rst)) { + dev_info(&pdev->dev, "soft reset control not defined\n"); + drv_data->sw_rst = NULL; + } + + drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst"); + if (IS_ERR(drv_data->pwr_rst)) { + dev_dbg(&pdev->dev, "power soft reset control not defined\n"); + drv_data->pwr_rst = NULL; + } + + return st_ahci_deassert_resets(&pdev->dev); +} + +static const struct ata_port_info st_ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static int st_ahci_probe(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data; + struct ahci_host_priv *hpriv; + int err; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + platform_set_drvdata(pdev, drv_data); + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + drv_data->hpriv = hpriv; + + err = st_ahci_probe_resets(pdev); + if (err) + return err; + + err = ahci_platform_enable_resources(hpriv); + if (err) + return err; + + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0); + if (err) { + ahci_platform_disable_resources(hpriv); + return err; + } + + return 0; +} + +static int st_ahci_remove(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) + dev_err(&pdev->dev, "unable to pwrdwn\n"); + } + + ahci_platform_disable_resources(hpriv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_ahci_suspend(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) { + dev_err(dev, "unable to pwrdwn"); + return err; + } + } + + ahci_platform_disable_resources(hpriv); + + return 0; +} + +static int st_ahci_resume(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + err = ahci_platform_enable_resources(hpriv); + if (err) + return err; + + err = st_ahci_deassert_resets(dev); + if (err) { + ahci_platform_disable_resources(hpriv); + return err; + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume); + +static struct of_device_id st_ahci_match[] = { + { .compatible = "st,ahci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_ahci_match); + +static struct platform_driver st_ahci_driver = { + .driver = { + .name = "st_ahci", + .owner = THIS_MODULE, + .pm = &st_ahci_pm_ops, + .of_match_table = of_match_ptr(st_ahci_match), + }, + .probe = st_ahci_probe, + .remove = st_ahci_remove, +}; +module_platform_driver(st_ahci_driver); + +MODULE_AUTHOR("Alexandre Torgue "); +MODULE_AUTHOR("Francesco Virlinzi "); +MODULE_DESCRIPTION("STMicroelectronics Sata Ahci driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4a2e51234038fb8b24963a2f32e8b55980085a23 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 12 Mar 2014 12:39:38 +0000 Subject: ahci: st: Standardise naming conventions Other devices have adopted similar naming conventions which have been accepted as the standard. This patch brings any mention of the the ST AHCI driver into line with them. Suggested-by: Bartlomiej Zolnierkiewicz Signed-off-by: Lee Jones Signed-off-by: Tejun Heo --- drivers/ata/Kconfig | 6 +++--- drivers/ata/ahci_st.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 46258ead19aa..88c2c1291b67 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -97,12 +97,12 @@ config SATA_AHCI_PLATFORM If unsure, say N. -config SATA_AHCI_ST - tristate "ST SATA support" +config AHCI_ST + tristate "ST AHCI SATA support" depends on SATA_AHCI_PLATFORM select GENERIC_PHY help - This option enables support for ST SATA controller. + This option enables support for ST AHCI SATA controller. If unsure, say N. diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 2f951332227f..f5af660835b0 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -235,5 +235,5 @@ module_platform_driver(st_ahci_driver); MODULE_AUTHOR("Alexandre Torgue "); MODULE_AUTHOR("Francesco Virlinzi "); -MODULE_DESCRIPTION("STMicroelectronics Sata Ahci driver"); +MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c51a848973da8b347191c0e317da3b912c2cdb6a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 12 Mar 2014 12:39:39 +0000 Subject: ahci: st: Remove legacy dependencies on PHY Suggested-by: Bartlomiej Zolnierkiewicz Signed-off-by: Lee Jones Signed-off-by: Tejun Heo --- drivers/ata/Kconfig | 1 - drivers/ata/ahci_st.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 88c2c1291b67..d2209e4f2dfe 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -100,7 +100,6 @@ config SATA_AHCI_PLATFORM config AHCI_ST tristate "ST AHCI SATA support" depends on SATA_AHCI_PLATFORM - select GENERIC_PHY help This option enables support for ST AHCI SATA controller. diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index f5af660835b0..a28532aad97b 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -33,7 +32,6 @@ struct st_ahci_drv_data { struct platform_device *ahci; - struct phy *phy; struct reset_control *pwr; struct reset_control *sw_rst; struct reset_control *pwr_rst; -- cgit v1.2.3 From a82370842834875c81b482181af280353aa6be05 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 12 Mar 2014 12:39:40 +0000 Subject: ahci: st: Utilise ata_platform_remove_one() call ata_platform_remove_one() allows us to specify our own exit function via platform data then goes off and removes ATA Host and Port in preparation for device removal. Suggested-by: Bartlomiej Zolnierkiewicz Signed-off-by: Lee Jones Signed-off-by: Tejun Heo --- drivers/ata/ahci_st.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index a28532aad97b..3edec5db3717 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -87,6 +87,23 @@ static int st_ahci_deassert_resets(struct device *dev) return 0; } +static int st_ahci_exit(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) + dev_err(&pdev->dev, "unable to pwrdwn\n"); + } + + ahci_platform_disable_resources(hpriv); + + return 0; +} + static int st_ahci_probe_resets(struct platform_device *pdev) { struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev); @@ -122,6 +139,7 @@ static const struct ata_port_info st_ahci_port_info = { static int st_ahci_probe(struct platform_device *pdev) { struct st_ahci_drv_data *drv_data; + struct ahci_platform_data *pdata; struct ahci_host_priv *hpriv; int err; @@ -131,6 +149,13 @@ static int st_ahci_probe(struct platform_device *pdev) platform_set_drvdata(pdev, drv_data); + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->exit = st_ahci_exit; + pdev->dev.platform_data = pdata; + hpriv = ahci_platform_get_resources(pdev); if (IS_ERR(hpriv)) return PTR_ERR(hpriv); @@ -154,23 +179,6 @@ static int st_ahci_probe(struct platform_device *pdev) return 0; } -static int st_ahci_remove(struct platform_device *pdev) -{ - struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev); - struct ahci_host_priv *hpriv = drv_data->hpriv; - int err; - - if (drv_data->pwr) { - err = reset_control_assert(drv_data->pwr); - if (err) - dev_err(&pdev->dev, "unable to pwrdwn\n"); - } - - ahci_platform_disable_resources(hpriv); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int st_ahci_suspend(struct device *dev) { @@ -227,7 +235,7 @@ static struct platform_driver st_ahci_driver = { .of_match_table = of_match_ptr(st_ahci_match), }, .probe = st_ahci_probe, - .remove = st_ahci_remove, + .remove = ata_platform_remove_one, }; module_platform_driver(st_ahci_driver); -- cgit v1.2.3 From 761a8c2765df17ac5296e2631a16ec08d1a0cb1c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 12 Mar 2014 12:39:42 +0000 Subject: ahci: st: Invoke AHCI Platform Suspend/Resume This is where we disable IRQs on suspend and update the internal power state during suspend/resume. Suggested-by: Bartlomiej Zolnierkiewicz Signed-off-by: Lee Jones Signed-off-by: Tejun Heo --- drivers/ata/ahci_st.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 3edec5db3717..17191b96648e 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -186,6 +186,10 @@ static int st_ahci_suspend(struct device *dev) struct ahci_host_priv *hpriv = drv_data->hpriv; int err; + ret = ahci_platform_suspend_host(dev); + if (ret) + return ret; + if (drv_data->pwr) { err = reset_control_assert(drv_data->pwr); if (err) { @@ -215,7 +219,7 @@ static int st_ahci_resume(struct device *dev) return err; } - return 0; + return ahci_platform_resume_host(dev); } #endif -- cgit v1.2.3 From 33081b34681742add8d8c1e49fc93045415e5a18 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 14 Mar 2014 19:20:58 +0100 Subject: ata: ahci_st: build fixes * The config option for ahci_st driver was renamed from CONFIG_SATA_AHCI_ST to CONFIG_AHCI_ST but Makefile was not updated. Fix it (also while at it move the ahci_st driver entry below ahci_imx and ahci_sunxi ones). * Fix a few build issues in the ahci_st driver itself. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/Makefile | 2 +- drivers/ata/ahci_st.c | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 6bbd6dadef50..e20148c022b4 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_ATA) += libata.o obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o -obj-$(CONFIG_SATA_AHCI_ST) += ahci_st.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o @@ -13,6 +12,7 @@ obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o obj-$(CONFIG_AHCI_IMX) += ahci_imx.o obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o +obj-$(CONFIG_AHCI_ST) += ahci_st.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 17191b96648e..e1aa5447a400 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -87,7 +87,7 @@ static int st_ahci_deassert_resets(struct device *dev) return 0; } -static int st_ahci_exit(struct device *dev) +static void st_ahci_exit(struct device *dev) { struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = drv_data->hpriv; @@ -96,12 +96,10 @@ static int st_ahci_exit(struct device *dev) if (drv_data->pwr) { err = reset_control_assert(drv_data->pwr); if (err) - dev_err(&pdev->dev, "unable to pwrdwn\n"); + dev_err(dev, "unable to pwrdwn\n"); } ahci_platform_disable_resources(hpriv); - - return 0; } static int st_ahci_probe_resets(struct platform_device *pdev) @@ -186,9 +184,9 @@ static int st_ahci_suspend(struct device *dev) struct ahci_host_priv *hpriv = drv_data->hpriv; int err; - ret = ahci_platform_suspend_host(dev); - if (ret) - return ret; + err = ahci_platform_suspend_host(dev); + if (err) + return err; if (drv_data->pwr) { err = reset_control_assert(drv_data->pwr); -- cgit v1.2.3 From b032378b4c3ffba86d2c78699b385ae646397938 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 14 Mar 2014 19:21:59 +0100 Subject: ata: ahci_st: remove deprecated struct ahci_platform_data usage struct ahci_platform_data is deprecated (please see comments in for details). Convert ahci_st driver to use custom ->host_stop method instead. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/ahci_st.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/ata/ahci_st.c') diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index e1aa5447a400..633222226c19 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -87,10 +87,11 @@ static int st_ahci_deassert_resets(struct device *dev) return 0; } -static void st_ahci_exit(struct device *dev) +static void st_ahci_host_stop(struct ata_host *host) { + struct ahci_host_priv *hpriv = host->private_data; + struct device *dev = host->dev; struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); - struct ahci_host_priv *hpriv = drv_data->hpriv; int err; if (drv_data->pwr) { @@ -127,17 +128,21 @@ static int st_ahci_probe_resets(struct platform_device *pdev) return st_ahci_deassert_resets(&pdev->dev); } +static struct ata_port_operations st_ahci_port_ops = { + .inherits = &ahci_platform_ops, + .host_stop = st_ahci_host_stop, +}; + static const struct ata_port_info st_ahci_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_ops, + .port_ops = &st_ahci_port_ops, }; static int st_ahci_probe(struct platform_device *pdev) { struct st_ahci_drv_data *drv_data; - struct ahci_platform_data *pdata; struct ahci_host_priv *hpriv; int err; @@ -147,13 +152,6 @@ static int st_ahci_probe(struct platform_device *pdev) platform_set_drvdata(pdev, drv_data); - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdata->exit = st_ahci_exit; - pdev->dev.platform_data = pdata; - hpriv = ahci_platform_get_resources(pdev); if (IS_ERR(hpriv)) return PTR_ERR(hpriv); -- cgit v1.2.3