diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-26 07:21:18 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-26 07:21:18 -1000 |
| commit | fb2c3f44a7e14627b9dca8e6b568e6cdc234b40b (patch) | |
| tree | 30711a073e5c2df6a4d8a7349e7627ff63998540 /drivers | |
| parent | 25e7ab89f4774b0f842cee0e8a20c11f755924d5 (diff) | |
| parent | 69be770908ae57354a41d7e1cc99ab46278d2e86 (diff) | |
Merge tag 'ata-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux
Pull ata updates from Niklas Cassel:
- Make ata_generic refuse to bind to non-IDE devices (me)
- Add missing <linux/pm_runtime.h> includes (Vladimir)
- Add pata_cswarp driver for the Amiga CS-Lab Warp card (Paolo)
- Fix a use-after-free and a parport attach bug in pata_parport (Pei)
- Switch ahci_da850 to DEFINE_SIMPLE_DEV_PM_OPS() (Li)
- Documentation and kernel-doc fixes (Karl, Bart, me)
- Remove the broken sata_inic162x driver (Ethan)
- Fix probe() error paths in libata, libahci and several drivers (me)
- Fail probe in sata_dwc_460ex if host activation fails (Nikolay)
- Use the standard DMA engine API in pata_arasan_cf (Frank)
- Abort multi-sector pass-through commands when multi_count is 0 (Hui)
- Replace kmap_atomic() with kmap_local_page() in libata-sff (Danish)
- Revert the no longer needed JMB582/JMB585 32-bit DMA quirk (Mario)
- Enable SATA LED presence indication in sata_mv (Carlos)
- Re-enable FBS after error handling on a port multiplier (Kyrylo)
- Fix NO_FUA quirks shadowed by earlier quirk entries (me)
- Do not overwrite the Marvell PATA quirk port mask in ahci (zhoumin)
- Ignore libata.force transfer modes the host does not support (Rand)
- Fix a race between IRQ deferral and ata_sff_flush_pio_task() (me)
* tag 'ata-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux: (26 commits)
ata: libata-sff: fix race between IRQ deferral and ata_sff_flush_pio_task()
ata: libata-core: Ignore forced transfer modes the host does not support
ata: ahci: preserve preset port masks
ata: libata-core: Fix NO_FUA quirks shadowed by earlier quirk entries
ata: libahci: re-enable FBS after error handling on a port multiplier
ata: sata_mv: enable SoC SATA LED presence indication
ata: libata: Fix scsi_done() documentation
Revert "ata: ahci: force 32-bit DMA for JMicron JMB582/JMB585"
ata: pata_parport: fix parport attach idr_alloc for
ata: libata-sff: replace kmap_atomic() with kmap_local_page()
ata: libata-scsi: abort multi-sector pass-through commands when multi_count is 0
ata: pata_arasan_cf: use standard DMA engine API
ata: sata_dwc_460ex: Don't enable interrupt if libATA core registration fails
ata: libata-core: Fix the ata_host_register() kdoc
ata: libata: Do not leave ata_host_stop() registered when activation fails
ata: libahci: Free the IRQs when activating a multi-IRQ host fails
ata: sata_fsl: Fix use-after-free of host_priv on probe() failure
ata: ahci_st: Assert the power down reset in the probe() error path
ata: ahci_st: Do not ignore errors when getting the reset controls
ata: sata_inic162x: remove dangerous broken driver
...
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/ata/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/ata/Makefile | 2 | ||||
| -rw-r--r-- | drivers/ata/ahci.c | 50 | ||||
| -rw-r--r-- | drivers/ata/ahci_brcm.c | 1 | ||||
| -rw-r--r-- | drivers/ata/ahci_ceva.c | 1 | ||||
| -rw-r--r-- | drivers/ata/ahci_da850.c | 6 | ||||
| -rw-r--r-- | drivers/ata/ahci_qoriq.c | 1 | ||||
| -rw-r--r-- | drivers/ata/ahci_st.c | 78 | ||||
| -rw-r--r-- | drivers/ata/ata_generic.c | 16 | ||||
| -rw-r--r-- | drivers/ata/libahci.c | 44 | ||||
| -rw-r--r-- | drivers/ata/libahci_platform.c | 4 | ||||
| -rw-r--r-- | drivers/ata/libata-core.c | 102 | ||||
| -rw-r--r-- | drivers/ata/libata-scsi.c | 8 | ||||
| -rw-r--r-- | drivers/ata/libata-sff.c | 45 | ||||
| -rw-r--r-- | drivers/ata/pata_arasan_cf.c | 6 | ||||
| -rw-r--r-- | drivers/ata/pata_cswarp.c | 183 | ||||
| -rw-r--r-- | drivers/ata/pata_parport/pata_parport.c | 9 | ||||
| -rw-r--r-- | drivers/ata/sata_dwc_460ex.c | 5 | ||||
| -rw-r--r-- | drivers/ata/sata_fsl.c | 24 | ||||
| -rw-r--r-- | drivers/ata/sata_inic162x.c | 903 | ||||
| -rw-r--r-- | drivers/ata/sata_mv.c | 10 | ||||
| -rw-r--r-- | drivers/ata/sata_qstor.c | 8 |
22 files changed, 493 insertions, 1030 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 28ca856ecc75..9704ed688775 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -328,13 +328,6 @@ config SATA_AHCI_SEATTLE If unsure, say N -config SATA_INIC162X - tristate "Initio 162x SATA support (Very Experimental)" - depends on PCI - select SATA_HOST - help - This option enables support for Initio 162x Serial ATA. - config SATA_ACARD_AHCI tristate "ACard AHCI variant (ATP 8620)" depends on PCI @@ -1040,6 +1033,16 @@ config PATA_GAYLE If unsure, say N. +config PATA_CSWARP + tristate "Amiga CS Warp PATA support" + depends on M68K && AMIGA && ZORRO + help + This option enables support for the on-board IDE interface on + CS-Lab Warp Expansion Card (NOTE that idemode=native has to be + set in Warp Configuration) + + If unsure, say N. + config PATA_BUDDHA tristate "Buddha/Catweasel/X-Surf PATA support" depends on ZORRO diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index b96025abd45e..20b400aea0c4 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_SATA_AHCI_SEATTLE) += ahci_seattle.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o obj-$(CONFIG_SATA_GEMINI) += sata_gemini.o -obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o @@ -100,6 +99,7 @@ obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o obj-$(CONFIG_PATA_FALCON) += pata_falcon.o obj-$(CONFIG_PATA_GAYLE) += pata_gayle.o +obj-$(CONFIG_PATA_CSWARP) += pata_cswarp.o obj-$(CONFIG_PATA_BUDDHA) += pata_buddha.o obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 9b8c0935001c..4916d9bf3293 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -26,6 +26,7 @@ #include <linux/device.h> #include <linux/dmi.h> #include <linux/gfp.h> +#include <linux/pm_runtime.h> #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <linux/libata.h> @@ -68,7 +69,6 @@ enum board_ids { /* board IDs for specific chipsets in alphabetical order */ board_ahci_al, board_ahci_avn, - board_ahci_jmb585, board_ahci_mcp65, board_ahci_mcp77, board_ahci_mcp89, @@ -213,15 +213,6 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_avn_ops, }, - /* JMicron JMB582/585: 64-bit DMA is broken, force 32-bit */ - [board_ahci_jmb585] = { - AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR | - AHCI_HFLAG_32BIT_ONLY), - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_ops, - }, [board_ahci_mcp65] = { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), @@ -955,14 +946,6 @@ static const struct pci_device_id ahci_pci_tbl[] = { PCI_VDEVICE(INTEL, 0x4b63), .driver_data = board_ahci_pcs_quirk, }, { - /* JMicron JMB582/585: force 32-bit DMA (broken 64-bit implementation) */ - PCI_VDEVICE(JMICRON, 0x0582), - .driver_data = board_ahci_jmb585, - - }, { - PCI_VDEVICE(JMICRON, 0x0585), - .driver_data = board_ahci_jmb585, - }, { /* JMicron 360/1/3/5/6, match class to avoid IDE function */ PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID), .class = PCI_CLASS_STORAGE_SATA_AHCI, @@ -1642,34 +1625,27 @@ MODULE_PARM_DESC(mask_port_ext, "where <pci_dev> is the PCI ID of an AHCI controller in the " "form \"domain:bus:dev.func\""); -static u32 ahci_port_mask(struct device *dev, char *mask_s) +static void ahci_port_mask(struct device *dev, char *mask_s, u32 *mask) { - unsigned int mask; - - if (kstrtouint(mask_s, 0, &mask)) { + if (kstrtou32(mask_s, 0, mask)) dev_err(dev, "Invalid port map mask\n"); - return 0; - } - - return mask; } -static u32 ahci_get_port_mask(struct device *dev, char *mask_p) +static void ahci_get_port_mask(struct device *dev, char *mask_p, u32 *mask) { char *param, *end, *str, *mask_s; char *name; - u32 mask = 0; if (!mask_p || !strlen(mask_p)) - return 0; + return; str = kstrdup(mask_p, GFP_KERNEL); if (!str) - return 0; + return; /* Handle single mask case */ if (!strchr(str, '=')) { - mask = ahci_port_mask(dev, str); + ahci_port_mask(dev, str, mask); goto free; } @@ -1704,13 +1680,11 @@ static u32 ahci_get_port_mask(struct device *dev, char *mask_p) param++; } - mask = ahci_port_mask(dev, mask_s); + ahci_port_mask(dev, mask_s, mask); } free: kfree(str); - - return mask; } static void ahci_pci_save_initial_config(struct pci_dev *pdev, @@ -1736,10 +1710,10 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev, } /* Handle port map masks passed as module parameter. */ - hpriv->mask_port_map = - ahci_get_port_mask(&pdev->dev, ahci_mask_port_map); - hpriv->mask_port_ext = - ahci_get_port_mask(&pdev->dev, ahci_mask_port_ext); + ahci_get_port_mask(&pdev->dev, ahci_mask_port_map, + &hpriv->mask_port_map); + ahci_get_port_mask(&pdev->dev, ahci_mask_port_ext, + &hpriv->mask_port_ext); ahci_save_initial_config(&pdev->dev, hpriv); } diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c index 29be74fedcf0..48460e515722 100644 --- a/drivers/ata/ahci_brcm.c +++ b/drivers/ata/ahci_brcm.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/string.h> diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c index 2961e53288f4..008869d9499b 100644 --- a/drivers/ata/ahci_ceva.c +++ b/drivers/ata/ahci_ceva.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include "ahci.h" diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index 4e53ff96d712..59b9ca26fd44 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -218,8 +218,8 @@ disable_resources: return rc; } -static SIMPLE_DEV_PM_OPS(ahci_da850_pm_ops, ahci_platform_suspend, - ahci_platform_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ahci_da850_pm_ops, ahci_platform_suspend, + ahci_platform_resume); static const struct of_device_id ahci_da850_of_match[] = { { .compatible = "ti,da850-ahci", }, @@ -233,7 +233,7 @@ static struct platform_driver ahci_da850_driver = { .driver = { .name = DRV_NAME, .of_match_table = ahci_da850_of_match, - .pm = &ahci_da850_pm_ops, + .pm = pm_sleep_ptr(&ahci_da850_pm_ops), }, }; module_platform_driver(ahci_da850_driver); diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c index 96492159fa7c..fc6b8cd75b32 100644 --- a/drivers/ata/ahci_qoriq.c +++ b/drivers/ata/ahci_qoriq.c @@ -14,6 +14,7 @@ #include <linux/device.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/libata.h> #include "ahci.h" diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 4336c8a6e208..eea75961038c 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -83,18 +83,27 @@ static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv, return 0; } -static void st_ahci_host_stop(struct ata_host *host) +static int st_ahci_assert_pwrdwn(struct ahci_host_priv *hpriv, + struct device *dev) { - struct ahci_host_priv *hpriv = host->private_data; struct st_ahci_drv_data *drv_data = hpriv->plat_data; - struct device *dev = host->dev; int err; - if (drv_data->pwr) { - err = reset_control_assert(drv_data->pwr); - if (err) - dev_err(dev, "unable to pwrdwn\n"); - } + if (!drv_data->pwr) + return 0; + + err = reset_control_assert(drv_data->pwr); + if (err) + dev_err(dev, "unable to pwrdwn\n"); + + return err; +} + +static void st_ahci_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + + st_ahci_assert_pwrdwn(hpriv, host->dev); ahci_platform_disable_resources(hpriv); } @@ -104,23 +113,20 @@ static int st_ahci_probe_resets(struct ahci_host_priv *hpriv, { struct st_ahci_drv_data *drv_data = hpriv->plat_data; - drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn"); - if (IS_ERR(drv_data->pwr)) { - dev_info(dev, "power reset control not defined\n"); - drv_data->pwr = NULL; - } + drv_data->pwr = devm_reset_control_get_optional(dev, "pwr-dwn"); + if (IS_ERR(drv_data->pwr)) + return dev_err_probe(dev, PTR_ERR(drv_data->pwr), + "failed to get pwr-dwn reset\n"); - drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst"); - if (IS_ERR(drv_data->sw_rst)) { - dev_info(dev, "soft reset control not defined\n"); - drv_data->sw_rst = NULL; - } + drv_data->sw_rst = devm_reset_control_get_optional(dev, "sw-rst"); + if (IS_ERR(drv_data->sw_rst)) + return dev_err_probe(dev, PTR_ERR(drv_data->sw_rst), + "failed to get sw-rst reset\n"); - drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst"); - if (IS_ERR(drv_data->pwr_rst)) { - dev_dbg(dev, "power soft reset control not defined\n"); - drv_data->pwr_rst = NULL; - } + drv_data->pwr_rst = devm_reset_control_get_optional(dev, "pwr-rst"); + if (IS_ERR(drv_data->pwr_rst)) + return dev_err_probe(dev, PTR_ERR(drv_data->pwr_rst), + "failed to get pwr-rst reset\n"); return st_ahci_deassert_resets(hpriv, dev); } @@ -162,38 +168,38 @@ static int st_ahci_probe(struct platform_device *pdev) err = ahci_platform_enable_resources(hpriv); if (err) - return err; + goto assert_pwrdwn; st_ahci_configure_oob(hpriv->mmio); err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, &ahci_platform_sht); - if (err) { - ahci_platform_disable_resources(hpriv); - return err; - } + if (err) + goto disable_resources; return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); +assert_pwrdwn: + st_ahci_assert_pwrdwn(hpriv, &pdev->dev); + + return err; } static int st_ahci_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; - struct st_ahci_drv_data *drv_data = hpriv->plat_data; int err; err = ahci_platform_suspend_host(dev); if (err) return err; - if (drv_data->pwr) { - err = reset_control_assert(drv_data->pwr); - if (err) { - dev_err(dev, "unable to pwrdwn"); - return err; - } - } + err = st_ahci_assert_pwrdwn(hpriv, dev); + if (err) + return err; ahci_platform_disable_resources(hpriv); diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c index 18ea740ca582..2858aed4a648 100644 --- a/drivers/ata/ata_generic.c +++ b/drivers/ata/ata_generic.c @@ -150,6 +150,9 @@ static int is_intel_ider(struct pci_dev *dev) return 1; } +/* Forward declaration for the pci_match_id() call in ata_generic_init_one() */ +static const struct pci_device_id ata_generic[]; + /** * ata_generic_init_one - attach generic IDE * @dev: PCI device found @@ -172,6 +175,19 @@ static int ata_generic_init_one(struct pci_dev *dev, const struct pci_device_id }; const struct ata_port_info *ppi[] = { &info, NULL }; + /* + * A device matched through driver_override or through an ID added + * with new_id does not come from our ID table, so pci_match_device() + * hands us a synthetic ID with no driver_data and none of the checks + * below apply. Probing maps BAR0 and BAR1 as the ATA command and + * control blocks and writes to the device control register, so only + * continue for devices which report the IDE class or which we list + * ourselves. + */ + if (!pci_match_id(ata_generic, dev) && + (dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return -ENODEV; + /* Don't use the generic entry unless instructed to do so */ if ((id->driver_data & ATA_GEN_CLASS_MATCH) && all_generic_ide == 0) return -ENODEV; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 08ab56e95566..001a6f5e4172 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -30,6 +30,7 @@ #include <scsi/scsi_cmnd.h> #include <linux/libata.h> #include <linux/pci.h> +#include <linux/pm_runtime.h> #include "ahci.h" #include "libata.h" @@ -2233,6 +2234,21 @@ void ahci_error_handler(struct ata_port *ap) sata_pmp_error_handler(ap); + /* + * ahci_do_softreset() turns FBS off before resetting a fan-out link + * and turns it back on only if that reset succeeds. When it fails + * (e.g. a hot-plugged drive is still spinning up), EH escalates to a + * PMP hardreset, which never re-enables FBS, so the port would keep + * command-based switching until the PMP is attached again (reboot). + * Restore FBS once recovery is done and the PMP is still attached. + */ + if (sata_pmp_attached(ap) && ata_dev_enabled(ap->link.device)) { + struct ahci_port_priv *pp = ap->private_data; + + if (pp->fbs_supported && !pp->fbs_enabled) + ahci_enable_fbs(ap); + } + if (!ata_dev_enabled(ap->link.device)) hpriv->stop_engine(ap); } @@ -2736,11 +2752,35 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, 0, pp->irq_desc, host->ports[i]); if (rc) - return rc; + goto free_irqs; ata_port_desc_misc(host->ports[i], irq); } - return ata_host_register(host, sht); + rc = ata_host_register(host, sht); + if (rc) + goto free_irqs; + + return 0; + +free_irqs: + /* + * Free the IRQs which have been requested, so that the handlers can no + * longer access the MMIO of the host once we return, e.g. after the + * caller has disabled the clocks of the host. + */ + while (--i >= 0) { + struct ahci_port_priv *pp = host->ports[i]->private_data; + + if (!pp) + continue; + + devm_free_irq(host->dev, hpriv->get_irq_vector(host, i), + host->ports[i]); + } + + ata_host_undo_start(host); + + return rc; } /** diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index f6524135ea11..ae342080556a 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -689,6 +689,10 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); * ahci-platform host, note any necessary resources (ie clks, phys, etc.) * must be initialized / enabled before calling this. * + * On failure, ->host_stop() is not called, so the caller has to release the + * resources it enabled (clocks, regulators, resets, PHYs) in its probe() + * error path. + * * RETURNS: * 0 on success otherwise a negative error code */ diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 92233fb25051..9c188b0d6236 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -434,6 +434,7 @@ static void ata_force_link_limits(struct ata_link *link) */ static void ata_force_xfermask(struct ata_device *dev) { + struct ata_port *ap = dev->link->ap; int devno = dev->link->pmp + dev->devno; int alt_devno = devno; int i; @@ -458,6 +459,17 @@ static void ata_force_xfermask(struct ata_device *dev) ata_unpack_xfermask(fe->param.xfer_mask, &pio_mask, &mwdma_mask, &udma_mask); + + /* Make sure that the port supports the specified xfer modes. */ + if (!(pio_mask & ap->pio_mask) && + !(mwdma_mask & ap->mwdma_mask) && + !(udma_mask & ap->udma_mask)) { + ata_dev_warn(dev, + "FORCE: ignoring unsupported xfer_mask %s\n", + fe->param.name); + continue; + } + if (udma_mask) dev->udma_mask = udma_mask; else if (mwdma_mask) { @@ -4323,7 +4335,8 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { { "MAXTOR 6L080L4", "A93.0500", ATA_QUIRK_BROKEN_HPA }, /* this one allows HPA unlocking but fails IOs on the area */ - { "OCZ-VERTEX", "1.30", ATA_QUIRK_BROKEN_HPA }, + { "OCZ-VERTEX", "1.30", ATA_QUIRK_BROKEN_HPA | + ATA_QUIRK_NO_FUA }, /* Devices which report 1 sector over size HPA */ { "ST340823A", NULL, ATA_QUIRK_HPA_SIZE }, @@ -4459,6 +4472,8 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { { "Crucial*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM }, { "INTEL SSDSC2KG480G8", "XCV10120", ATA_QUIRK_ZERO_AFTER_TRIM | ATA_QUIRK_MAX_SEC }, + { "INTEL*SSDSC2CT*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM | + ATA_QUIRK_NO_FUA }, { "INTEL*SSD*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM }, { "SSD*INTEL*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM }, { "Samsung*SSD*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM }, @@ -4479,7 +4494,8 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { { "WDC WD1200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, { "WDC WD1600JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, { "WDC WD2000JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, - { "WDC WD2500JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, + { "WDC WD2500JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM | + ATA_QUIRK_NO_FUA }, { "WDC WD3000JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, { "WDC WD3200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, @@ -4504,7 +4520,6 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { { "Maxtor", "BANC1G10", ATA_QUIRK_NO_FUA }, { "WDC*WD2500J*", NULL, ATA_QUIRK_NO_FUA }, { "OCZ-VERTEX*", NULL, ATA_QUIRK_NO_FUA }, - { "INTEL*SSDSC2CT*", NULL, ATA_QUIRK_NO_FUA }, /* End Marker */ { } @@ -6129,6 +6144,46 @@ int ata_host_start(struct ata_host *host) EXPORT_SYMBOL_GPL(ata_host_start); /** + * ata_host_undo_start - undo ata_host_start() + * @host: ATA host to undo_start + * + * Stop the ports of @host and drop the devres action registered by + * ata_host_start(), without calling ->host_stop(). Nothing is done if + * @host has not been started. + * + * This gives the release of the host resources back to the caller, which + * is what a driver whose probe() error path releases those resources + * itself needs when starting or activating the host fails. + * + * LOCKING: + * Inherited from calling layer (may sleep). + */ +void ata_host_undo_start(struct ata_host *host) +{ + int i; + + if (!(host->flags & ATA_HOST_STARTED)) + return; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + if (ap->ops->port_stop) + ap->ops->port_stop(ap); + } + + /* + * Drop the action added by ata_host_start() without calling it. + * It does not exist if neither ->port_stop() nor ->host_stop() is + * implemented, in which case there is nothing to drop. + */ + devres_destroy(host->dev, ata_host_stop, NULL, NULL); + + host->flags &= ~ATA_HOST_STARTED; +} +EXPORT_SYMBOL_GPL(ata_host_undo_start); + +/** * ata_host_init - Initialize a host struct for sas (ipr, libsas) * @host: host to initialize * @dev: device host is attached to @@ -6198,9 +6253,15 @@ static void async_port_probe(void *data, async_cookie_t cookie) * @sht: template for SCSI host * * Register initialized ATA host. @host is allocated using - * ata_host_alloc() and fully initialized by LLD. This function - * starts ports, registers @host with ATA and SCSI layers and - * probe registered devices. + * ata_host_alloc(), fully initialized by the LLD and started using + * ata_host_start(). This function registers @host with the ATA and + * SCSI layers and probes the registered devices. + * + * On failure, the host remains started, i.e. the devres action + * registered by ata_host_start() is kept, so ->host_stop() is called by + * the driver core when probe() fails. A caller which releases the host + * resources in its own probe() error path must therefore call + * ata_host_undo_start(). * * LOCKING: * Inherited from calling layer (may sleep). @@ -6295,6 +6356,10 @@ EXPORT_SYMBOL_GPL(ata_host_register); * have set polling mode on the port. In this case, @irq_handler * should be NULL. * + * On failure, the ports are stopped again and the devres action + * registered by ata_host_start() is dropped without calling + * ->host_stop(), so releasing the host resources is left to the caller. + * * LOCKING: * Inherited from calling layer (may sleep). * @@ -6315,27 +6380,40 @@ int ata_host_activate(struct ata_host *host, int irq, /* Special case for polling mode */ if (!irq) { WARN_ON(irq_handler); - return ata_host_register(host, sht); + rc = ata_host_register(host, sht); + if (rc) + goto undo_start; + + return 0; } irq_desc = devm_kasprintf(host->dev, GFP_KERNEL, "%s[%s]", dev_driver_string(host->dev), dev_name(host->dev)); - if (!irq_desc) - return -ENOMEM; + if (!irq_desc) { + rc = -ENOMEM; + goto undo_start; + } rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags, irq_desc, host); if (rc) - return rc; + goto undo_start; for (i = 0; i < host->n_ports; i++) ata_port_desc_misc(host->ports[i], irq); rc = ata_host_register(host, sht); - /* if failed, just free the IRQ and leave ports alone */ - if (rc) + if (rc) { + /* Free the IRQ, so that the handler can no longer run */ devm_free_irq(host->dev, irq, host); + goto undo_start; + } + + return 0; + +undo_start: + ata_host_undo_start(host); return rc; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5939371242b0..a61fb0331279 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3582,6 +3582,12 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) if (is_multi_taskfile(tf)) { unsigned int multi_count = 1 << (cdb[1] >> 5); + if (!dev->multi_count) { + ata_scsi_set_sense(dev, scmd, ABORTED_COMMAND, + NO_ADDITIONAL_SENSE_INFORMATION); + return 1; + } + /* compare the passed through multi_count * with the cached multi_count of libata */ @@ -5462,7 +5468,7 @@ bool ata_scsi_offline_dev(struct ata_device *dev) * ata_scsi_remove_dev - remove attached SCSI device * @dev: ATA device to remove attached SCSI device for * - * This function is called from ata_eh_scsi_hotplug() and + * This function is called from ata_scsi_hotplug() and * responsible for removing the SCSI device attached to @dev. * * LOCKING: diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 976e4e160494..cf2b49457456 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -580,9 +580,9 @@ static void ata_pio_xfer(struct ata_queued_cmd *qc, struct page *page, bool do_write = (qc->tf.flags & ATA_TFLAG_WRITE); unsigned char *buf; - buf = kmap_atomic(page); + buf = kmap_local_page(page); qc->ap->ops->sff_data_xfer(qc, buf + offset, xfer_size, do_write); - kunmap_atomic(buf); + kunmap_local(buf); if (!do_write && !PageSlab(page)) flush_dcache_page(page); @@ -763,9 +763,9 @@ next_sg: trace_atapi_pio_transfer_data(qc, offset, count); /* do the actual data transfer */ - buf = kmap_atomic(page); + buf = kmap_local_page(page); consumed = ap->ops->sff_data_xfer(qc, buf + offset, count, rw); - kunmap_atomic(buf); + kunmap_local(buf); bytes -= min(bytes, consumed); qc->curbytes += count; @@ -1196,8 +1196,6 @@ void ata_sff_flush_pio_task(struct ata_port *ap) { trace_ata_sff_flush_pio_task(ap); - cancel_delayed_work_sync(&ap->sff_pio_task); - /* * We wanna reset the HSM state to IDLE. If we do so without * grabbing the port lock, critical sections protected by it which @@ -1205,11 +1203,26 @@ void ata_sff_flush_pio_task(struct ata_port *ap) * example, we may set IDLE in between the time * __ata_sff_port_intr() checks for HSM_ST_IDLE and before it calls * ata_sff_hsm_move() causing ata_sff_hsm_move() to BUG(). + * + * Reset the state before cancelling the work item: once the state + * is IDLE, __ata_sff_port_intr() can no longer enter + * ata_sff_hsm_move(), which prevents the interrupt handler from + * queueing sff_pio_task when deferring the completion of a PIO + * data-in command. The SCSI error handler cannot dispatch new + * commands while error handling is in progress, so issue-time + * queuing is not possible either. */ spin_lock_irq(ap->lock); ap->hsm_task_state = HSM_ST_IDLE; spin_unlock_irq(ap->lock); + /* + * Now that the interrupt handler can no longer queue the pio task, + * cancel it. This also drains a work item that the interrupt + * handler managed to queue before the HSM state was reset. + */ + cancel_delayed_work_sync(&ap->sff_pio_task); + ap->sff_pio_task_link = NULL; } @@ -1233,7 +1246,13 @@ static void ata_sff_pio_task(struct work_struct *work) } fsm_start: - WARN_ON_ONCE(ap->hsm_task_state == HSM_ST_IDLE); + /* + * ata_sff_flush_pio_task() may have marked the HSM idle after this + * work item was queued or while it was running. There is nothing + * left to do. + */ + if (ap->hsm_task_state == HSM_ST_IDLE) + goto out_unlock; wait_mask = ATA_BUSY; if (ap->hsm_task_state == HSM_ST_LAST) @@ -1252,6 +1271,13 @@ fsm_start: ata_msleep(ap, 2); spin_lock_irq(ap->lock); + /* + * ata_sff_flush_pio_task() may have marked the HSM idle + * while the port lock was dropped for ata_msleep(). + */ + if (ap->hsm_task_state == HSM_ST_IDLE) + goto out_unlock; + status = ata_sff_busy_wait(ap, wait_mask, 10); if (status & wait_mask) { ata_sff_queue_pio_task(link, ATA_SHORT_PAUSE); @@ -2267,6 +2293,11 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_prepare_host); * hosts. This separate helper is necessary because SFF hosts * use two separate interrupts in legacy mode. * + * Note that, unlike ata_host_activate(), the devres action registered + * by ata_host_start() is kept on failure, i.e. ->host_stop() is called + * by the driver core when probe() fails. All callers of this function + * rely on that, none of them releases the host resources itself. + * * LOCKING: * Inherited from calling layer (may sleep). * diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index e05a4847e037..a652a1b57338 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -400,7 +400,7 @@ dma_xfer(struct arasan_cf_dev *acdev, dma_addr_t src, dma_addr_t dest, u32 len) unsigned long flags = DMA_PREP_INTERRUPT; int ret = 0; - tx = chan->device->device_prep_dma_memcpy(chan, dest, src, len, flags); + tx = dmaengine_prep_dma_memcpy(chan, dest, src, len, flags); if (!tx) { dev_err(acdev->host->dev, "device_prep_dma_memcpy failed\n"); return -EAGAIN; @@ -408,7 +408,7 @@ dma_xfer(struct arasan_cf_dev *acdev, dma_addr_t src, dma_addr_t dest, u32 len) tx->callback = dma_callback; tx->callback_param = acdev; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { @@ -416,7 +416,7 @@ dma_xfer(struct arasan_cf_dev *acdev, dma_addr_t src, dma_addr_t dest, u32 len) return ret; } - chan->device->device_issue_pending(chan); + dma_async_issue_pending(chan); /* Wait for DMA to complete */ if (!wait_for_completion_timeout(&acdev->dma_completion, TIMEOUT)) { diff --git a/drivers/ata/pata_cswarp.c b/drivers/ata/pata_cswarp.c new file mode 100644 index 000000000000..58fe6d0012dc --- /dev/null +++ b/drivers/ata/pata_cswarp.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Amiga CS Warp PATA controller driver + * + * Copyright (c) 2024 CS-Lab s.c. + * http://www.cs-lab.eu + * + * Based on pata_gayle.c, pata_buddha.c and warpATA.device: + * + * Created 2 Jun 2024 by Andrzej Rogozynski + */ + +#include <linux/ata.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/device-id/zorro.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/libata.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/zorro.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_host.h> + +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#include <asm/amigayle.h> +#include <asm/setup.h> + +#define DRV_NAME "pata_cswarp" + +#define WARP_OFFSET_ATA 0x6000 + +static const struct scsi_host_template pata_cswarp_sht = { + ATA_PIO_SHT(DRV_NAME), +}; + +static unsigned int pata_cswarp_data_xfer(struct ata_queued_cmd *qc, + unsigned char *buf, + unsigned int buflen, int rw) +{ + struct ata_device *dev = qc->dev; + struct ata_port *ap = dev->link->ap; + void __iomem *data_addr = ap->ioaddr.data_addr; + unsigned int words = buflen >> 1; + u16 *buf16 = (u16 *)buf; + + /* Transfer multiple of 2 bytes */ + if (rw == READ) + raw_insw(data_addr, buf16, words); + else + raw_outsw(data_addr, buf16, words); + + /* Transfer trailing byte, if any. */ + if (unlikely(buflen & 0x01)) { + if (rw == READ) + buf[buflen - 1] = raw_inw(data_addr) >> 8; + else + raw_outw(buf[buflen - 1] << 8, data_addr); + words++; + } + + return words << 1; +} + +/* + * Provide our own set_mode() as we don't want to change anything that has + * already been configured.. + */ +static int pata_cswarp_set_mode(struct ata_link *link, + struct ata_device **unused) +{ + struct ata_device *dev; + + ata_for_each_dev(dev, link, ENABLED) { + /* We don't really care */ + dev->pio_mode = dev->xfer_mode = XFER_PIO_0; + dev->xfer_shift = ATA_SHIFT_PIO; + dev->flags |= ATA_DFLAG_PIO; + ata_dev_info(dev, "configured for PIO\n"); + } + return 0; +} + +static struct ata_port_operations pata_cswarp_ops = { + .inherits = &ata_sff_port_ops, + .sff_data_xfer = pata_cswarp_data_xfer, + .cable_detect = ata_cable_unknown, + .set_mode = pata_cswarp_set_mode, +}; + +static int pata_cswarp_probe(struct zorro_dev *z, + const struct zorro_device_id *ent) +{ + static const char board_name[] = "csWarp"; + struct ata_host *host; + struct ata_port *ap; + struct resource *res; + void __iomem *base; + unsigned long board = z->resource.start; + + dev_info(&z->dev, "%s IDE controller (board: 0x%lx)\n", board_name, + board); + + res = devm_request_mem_region(&z->dev, board + WARP_OFFSET_ATA, 0x1800, + DRV_NAME); + if (!res) + return -ENXIO; + + host = ata_host_alloc(&z->dev, 1); + if (!host) + return -ENXIO; + + ap = host->ports[0]; + base = devm_ioremap(&z->dev, board + WARP_OFFSET_ATA, 0x1800); + if (!base) + return -ENOMEM; + + ap->ops = &pata_cswarp_ops; + + ap->pio_mask = ATA_PIO4; + ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY | + ATA_FLAG_PIO_POLLING; + + ap->ioaddr.data_addr = base; + ap->ioaddr.error_addr = base + 1 * 4; + ap->ioaddr.feature_addr = base + 1 * 4; + ap->ioaddr.nsect_addr = base + 2 * 4; + ap->ioaddr.lbal_addr = base + 3 * 4; + ap->ioaddr.lbam_addr = base + 4 * 4; + ap->ioaddr.lbah_addr = base + 5 * 4; + ap->ioaddr.device_addr = base + 6 * 4; + ap->ioaddr.status_addr = base + 7 * 4; + ap->ioaddr.command_addr = base + 7 * 4; + + ap->ioaddr.altstatus_addr = base + (0x1000 | (6UL << 2)); + ap->ioaddr.ctl_addr = base + (0x1000 | (6UL << 2)); + + ata_port_desc(ap, "mmio %pR", res); + + return ata_host_activate(host, 0, NULL, + IRQF_SHARED, &pata_cswarp_sht); +} + +static void pata_cswarp_remove(struct zorro_dev *z) +{ + struct ata_host *host = dev_get_drvdata(&z->dev); + + ata_host_detach(host); +} + +static const struct zorro_device_id pata_cswarp_zorro_tbl[] = { + { .id = ZORRO_PROD_CSLAB_WARP_1260 }, + { } +}; +MODULE_DEVICE_TABLE(zorro, pata_cswarp_zorro_tbl); + +static struct zorro_driver pata_cswarp_driver = { + .name = "pata_cswarp", + .id_table = pata_cswarp_zorro_tbl, + .probe = pata_cswarp_probe, + .remove = pata_cswarp_remove, +}; + +static int __init pata_cswarp_init(void) +{ + return zorro_register_driver(&pata_cswarp_driver); +} + +static void __exit pata_cswarp_unregister(void) +{ + zorro_unregister_driver(&pata_cswarp_driver); +} + +module_init(pata_cswarp_init); +module_exit(pata_cswarp_unregister); + +MODULE_AUTHOR("Andrzej Rogozynski"); +MODULE_DESCRIPTION("low-level driver for CSWarp PATA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c index 40baeac594a9..f0fe921a10b4 100644 --- a/drivers/ata/pata_parport/pata_parport.c +++ b/drivers/ata/pata_parport/pata_parport.c @@ -618,8 +618,9 @@ void pata_parport_unregister_driver(struct pi_protocol *pr) break; } idr_remove(&protocols, id); - mutex_unlock(&pi_mutex); driver_unregister(&pr->driver); + mutex_unlock(&pi_mutex); + } EXPORT_SYMBOL_GPL(pata_parport_unregister_driver); @@ -646,18 +647,18 @@ static ssize_t new_device_store(const struct bus_type *bus, const char *buf, siz port_wanted = -1; } + mutex_lock(&pi_mutex); drv = driver_find(protocol, &pata_parport_bus_type); if (!drv) { if (strcmp(protocol, "auto")) { pr_err("protocol %s not found\n", protocol); + mutex_unlock(&pi_mutex); return -EINVAL; } pr_wanted = NULL; } else { pr_wanted = container_of(drv, struct pi_protocol, driver); } - - mutex_lock(&pi_mutex); /* walk all parports */ idr_for_each_entry(&parport_list, parport, port_num) { if (port_num == port_wanted || port_wanted == -1) { @@ -722,7 +723,7 @@ static void pata_parport_attach(struct parport *port) int pr_num, id; mutex_lock(&pi_mutex); - id = idr_alloc(&parport_list, port, port->number, port->number, + id = idr_alloc(&parport_list, port, port->number, port->number + 1, GFP_KERNEL); if (id < 0) { mutex_unlock(&pi_mutex); diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 8e3fc713891a..aa816a1a4c54 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1185,11 +1185,14 @@ static int sata_dwc_probe(struct platform_device *ofdev) * error_handler() to execute a dummy Softreset EH session */ err = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); - if (err) + if (err) { dev_err(dev, "failed to activate host"); + goto error_out; + } /* Enable SATA Interrupts */ sata_dwc_enable_interrupts(hsdev); + return 0; error_out: diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 70b210afd291..987d8acf1486 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1490,8 +1490,10 @@ static int sata_fsl_probe(struct platform_device *ofdev) * device discovery process, invoking our port_start() handler & * error_handler() to execute a dummy Softreset EH session */ - ata_host_activate(host, irq, sata_fsl_interrupt, SATA_FSL_IRQ_FLAG, - &sata_fsl_sht); + retval = ata_host_activate(host, irq, sata_fsl_interrupt, + SATA_FSL_IRQ_FLAG, &sata_fsl_sht); + if (retval) + goto error_exit_with_cleanup; host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show; host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store; @@ -1500,7 +1502,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) host_priv->intr_coalescing.attr.mode = S_IRUGO | S_IWUSR; retval = device_create_file(host->dev, &host_priv->intr_coalescing); if (retval) - goto error_exit_with_cleanup; + goto error_exit_detach; host_priv->rx_watermark.show = fsl_sata_rx_watermark_show; host_priv->rx_watermark.store = fsl_sata_rx_watermark_store; @@ -1510,16 +1512,24 @@ static int sata_fsl_probe(struct platform_device *ofdev) retval = device_create_file(host->dev, &host_priv->rx_watermark); if (retval) { device_remove_file(&ofdev->dev, &host_priv->intr_coalescing); - goto error_exit_with_cleanup; + goto error_exit_detach; } return 0; -error_exit_with_cleanup: +error_exit_detach: + /* + * Once the host has been activated, hcr_base and host_priv are + * released by sata_fsl_host_stop(), which is called by the driver core + * through the ata_host_stop() devres action registered by + * ata_host_start(). Releasing them here as well would result in a + * double iounmap() and a use-after-free. + */ + ata_host_detach(host); - if (host) - ata_host_detach(host); + return retval; +error_exit_with_cleanup: if (hcr_base) iounmap(hcr_base); kfree(host_priv); diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c deleted file mode 100644 index 065408975f5b..000000000000 --- a/drivers/ata/sata_inic162x.c +++ /dev/null @@ -1,903 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * sata_inic162x.c - Driver for Initio 162x SATA controllers - * - * Copyright 2006 SUSE Linux Products GmbH - * Copyright 2006 Tejun Heo <teheo@novell.com> - * - * **** WARNING **** - * - * This driver never worked properly and unfortunately data corruption is - * relatively common. There isn't anyone working on the driver and there's - * no support from the vendor. Do not use this driver in any production - * environment. - * - * http://thread.gmane.org/gmane.linux.debian.devel.bugs.rc/378525/focus=54491 - * https://bugzilla.kernel.org/show_bug.cgi?id=60565 - * - * ***************** - * - * This controller is eccentric and easily locks up if something isn't - * right. Documentation is available at initio's website but it only - * documents registers (not programming model). - * - * This driver has interesting history. The first version was written - * from the documentation and a 2.4 IDE driver posted on a Taiwan - * company, which didn't use any IDMA features and couldn't handle - * LBA48. The resulting driver couldn't handle LBA48 devices either - * making it pretty useless. - * - * After a while, initio picked the driver up, renamed it to - * sata_initio162x, updated it to use IDMA for ATA DMA commands and - * posted it on their website. It only used ATA_PROT_DMA for IDMA and - * attaching both devices and issuing IDMA and !IDMA commands - * simultaneously broke it due to PIRQ masking interaction but it did - * show how to use the IDMA (ADMA + some initio specific twists) - * engine. - * - * Then, I picked up their changes again and here's the usable driver - * which uses IDMA for everything. Everything works now including - * LBA48, CD/DVD burning, suspend/resume and hotplug. There are some - * issues tho. Result Tf is not resported properly, NCQ isn't - * supported yet and CD/DVD writing works with DMA assisted PIO - * protocol (which, for native SATA devices, shouldn't cause any - * noticeable difference). - * - * Anyways, so, here's finally a working driver for inic162x. Enjoy! - * - * initio: If you guys wanna improve the driver regarding result TF - * access and other stuff, please feel free to contact me. I'll be - * happy to assist. - */ - -#include <linux/gfp.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <scsi/scsi_host.h> -#include <linux/libata.h> -#include <linux/blkdev.h> -#include <scsi/scsi_device.h> - -#define DRV_NAME "sata_inic162x" -#define DRV_VERSION "0.4" - -enum { - MMIO_BAR_PCI = 5, - MMIO_BAR_CARDBUS = 1, - - NR_PORTS = 2, - - IDMA_CPB_TBL_SIZE = 4 * 32, - - INIC_DMA_BOUNDARY = 0xffffff, - - HOST_ACTRL = 0x08, - HOST_CTL = 0x7c, - HOST_STAT = 0x7e, - HOST_IRQ_STAT = 0xbc, - HOST_IRQ_MASK = 0xbe, - - PORT_SIZE = 0x40, - - /* registers for ATA TF operation */ - PORT_TF_DATA = 0x00, - PORT_TF_FEATURE = 0x01, - PORT_TF_NSECT = 0x02, - PORT_TF_LBAL = 0x03, - PORT_TF_LBAM = 0x04, - PORT_TF_LBAH = 0x05, - PORT_TF_DEVICE = 0x06, - PORT_TF_COMMAND = 0x07, - PORT_TF_ALT_STAT = 0x08, - PORT_IRQ_STAT = 0x09, - PORT_IRQ_MASK = 0x0a, - PORT_PRD_CTL = 0x0b, - PORT_PRD_ADDR = 0x0c, - PORT_PRD_XFERLEN = 0x10, - PORT_CPB_CPBLAR = 0x18, - PORT_CPB_PTQFIFO = 0x1c, - - /* IDMA register */ - PORT_IDMA_CTL = 0x14, - PORT_IDMA_STAT = 0x16, - - PORT_RPQ_FIFO = 0x1e, - PORT_RPQ_CNT = 0x1f, - - PORT_SCR = 0x20, - - /* HOST_CTL bits */ - HCTL_LEDEN = (1 << 3), /* enable LED operation */ - HCTL_IRQOFF = (1 << 8), /* global IRQ off */ - HCTL_FTHD0 = (1 << 10), /* fifo threshold 0 */ - HCTL_FTHD1 = (1 << 11), /* fifo threshold 1*/ - HCTL_PWRDWN = (1 << 12), /* power down PHYs */ - HCTL_SOFTRST = (1 << 13), /* global reset (no phy reset) */ - HCTL_RPGSEL = (1 << 15), /* register page select */ - - HCTL_KNOWN_BITS = HCTL_IRQOFF | HCTL_PWRDWN | HCTL_SOFTRST | - HCTL_RPGSEL, - - /* HOST_IRQ_(STAT|MASK) bits */ - HIRQ_PORT0 = (1 << 0), - HIRQ_PORT1 = (1 << 1), - HIRQ_SOFT = (1 << 14), - HIRQ_GLOBAL = (1 << 15), /* STAT only */ - - /* PORT_IRQ_(STAT|MASK) bits */ - PIRQ_OFFLINE = (1 << 0), /* device unplugged */ - PIRQ_ONLINE = (1 << 1), /* device plugged */ - PIRQ_COMPLETE = (1 << 2), /* completion interrupt */ - PIRQ_FATAL = (1 << 3), /* fatal error */ - PIRQ_ATA = (1 << 4), /* ATA interrupt */ - PIRQ_REPLY = (1 << 5), /* reply FIFO not empty */ - PIRQ_PENDING = (1 << 7), /* port IRQ pending (STAT only) */ - - PIRQ_ERR = PIRQ_OFFLINE | PIRQ_ONLINE | PIRQ_FATAL, - PIRQ_MASK_DEFAULT = PIRQ_REPLY | PIRQ_ATA, - PIRQ_MASK_FREEZE = 0xff, - - /* PORT_PRD_CTL bits */ - PRD_CTL_START = (1 << 0), - PRD_CTL_WR = (1 << 3), - PRD_CTL_DMAEN = (1 << 7), /* DMA enable */ - - /* PORT_IDMA_CTL bits */ - IDMA_CTL_RST_ATA = (1 << 2), /* hardreset ATA bus */ - IDMA_CTL_RST_IDMA = (1 << 5), /* reset IDMA machinery */ - IDMA_CTL_GO = (1 << 7), /* IDMA mode go */ - IDMA_CTL_ATA_NIEN = (1 << 8), /* ATA IRQ disable */ - - /* PORT_IDMA_STAT bits */ - IDMA_STAT_PERR = (1 << 0), /* PCI ERROR MODE */ - IDMA_STAT_CPBERR = (1 << 1), /* ADMA CPB error */ - IDMA_STAT_LGCY = (1 << 3), /* ADMA legacy */ - IDMA_STAT_UIRQ = (1 << 4), /* ADMA unsolicited irq */ - IDMA_STAT_STPD = (1 << 5), /* ADMA stopped */ - IDMA_STAT_PSD = (1 << 6), /* ADMA pause */ - IDMA_STAT_DONE = (1 << 7), /* ADMA done */ - - IDMA_STAT_ERR = IDMA_STAT_PERR | IDMA_STAT_CPBERR, - - /* CPB Control Flags*/ - CPB_CTL_VALID = (1 << 0), /* CPB valid */ - CPB_CTL_QUEUED = (1 << 1), /* queued command */ - CPB_CTL_DATA = (1 << 2), /* data, rsvd in datasheet */ - CPB_CTL_IEN = (1 << 3), /* PCI interrupt enable */ - CPB_CTL_DEVDIR = (1 << 4), /* device direction control */ - - /* CPB Response Flags */ - CPB_RESP_DONE = (1 << 0), /* ATA command complete */ - CPB_RESP_REL = (1 << 1), /* ATA release */ - CPB_RESP_IGNORED = (1 << 2), /* CPB ignored */ - CPB_RESP_ATA_ERR = (1 << 3), /* ATA command error */ - CPB_RESP_SPURIOUS = (1 << 4), /* ATA spurious interrupt error */ - CPB_RESP_UNDERFLOW = (1 << 5), /* APRD deficiency length error */ - CPB_RESP_OVERFLOW = (1 << 6), /* APRD exccess length error */ - CPB_RESP_CPB_ERR = (1 << 7), /* CPB error flag */ - - /* PRD Control Flags */ - PRD_DRAIN = (1 << 1), /* ignore data excess */ - PRD_CDB = (1 << 2), /* atapi packet command pointer */ - PRD_DIRECT_INTR = (1 << 3), /* direct interrupt */ - PRD_DMA = (1 << 4), /* data transfer method */ - PRD_WRITE = (1 << 5), /* data dir, rsvd in datasheet */ - PRD_IOM = (1 << 6), /* io/memory transfer */ - PRD_END = (1 << 7), /* APRD chain end */ -}; - -/* Comman Parameter Block */ -struct inic_cpb { - u8 resp_flags; /* Response Flags */ - u8 error; /* ATA Error */ - u8 status; /* ATA Status */ - u8 ctl_flags; /* Control Flags */ - __le32 len; /* Total Transfer Length */ - __le32 prd; /* First PRD pointer */ - u8 rsvd[4]; - /* 16 bytes */ - u8 feature; /* ATA Feature */ - u8 hob_feature; /* ATA Ex. Feature */ - u8 device; /* ATA Device/Head */ - u8 mirctl; /* Mirror Control */ - u8 nsect; /* ATA Sector Count */ - u8 hob_nsect; /* ATA Ex. Sector Count */ - u8 lbal; /* ATA Sector Number */ - u8 hob_lbal; /* ATA Ex. Sector Number */ - u8 lbam; /* ATA Cylinder Low */ - u8 hob_lbam; /* ATA Ex. Cylinder Low */ - u8 lbah; /* ATA Cylinder High */ - u8 hob_lbah; /* ATA Ex. Cylinder High */ - u8 command; /* ATA Command */ - u8 ctl; /* ATA Control */ - u8 slave_error; /* Slave ATA Error */ - u8 slave_status; /* Slave ATA Status */ - /* 32 bytes */ -} __packed; - -/* Physical Region Descriptor */ -struct inic_prd { - __le32 mad; /* Physical Memory Address */ - __le16 len; /* Transfer Length */ - u8 rsvd; - u8 flags; /* Control Flags */ -} __packed; - -struct inic_pkt { - struct inic_cpb cpb; - struct inic_prd prd[LIBATA_MAX_PRD + 1]; /* + 1 for cdb */ - u8 cdb[ATAPI_CDB_LEN]; -} __packed; - -struct inic_host_priv { - void __iomem *mmio_base; - u16 cached_hctl; -}; - -struct inic_port_priv { - struct inic_pkt *pkt; - dma_addr_t pkt_dma; - u32 *cpb_tbl; - dma_addr_t cpb_tbl_dma; -}; - -static const struct scsi_host_template inic_sht = { - ATA_BASE_SHT(DRV_NAME), - .sg_tablesize = LIBATA_MAX_PRD, /* maybe it can be larger? */ - - /* - * This controller is braindamaged. dma_boundary is 0xffff like others - * but it will lock up the whole machine HARD if 65536 byte PRD entry - * is fed. Reduce maximum segment size. - */ - .dma_boundary = INIC_DMA_BOUNDARY, - .max_segment_size = 65536 - 512, -}; - -static const int scr_map[] = { - [SCR_STATUS] = 0, - [SCR_ERROR] = 1, - [SCR_CONTROL] = 2, -}; - -static void __iomem *inic_port_base(struct ata_port *ap) -{ - struct inic_host_priv *hpriv = ap->host->private_data; - - return hpriv->mmio_base + ap->port_no * PORT_SIZE; -} - -static void inic_reset_port(void __iomem *port_base) -{ - void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; - - /* stop IDMA engine */ - readw(idma_ctl); /* flush */ - msleep(1); - - /* mask IRQ and assert reset */ - writew(IDMA_CTL_RST_IDMA, idma_ctl); - readw(idma_ctl); /* flush */ - msleep(1); - - /* release reset */ - writew(0, idma_ctl); - - /* clear irq */ - writeb(0xff, port_base + PORT_IRQ_STAT); -} - -static int inic_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) -{ - void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; - - if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) - return -EINVAL; - - *val = readl(scr_addr + scr_map[sc_reg] * 4); - - /* this controller has stuck DIAG.N, ignore it */ - if (sc_reg == SCR_ERROR) - *val &= ~SERR_PHYRDY_CHG; - return 0; -} - -static int inic_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) -{ - void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; - - if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) - return -EINVAL; - - writel(val, scr_addr + scr_map[sc_reg] * 4); - return 0; -} - -static void inic_stop_idma(struct ata_port *ap) -{ - void __iomem *port_base = inic_port_base(ap); - - readb(port_base + PORT_RPQ_FIFO); - readb(port_base + PORT_RPQ_CNT); - writew(0, port_base + PORT_IDMA_CTL); -} - -static void inic_host_err_intr(struct ata_port *ap, u8 irq_stat, u16 idma_stat) -{ - struct ata_eh_info *ehi = &ap->link.eh_info; - struct inic_port_priv *pp = ap->private_data; - struct inic_cpb *cpb = &pp->pkt->cpb; - bool freeze = false; - - ata_ehi_clear_desc(ehi); - ata_ehi_push_desc(ehi, "irq_stat=0x%x idma_stat=0x%x", - irq_stat, idma_stat); - - inic_stop_idma(ap); - - if (irq_stat & (PIRQ_OFFLINE | PIRQ_ONLINE)) { - ata_ehi_push_desc(ehi, "hotplug"); - ata_ehi_hotplugged(ehi); - freeze = true; - } - - if (idma_stat & IDMA_STAT_PERR) { - ata_ehi_push_desc(ehi, "PCI error"); - freeze = true; - } - - if (idma_stat & IDMA_STAT_CPBERR) { - ata_ehi_push_desc(ehi, "CPB error"); - - if (cpb->resp_flags & CPB_RESP_IGNORED) { - __ata_ehi_push_desc(ehi, " ignored"); - ehi->err_mask |= AC_ERR_INVALID; - freeze = true; - } - - if (cpb->resp_flags & CPB_RESP_ATA_ERR) - ehi->err_mask |= AC_ERR_DEV; - - if (cpb->resp_flags & CPB_RESP_SPURIOUS) { - __ata_ehi_push_desc(ehi, " spurious-intr"); - ehi->err_mask |= AC_ERR_HSM; - freeze = true; - } - - if (cpb->resp_flags & - (CPB_RESP_UNDERFLOW | CPB_RESP_OVERFLOW)) { - __ata_ehi_push_desc(ehi, " data-over/underflow"); - ehi->err_mask |= AC_ERR_HSM; - freeze = true; - } - } - - if (freeze) - ata_port_freeze(ap); - else - ata_port_abort(ap); -} - -static void inic_host_intr(struct ata_port *ap) -{ - void __iomem *port_base = inic_port_base(ap); - struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->link.active_tag); - u8 irq_stat; - u16 idma_stat; - - /* read and clear IRQ status */ - irq_stat = readb(port_base + PORT_IRQ_STAT); - writeb(irq_stat, port_base + PORT_IRQ_STAT); - idma_stat = readw(port_base + PORT_IDMA_STAT); - - if (unlikely((irq_stat & PIRQ_ERR) || (idma_stat & IDMA_STAT_ERR))) - inic_host_err_intr(ap, irq_stat, idma_stat); - - if (unlikely(!qc)) - goto spurious; - - if (likely(idma_stat & IDMA_STAT_DONE)) { - inic_stop_idma(ap); - - /* Depending on circumstances, device error - * isn't reported by IDMA, check it explicitly. - */ - if (unlikely(readb(port_base + PORT_TF_COMMAND) & - (ATA_DF | ATA_ERR))) - qc->err_mask |= AC_ERR_DEV; - - ata_qc_complete(qc); - return; - } - - spurious: - ata_port_warn(ap, "unhandled interrupt: cmd=0x%x irq_stat=0x%x idma_stat=0x%x\n", - qc ? qc->tf.command : 0xff, irq_stat, idma_stat); -} - -static irqreturn_t inic_interrupt(int irq, void *dev_instance) -{ - struct ata_host *host = dev_instance; - struct inic_host_priv *hpriv = host->private_data; - u16 host_irq_stat; - int i, handled = 0; - - host_irq_stat = readw(hpriv->mmio_base + HOST_IRQ_STAT); - - if (unlikely(!(host_irq_stat & HIRQ_GLOBAL))) - goto out; - - spin_lock(&host->lock); - - for (i = 0; i < NR_PORTS; i++) - if (host_irq_stat & (HIRQ_PORT0 << i)) { - inic_host_intr(host->ports[i]); - handled++; - } - - spin_unlock(&host->lock); - - out: - return IRQ_RETVAL(handled); -} - -static int inic_check_atapi_dma(struct ata_queued_cmd *qc) -{ - /* For some reason ATAPI_PROT_DMA doesn't work for some - * commands including writes and other misc ops. Use PIO - * protocol instead, which BTW is driven by the DMA engine - * anyway, so it shouldn't make much difference for native - * SATA devices. - */ - if (atapi_cmd_type(qc->cdb[0]) == READ) - return 0; - return 1; -} - -static void inic_fill_sg(struct inic_prd *prd, struct ata_queued_cmd *qc) -{ - struct scatterlist *sg; - unsigned int si; - u8 flags = 0; - - if (qc->tf.flags & ATA_TFLAG_WRITE) - flags |= PRD_WRITE; - - if (ata_is_dma(qc->tf.protocol)) - flags |= PRD_DMA; - - for_each_sg(qc->sg, sg, qc->n_elem, si) { - prd->mad = cpu_to_le32(sg_dma_address(sg)); - prd->len = cpu_to_le16(sg_dma_len(sg)); - prd->flags = flags; - prd++; - } - - WARN_ON(!si); - prd[-1].flags |= PRD_END; -} - -static enum ata_completion_errors inic_qc_prep(struct ata_queued_cmd *qc) -{ - struct inic_port_priv *pp = qc->ap->private_data; - struct inic_pkt *pkt = pp->pkt; - struct inic_cpb *cpb = &pkt->cpb; - struct inic_prd *prd = pkt->prd; - bool is_atapi = ata_is_atapi(qc->tf.protocol); - bool is_data = ata_is_data(qc->tf.protocol); - unsigned int cdb_len = 0; - - if (is_atapi) - cdb_len = qc->dev->cdb_len; - - /* prepare packet, based on initio driver */ - memset(pkt, 0, sizeof(struct inic_pkt)); - - cpb->ctl_flags = CPB_CTL_VALID | CPB_CTL_IEN; - if (is_atapi || is_data) - cpb->ctl_flags |= CPB_CTL_DATA; - - cpb->len = cpu_to_le32(qc->nbytes + cdb_len); - cpb->prd = cpu_to_le32(pp->pkt_dma + offsetof(struct inic_pkt, prd)); - - cpb->device = qc->tf.device; - cpb->feature = qc->tf.feature; - cpb->nsect = qc->tf.nsect; - cpb->lbal = qc->tf.lbal; - cpb->lbam = qc->tf.lbam; - cpb->lbah = qc->tf.lbah; - - if (qc->tf.flags & ATA_TFLAG_LBA48) { - cpb->hob_feature = qc->tf.hob_feature; - cpb->hob_nsect = qc->tf.hob_nsect; - cpb->hob_lbal = qc->tf.hob_lbal; - cpb->hob_lbam = qc->tf.hob_lbam; - cpb->hob_lbah = qc->tf.hob_lbah; - } - - cpb->command = qc->tf.command; - /* don't load ctl - dunno why. it's like that in the initio driver */ - - /* setup PRD for CDB */ - if (is_atapi) { - memcpy(pkt->cdb, qc->cdb, ATAPI_CDB_LEN); - prd->mad = cpu_to_le32(pp->pkt_dma + - offsetof(struct inic_pkt, cdb)); - prd->len = cpu_to_le16(cdb_len); - prd->flags = PRD_CDB | PRD_WRITE; - if (!is_data) - prd->flags |= PRD_END; - prd++; - } - - /* setup sg table */ - if (is_data) - inic_fill_sg(prd, qc); - - pp->cpb_tbl[0] = pp->pkt_dma; - - return AC_ERR_OK; -} - -static unsigned int inic_qc_issue(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *port_base = inic_port_base(ap); - - /* fire up the ADMA engine */ - writew(HCTL_FTHD0 | HCTL_LEDEN, port_base + HOST_CTL); - writew(IDMA_CTL_GO, port_base + PORT_IDMA_CTL); - writeb(0, port_base + PORT_CPB_PTQFIFO); - - return 0; -} - -static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - void __iomem *port_base = inic_port_base(ap); - - tf->error = readb(port_base + PORT_TF_FEATURE); - tf->nsect = readb(port_base + PORT_TF_NSECT); - tf->lbal = readb(port_base + PORT_TF_LBAL); - tf->lbam = readb(port_base + PORT_TF_LBAM); - tf->lbah = readb(port_base + PORT_TF_LBAH); - tf->device = readb(port_base + PORT_TF_DEVICE); - tf->status = readb(port_base + PORT_TF_COMMAND); -} - -static void inic_qc_fill_rtf(struct ata_queued_cmd *qc) -{ - struct ata_taskfile *rtf = &qc->result_tf; - struct ata_taskfile tf; - - /* FIXME: Except for status and error, result TF access - * doesn't work. I tried reading from BAR0/2, CPB and BAR5. - * None works regardless of which command interface is used. - * For now return true iff status indicates device error. - * This means that we're reporting bogus sector for RW - * failures. Eeekk.... - */ - inic_tf_read(qc->ap, &tf); - - if (tf.status & ATA_ERR) { - rtf->status = tf.status; - rtf->error = tf.error; - } -} - -static void inic_freeze(struct ata_port *ap) -{ - void __iomem *port_base = inic_port_base(ap); - - writeb(PIRQ_MASK_FREEZE, port_base + PORT_IRQ_MASK); - writeb(0xff, port_base + PORT_IRQ_STAT); -} - -static void inic_thaw(struct ata_port *ap) -{ - void __iomem *port_base = inic_port_base(ap); - - writeb(0xff, port_base + PORT_IRQ_STAT); - writeb(PIRQ_MASK_DEFAULT, port_base + PORT_IRQ_MASK); -} - -static int inic_check_ready(struct ata_link *link) -{ - void __iomem *port_base = inic_port_base(link->ap); - - return ata_check_ready(readb(port_base + PORT_TF_COMMAND)); -} - -/* - * SRST and SControl hardreset don't give valid signature on this - * controller. Only controller specific hardreset mechanism works. - */ -static int inic_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - struct ata_port *ap = link->ap; - void __iomem *port_base = inic_port_base(ap); - void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; - const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); - int rc; - - /* hammer it into sane state */ - inic_reset_port(port_base); - - writew(IDMA_CTL_RST_ATA, idma_ctl); - readw(idma_ctl); /* flush */ - ata_msleep(ap, 1); - writew(0, idma_ctl); - - rc = sata_link_resume(link, timing, deadline); - if (rc) { - ata_link_warn(link, - "failed to resume link after reset (errno=%d)\n", - rc); - return rc; - } - - *class = ATA_DEV_NONE; - if (ata_link_online(link)) { - struct ata_taskfile tf; - - /* wait for link to become ready */ - rc = ata_wait_after_reset(link, deadline, inic_check_ready); - /* link occupied, -ENODEV too is an error */ - if (rc) { - ata_link_warn(link, - "device not ready after hardreset (errno=%d)\n", - rc); - return rc; - } - - inic_tf_read(ap, &tf); - *class = ata_port_classify(ap, &tf); - } - - return 0; -} - -static void inic_error_handler(struct ata_port *ap) - __must_hold(&ap->host->eh_mutex) -{ - void __iomem *port_base = inic_port_base(ap); - - inic_reset_port(port_base); - ata_std_error_handler(ap); -} - -static void inic_post_internal_cmd(struct ata_queued_cmd *qc) -{ - /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_EH) - inic_reset_port(inic_port_base(qc->ap)); -} - -static void init_port(struct ata_port *ap) -{ - void __iomem *port_base = inic_port_base(ap); - struct inic_port_priv *pp = ap->private_data; - - /* clear packet and CPB table */ - memset(pp->pkt, 0, sizeof(struct inic_pkt)); - memset(pp->cpb_tbl, 0, IDMA_CPB_TBL_SIZE); - - /* setup CPB lookup table addresses */ - writel(pp->cpb_tbl_dma, port_base + PORT_CPB_CPBLAR); -} - -static int inic_port_resume(struct ata_port *ap) -{ - init_port(ap); - return 0; -} - -static int inic_port_start(struct ata_port *ap) -{ - struct device *dev = ap->host->dev; - struct inic_port_priv *pp; - - /* alloc and initialize private data */ - pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); - if (!pp) - return -ENOMEM; - ap->private_data = pp; - - /* Alloc resources */ - pp->pkt = dmam_alloc_coherent(dev, sizeof(struct inic_pkt), - &pp->pkt_dma, GFP_KERNEL); - if (!pp->pkt) - return -ENOMEM; - - pp->cpb_tbl = dmam_alloc_coherent(dev, IDMA_CPB_TBL_SIZE, - &pp->cpb_tbl_dma, GFP_KERNEL); - if (!pp->cpb_tbl) - return -ENOMEM; - - init_port(ap); - - return 0; -} - -static struct ata_port_operations inic_port_ops = { - .inherits = &sata_port_ops, - - .check_atapi_dma = inic_check_atapi_dma, - .qc_prep = inic_qc_prep, - .qc_issue = inic_qc_issue, - .qc_fill_rtf = inic_qc_fill_rtf, - - .freeze = inic_freeze, - .thaw = inic_thaw, - .reset.hardreset = inic_hardreset, - .error_handler = inic_error_handler, - .post_internal_cmd = inic_post_internal_cmd, - - .scr_read = inic_scr_read, - .scr_write = inic_scr_write, - - .port_resume = inic_port_resume, - .port_start = inic_port_start, -}; - -static const struct ata_port_info inic_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &inic_port_ops -}; - -static int init_controller(void __iomem *mmio_base, u16 hctl) -{ - int i; - u16 val; - - hctl &= ~HCTL_KNOWN_BITS; - - /* Soft reset whole controller. Spec says reset duration is 3 - * PCI clocks, be generous and give it 10ms. - */ - writew(hctl | HCTL_SOFTRST, mmio_base + HOST_CTL); - readw(mmio_base + HOST_CTL); /* flush */ - - for (i = 0; i < 10; i++) { - msleep(1); - val = readw(mmio_base + HOST_CTL); - if (!(val & HCTL_SOFTRST)) - break; - } - - if (val & HCTL_SOFTRST) - return -EIO; - - /* mask all interrupts and reset ports */ - for (i = 0; i < NR_PORTS; i++) { - void __iomem *port_base = mmio_base + i * PORT_SIZE; - - writeb(0xff, port_base + PORT_IRQ_MASK); - inic_reset_port(port_base); - } - - /* port IRQ is masked now, unmask global IRQ */ - writew(hctl & ~HCTL_IRQOFF, mmio_base + HOST_CTL); - val = readw(mmio_base + HOST_IRQ_MASK); - val &= ~(HIRQ_PORT0 | HIRQ_PORT1); - writew(val, mmio_base + HOST_IRQ_MASK); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int inic_pci_device_resume(struct pci_dev *pdev) -{ - struct ata_host *host = pci_get_drvdata(pdev); - struct inic_host_priv *hpriv = host->private_data; - int rc; - - rc = ata_pci_device_do_resume(pdev); - if (rc) - return rc; - - if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - rc = init_controller(hpriv->mmio_base, hpriv->cached_hctl); - if (rc) - return rc; - } - - ata_host_resume(host); - - return 0; -} -#endif - -static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - const struct ata_port_info *ppi[] = { &inic_port_info, NULL }; - struct ata_host *host; - struct inic_host_priv *hpriv; - void __iomem * const *iomap; - int mmio_bar; - int i, rc; - - ata_print_version_once(&pdev->dev, DRV_VERSION); - - dev_alert(&pdev->dev, "inic162x support is broken with common data corruption issues and will be disabled by default, contact linux-ide@vger.kernel.org if in production use\n"); - - /* alloc host */ - host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS); - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); - if (!host || !hpriv) - return -ENOMEM; - - host->private_data = hpriv; - - /* Acquire resources and fill host. Note that PCI and cardbus - * use different BARs. - */ - rc = pcim_enable_device(pdev); - if (rc) - return rc; - - if (pci_resource_flags(pdev, MMIO_BAR_PCI) & IORESOURCE_MEM) - mmio_bar = MMIO_BAR_PCI; - else - mmio_bar = MMIO_BAR_CARDBUS; - - rc = pcim_iomap_regions(pdev, 1 << mmio_bar, DRV_NAME); - if (rc) - return rc; - host->iomap = iomap = pcim_iomap_table(pdev); - hpriv->mmio_base = iomap[mmio_bar]; - hpriv->cached_hctl = readw(hpriv->mmio_base + HOST_CTL); - - for (i = 0; i < NR_PORTS; i++) { - struct ata_port *ap = host->ports[i]; - - ata_port_pbar_desc(ap, mmio_bar, -1, "mmio"); - ata_port_pbar_desc(ap, mmio_bar, i * PORT_SIZE, "port"); - } - - /* Set dma_mask. This devices doesn't support 64bit addressing. */ - rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (rc) { - dev_err(&pdev->dev, "32-bit DMA enable failed\n"); - return rc; - } - - rc = init_controller(hpriv->mmio_base, hpriv->cached_hctl); - if (rc) { - dev_err(&pdev->dev, "failed to initialize controller\n"); - return rc; - } - - pci_set_master(pdev); - return ata_host_activate(host, pdev->irq, inic_interrupt, IRQF_SHARED, - &inic_sht); -} - -static const struct pci_device_id inic_pci_tbl[] = { - { PCI_VDEVICE(INIT, 0x1622), }, - { }, -}; - -static struct pci_driver inic_pci_driver = { - .name = DRV_NAME, - .id_table = inic_pci_tbl, -#ifdef CONFIG_PM_SLEEP - .suspend = ata_pci_device_suspend, - .resume = inic_pci_device_resume, -#endif - .probe = inic_init_one, - .remove = ata_pci_remove_one, -}; - -module_pci_driver(inic_pci_driver); - -MODULE_AUTHOR("Tejun Heo"); -MODULE_DESCRIPTION("low-level driver for Initio 162x SATA"); -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, inic_pci_tbl); -MODULE_VERSION(DRV_VERSION); diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index d260f04e6384..c408d04d9bf7 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -252,6 +252,7 @@ enum { SOC_LED_CTRL_BLINK = (1 << 0), /* Active LED blink */ SOC_LED_CTRL_ACT_PRESENCE = (1 << 2), /* Multiplex dev presence */ /* with dev activity LED */ + SOC_LED_CTRL_ENABLE = (1 << 3), /* Enable SATA LED control */ /* Shadow block registers */ SHD_BLK = 0x100, @@ -3382,12 +3383,15 @@ static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio, writel(m2, port_mmio + PHY_MODE2); } -/* TODO: use the generic LED interface to configure the SATA Presence */ -/* & Acitivy LEDs on the board */ +/* + * Enable the SoC SATA LED controller and device presence + * indication on the board's SATA LEDs. + */ static void mv_soc_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio) { - return; + writel(SOC_LED_CTRL_ENABLE | SOC_LED_CTRL_ACT_PRESENCE, + mv_hc_base(mmio, 0) + SOC_LED_CTRL); } static void mv_soc_read_preamp(struct mv_host_priv *hpriv, int idx, diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 4e7f5b2ff3f6..f38310b84f51 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -584,8 +584,12 @@ static int qs_ata_init_one(struct pci_dev *pdev, qs_host_init(host, board_idx); pci_set_master(pdev); - return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED, - &qs_ata_sht); + rc = ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED, + &qs_ata_sht); + if (rc) + qs_host_stop(host); + + return rc; } module_pci_driver(qs_ata_pci_driver); |
