From 29b8602629661391a7586503b71628faade45e30 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 18 Aug 2026 11:42:05 +0200 Subject: [PATCH 01/26] ata: ata_generic: Do not bind to devices that are not IDE controllers syzbot force-bound ata_generic to 0000:00:03.0 on a QEMU arm64 virt machine. That device is a virtio-blk-pci device holding the root file system, and it reports PCI class 0x010000, i.e. PCI_CLASS_STORAGE_SCSI. QEMU gives the virtio-blk-pci device a legacy virtio I/O BAR0 and a 4 KiB MSI-X BAR1, so both resources are non-empty, the port is not discarded, and the device control register ends up in the middle of the MSI-X table. The emulated device rejects the byte write, and arm64 reports the resulting bus error as a fatal synchronous external abort: Internal error: synchronous external abort: 0000000096000050 [#1] SMP pc : ata_sff_freeze+0x7c/0x90 drivers/ata/libata-sff.c:1606 Call trace: ata_sff_freeze+0x7c/0x90 ata_eh_freeze_port+0x34/0x5c ata_host_start+0x13c/0x228 ata_pci_sff_activate_host+0x50/0x340 ata_pci_init_one+0x19c/0x1d8 ata_pci_bmdma_init_one+0x14/0x20 ata_generic_init_one+0xc4/0x1ac local_pci_probe+0x40/0xa8 pci_device_probe+0xd8/0x288 really_probe+0xbc/0x2bc device_driver_attach+0x48/0xb4 bind_store+0x7c/0xd8 Refuse devices which neither report the IDE class nor appear in our ID table. Table entries keep binding as before, because some of the listed controllers cannot be assumed to report the IDE class. A controller which needs ata_generic but does not report the IDE class should get an ID table entry, which is what the table is for. Binding a driver to unrelated hardware requires root and is what driver_override is meant to do, so this does not fix a privilege boundary. This change only stops ata_generic from binding to a PCI device which it has no reason to believe to be an IDE controller. Reported-by: syzbot+891c7b195b408052e519@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-ide/6a82bc54.10853dc7.22f513.001b.GAE@google.com/ Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260818094205.2672967-2-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/ata_generic.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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; From abf62e9d56090d3633ce077453d7f5011fdf925f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 10 Aug 2026 16:25:02 +0300 Subject: [PATCH 02/26] ata: add where missing It appears that libahci.c, ahci.c as well as the ahci_brcm, ahci_ceva and ahci_qoriq drivers are using runtime PM operations without including . This header is somehow being indirectly provided by , which would like to drop it (none of the functions it exports need it). Signed-off-by: Vladimir Oltean Acked-by: Damien Le Moal Link: https://lore.kernel.org/r/20260810132502.460566-1-vladimir.oltean@nxp.com Signed-off-by: Niklas Cassel --- drivers/ata/ahci.c | 1 + drivers/ata/ahci_brcm.c | 1 + drivers/ata/ahci_ceva.c | 1 + drivers/ata/ahci_qoriq.c | 1 + drivers/ata/libahci.c | 1 + 5 files changed, 5 insertions(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 58f512f8952a..d1cdbf86da78 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include 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 #include #include +#include #include #include 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 #include #include +#include #include #include "ahci.h" 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 #include #include +#include #include #include "ahci.h" diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 6d72eb017b49..bea9a39daced 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "ahci.h" #include "libata.h" From d388f9657ad68eca31d86765d48133b9258b8983 Mon Sep 17 00:00:00 2001 From: Paolo Pisati Date: Wed, 26 Aug 2026 18:09:10 +0200 Subject: [PATCH 03/26] ata: pata_cswarp: Add Amiga cslab ata support Driver for the on-board IDE interface on CS-Lab Warp Expansion Card (NOTE that idemode=native has to be set in Warp Configuration) Assisted-by: Claude_Code:claude-opus-5 Signed-off-by: Paolo Pisati Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260826160911.460165-2-p.pisati@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/Kconfig | 10 +++ drivers/ata/Makefile | 1 + drivers/ata/pata_cswarp.c | 183 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 drivers/ata/pata_cswarp.c diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 28ca856ecc75..86af89a9bfce 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -1040,6 +1040,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..647e3d4b6f75 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -100,6 +100,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/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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"); From bd46a0b22933fb45f4ff009fa4a3836124df34b3 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Thu, 30 Jul 2026 11:16:29 +0800 Subject: [PATCH 04/26] ata: pata_parport: Fix use-after-free in new_device_store The function new_device_store() calls driver_find() without any protection against concurrent driver unregistration. This can lead to a use-after-free (UAF) when a driver is unloaded (via rmmod) in parallel with a new device addition via sysfs. The race window exists because driver_find() returns a pointer to the driver's private data, but does not increase its reference count. The caller is responsible for ensuring the driver remains valid, but new_device_store() did not hold any lock or reference during the lookup and subsequent use. Concurrently, pata_parport_unregister_driver() releases the pi_mutex before calling driver_unregister(), allowing new_device_store to proceed with a stale pointer after the driver has been freed. Fix this by expanding the critical section protected by pi_mutex in both functions: - In new_device_store(): acquire pi_mutex before calling driver_find(), and keep it held until all uses of the found driver pointer are completed. - In pata_parport_unregister_driver(): hold pi_mutex throughout the entire unregistration process, including the driver_unregister() call, so that no concurrent lookup can see a partially removed driver. Thread A (new_device_store) | Thread B (pata_parport_unregister_driver) driver_find("ate") | | mutex_lock(&pi_mutex) | idr_remove(&protocols, id) | mutex_unlock(&pi_mutex) | driver_unregister(&pr->driver) | (frees driver_private) /* continues using stale driv */| -> UAF! | Logs: BUG: KASAN: slab-use-after-free in driver_find+0xd0/0xd4 Read of size 8 at addr ffffff9f970dc690 by task sh/4737 Call trace: show_stack+0x14/0x1c (C) dump_stack_lvl+0x70/0x84 print_report+0xf4/0x5a4 kasan_report+0xa0/0xe4 __asan_report_load8_noabort+0x18/0x20 driver_find+0xd0/0xd4 new_device_store+0x140/0x30c [pata_parport] bus_attr_store+0x5c/0x94 sysfs_kf_write+0x1c4/0x25c ... Allocated by task 4734: kasan_save_stack+0x28/0x4c kasan_save_track+0x1c/0x34 kasan_save_alloc_info+0x3c/0x4c __kasan_kmalloc+0x98/0xac __kmalloc_cache_noprof+0x158/0x3cc bus_add_driver+0x70/0x4d8 driver_register+0xf0/0x3b0 pata_parport_register_driver+0xb8/0x1c4 [pata_parport] 0xffffffc07f5ce014 do_one_initcall+0xb8/0x36c do_init_module+0x230/0x6d4 ... Freed by task 4737: kasan_save_stack+0x28/0x4c kasan_save_track+0x1c/0x34 kasan_save_free_info+0x48/0x8c __kasan_slab_free+0x5c/0x84 kfree+0x174/0x3e0 driver_release+0x1c/0x7c kobject_put+0x178/0x47c driver_find+0x78/0xd4 new_device_store+0x140/0x30c [pata_parport] bus_attr_store+0x5c/0x94 sysfs_kf_write+0x1c4/0x25c ... Fixes: 246a1c4c6b7f ("ata: pata_parport: add driver (PARIDE replacement)") Reported-by: Shuangpeng Bai Closes: https://lore.kernel.org/lkml/20260728024015.2014674-1-shuangpeng.kernel@gmail.com/ Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/77dad1184ebd0e1e7ea541d4207568f8bc5ec01f.1785381141.git.xiaopei01@kylinos.cn Signed-off-by: Niklas Cassel --- drivers/ata/pata_parport/pata_parport.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c index 40baeac594a9..cf81a6128f55 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) { From f091e60fc986eb12ff632f682848b88da824bccb Mon Sep 17 00:00:00 2001 From: Li Jun Date: Tue, 8 Sep 2026 17:49:01 +0800 Subject: [PATCH 05/26] ata: ahci_da850: switch to DEFINE_SIMPLE_DEV_PM_OPS Replace the deprecated SIMPLE_DEV_PM_OPS macro with the recommended DEFINE_SIMPLE_DEV_PM_OPS. Use pm_ptr(&ahci_da850_pm_ops) to complete the API migration and allow the structure to be correctly dropped when CONFIG_PM_SLEEP=n. Signed-off-by: Li Jun Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260908094901.2527974-1-lijun01@kylinos.cn Signed-off-by: Niklas Cassel --- drivers/ata/ahci_da850.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 @@ static int ahci_da850_probe(struct platform_device *pdev) 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); From 5915ab523e33057224a0240e27b88c6c2798b919 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 12 Sep 2026 12:12:15 +0200 Subject: [PATCH 06/26] ata: libata-scsi: Fix ata_scsi_hotplug() reference in kernel-doc The ata_scsi_remove_dev() comment misspells its caller as ata_eh_scsi_hotplug(). The caller is ata_scsi_hotplug(). Correct the reference, which predates libata's move from drivers/scsi to drivers/ata. Fixes: 580b2102327a ("[PATCH] libata-hp: implement SCSI part of hotplug") Assisted-by: LLM Signed-off-by: Karl Mehltretter Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260912101215.86413-1-kmehltretter@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/libata-scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a7d667cfcfec..00e8878fb8a2 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -5406,7 +5406,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: From 96fdeb7e3fd8d937f8351ed90824c9d904e590a1 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 14 Sep 2026 18:47:09 -0700 Subject: [PATCH 07/26] ata: sata_inic162x: remove dangerous broken driver The sata_inic162x driver was labeled as "very experimental" in commit bb9696192826 ("libata: make it clear that sata_inic162x is experimental") due to data corruption issues, but not marked as broken. Since then, no changes have been made to it that could fix these issues. Since the driver was not marked as broken, and the message about potential data corruption is only visible in the code and dmesg, it is possible that someone could enable the driver without realizing its potential for data corruption. Remove the driver to protect users' data. Fixes: 1fd7a697a37b ("sata_inic162x: finally, driver for initio 162x SATA controllers, take #2") Cc: stable@vger.kernel.org Signed-off-by: Ethan Nelson-Moore Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915014714.41274-1-enelsonmoore@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/Kconfig | 7 - drivers/ata/Makefile | 1 - drivers/ata/sata_inic162x.c | 903 ------------------------------------ 3 files changed, 911 deletions(-) delete mode 100644 drivers/ata/sata_inic162x.c diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 86af89a9bfce..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 diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 647e3d4b6f75..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 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 - * - * **** 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 -#include -#include -#include -#include -#include -#include -#include - -#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); From c74ee9230a8d1d2f6fcbceff55f56f7dd9b9cccd Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 15 Sep 2026 10:41:27 +0200 Subject: [PATCH 08/26] ata: ahci_st: Do not ignore errors when getting the reset controls st_ahci_probe_resets() treats any error from devm_reset_control_get() as "reset control not defined" and continues with a NULL reset control: 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; } This also swallows -EPROBE_DEFER, which is returned when the reset controller providing the reset has not been probed yet. probe() then continues as if the device tree did not specify any reset, so the resets of the SATA IP are never deasserted, and st_ahci_configure_oob() and ahci_platform_init_host() access the MMIO of an IP which is still held in reset and powered down, which can result in an external abort. The resets are optional in the binding, as they are not part of its required properties, so use devm_reset_control_get_optional(), which returns NULL if the reset is not specified in the device tree, and propagate all other errors. Note that an absent reset is now indicated by a NULL reset control instead of an error, so the "reset control not defined" messages are dropped. Fixes: 76884cb2f7da ("ahci: st: Add support for ST's SATA IP") Cc: stable@vger.kernel.org Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915084127.692494-9-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/ahci_st.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 4336c8a6e208..819269a022ef 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -104,23 +104,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); } From 91e0a7452d7a81e924a499b23f30f3bf85fddbd2 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 15 Sep 2026 10:41:28 +0200 Subject: [PATCH 09/26] ata: ahci_st: Assert the power down reset in the probe() error path st_ahci_probe_resets() deasserts the "pwr-dwn" reset, but the probe() error path of st_ahci_probe() only releases the host resources, so the SATA IP is left powered up when probe() fails after st_ahci_probe_resets() has succeeded, e.g. when ahci_platform_enable_resources() fails. The reset is asserted by st_ahci_host_stop(), however ->host_stop() is only called through the ata_host_stop() devres action registered by ata_host_start(), so it does not cover any failure happening before the host has been started. Factor the assert out into st_ahci_assert_pwrdwn() and call it when either ahci_platform_enable_resources() or ahci_platform_init_host() fails. The "pwr-dwn" reset control is an exclusive one, so asserting it once more from st_ahci_host_stop() is harmless. No functional change intended for ->host_stop() and st_ahci_suspend(). Fixes: 76884cb2f7da ("ahci: st: Add support for ST's SATA IP") Cc: stable@vger.kernel.org Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915084127.692494-10-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/ahci_st.c | 51 +++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 819269a022ef..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 int st_ahci_assert_pwrdwn(struct ahci_host_priv *hpriv, + struct device *dev) +{ + struct st_ahci_drv_data *drv_data = hpriv->plat_data; + int err; + + 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; - 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"); - } + st_ahci_assert_pwrdwn(hpriv, host->dev); ahci_platform_disable_resources(hpriv); } @@ -159,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); From 70b1196740d61aabda13b2ad0e9c1dfa2ef9cfa3 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 15 Sep 2026 10:41:29 +0200 Subject: [PATCH 10/26] ata: sata_fsl: Fix use-after-free of host_priv on probe() failure sata_fsl_host_stop() releases hcr_base and host_priv: static void sata_fsl_host_stop(struct ata_host *host) { struct sata_fsl_host_priv *host_priv = host->private_data; iounmap(host_priv->hcr_base); kfree(host_priv); } ->host_stop() is called by the driver core through the ata_host_stop() devres action which ata_host_start() registers, so it is called both when the device is unbound and when probe() fails after the host has been started. The error_exit_with_cleanup label of sata_fsl_probe() releases hcr_base and host_priv as well, and it is reachable from the two device_create_file() calls which are done after the host has been activated. In that case sata_fsl_host_stop() runs on an already freed host_priv, resulting in a use-after-free and a double iounmap()/kfree(). Add a separate error label for the failures happening after the host has been activated, which only detaches the host and leaves hcr_base and host_priv to sata_fsl_host_stop(). Note that ata_host_detach() is dropped from error_exit_with_cleanup, as that label is now only reachable while host is still NULL. Fixes: 6c8ad7e8cf29 ("sata_fsl: fix UAF in sata_fsl_port_stop when rmmod sata_fsl") Cc: stable@vger.kernel.org Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915084127.692494-11-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/sata_fsl.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 70b210afd291..c6df55886fe1 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1500,7 +1500,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 +1510,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_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); + + return retval; + error_exit_with_cleanup: - - if (host) - ata_host_detach(host); - if (hcr_base) iounmap(hcr_base); kfree(host_priv); From 34a4a34431ed81d85d14f8fb8035e5a25214ad39 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 15 Sep 2026 10:41:30 +0200 Subject: [PATCH 11/26] ata: libahci: Free the IRQs when activating a multi-IRQ host fails ahci_host_activate_multi_irqs() requests one IRQ per port, but it does not free them when requesting one of them, or when registering the host, fails. The IRQs are only freed by the driver core, when it releases the devres of the device after probe() has returned, while the caller of ahci_host_activate() releases the resources of the host in its probe() error path, e.g. ahci_probe() disables the clocks, regulators, resets and PHYs of the host. An IRQ handler running in that window would access the MMIO of a host which is no longer clocked. ahci_host_activate_multi_irqs() did free the IRQs until commit 0a142b26921c ("ahci: cleanup ahci_host_activate_multi_irqs"), which removed the explicit free because devm makes it unnecessary. That is true for freeing the IRQs as such, but not for the window described above: devres is only released after probe() has returned, i.e. after the error path of the caller has released the resources of the host. Note that this window cannot be hit with the current users: the only user of AHCI_HFLAG_MULTI_MSI is the AHCI PCI driver, which does not release any resource in its probe() error path, and the ports of the host are still frozen, i.e. their interrupts are masked, when ata_host_register() fails. Free the IRQs explicitly again, like ata_host_activate() does, so that the IRQ handlers cannot run once ahci_host_activate() has failed. Fixes: 0a142b26921c ("ahci: cleanup ahci_host_activate_multi_irqs") Cc: stable@vger.kernel.org Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915084127.692494-12-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/libahci.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index bea9a39daced..1290dd540101 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2724,11 +2724,33 @@ 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]); + } + + return rc; } /** From d2f977008cbbae195df678ee1e70e5802f9bd0b8 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 15 Sep 2026 10:41:31 +0200 Subject: [PATCH 12/26] ata: libata: Do not leave ata_host_stop() registered when activation fails ata_host_start() registers ata_host_stop() as a devres action as soon as it has succeeded, which hands the release of the host resources over to devres: ->port_stop() and ->host_stop() are then called by the driver core when probe() fails. ata_host_activate() and ahci_host_activate_multi_irqs() can both fail after ata_host_start() has succeeded, e.g. if devm_request_irq() or ata_host_register() fails, and they return the error with the devres action still registered. A driver which releases the host resources in its probe() error path therefore releases them twice: once itself and once through ->host_stop(). All ahci-platform drivers are in that situation, e.g. ahci_probe() calls ahci_platform_disable_resources() while ahci_host_stop() does the same through devres. This gives refcount underflow warnings from the clk, regulator and phy cores and, for shared resources, can disable resources which are still in use by other devices. Add ata_host_undo_start(), which stops the ports and drops the devres action without calling ->host_stop(), and call it from both activation helpers when they fail. Releasing the host resources on failure is then always left to the caller, which is what all callers having a probe() error path already assume. This changes the semantics for the drivers which implement ->host_stop() while also completely lacking error handling for the activate host call. Add activate host error handling for sata_qstor and sata_fsl. For sata_fsl this also means that hcr_base and host_priv are now released when activating the host fails, which ->host_stop() did not do when ata_host_start() itself failed. Note that ata_pci_sff_activate_host() is deliberately left as is: none of its callers releases the host resources in its probe() error path, they all rely on ->host_stop() being called by devres, including ata_pci_init_one(), which releases the devres group of the host itself. Fixes: 1896b15eddb4 ("ahci_platform: perform platform exit in host_stop() hook") Cc: stable@vger.kernel.org Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915084127.692494-13-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/libahci.c | 2 + drivers/ata/libahci_platform.c | 4 ++ drivers/ata/libata-core.c | 75 +++++++++++++++++++++++++++++++--- drivers/ata/libata-sff.c | 5 +++ drivers/ata/sata_fsl.c | 6 ++- drivers/ata/sata_qstor.c | 8 +++- include/linux/libata.h | 1 + 7 files changed, 91 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 1290dd540101..95e4a18fd459 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2750,6 +2750,8 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, 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 6e072d681341..b44c0db4a86e 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 f482c0a6d7e9..197f5d6e93ce 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6127,6 +6127,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 @@ -6201,6 +6241,12 @@ static void async_port_probe(void *data, async_cookie_t cookie) * starts ports, registers @host with ATA and SCSI layers and * probe 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). * @@ -6294,6 +6340,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). * @@ -6314,27 +6364,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-sff.c b/drivers/ata/libata-sff.c index 976e4e160494..d5ef0338735c 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -2267,6 +2267,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/sata_fsl.c b/drivers/ata/sata_fsl.c index c6df55886fe1..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; 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); diff --git a/include/linux/libata.h b/include/linux/libata.h index 313e96173b19..4625b2eea294 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1147,6 +1147,7 @@ extern struct ata_host *ata_host_alloc_pinfo(struct device *dev, extern void ata_host_get(struct ata_host *host); extern void ata_host_put(struct ata_host *host); extern int ata_host_start(struct ata_host *host); +void ata_host_undo_start(struct ata_host *host); extern int ata_host_register(struct ata_host *host, const struct scsi_host_template *sht); extern int ata_host_activate(struct ata_host *host, int irq, From 52d19459ca79fef362126e472b9e9a3bfc5020b8 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 15 Sep 2026 10:41:32 +0200 Subject: [PATCH 13/26] ata: libata-core: Fix the ata_host_register() kdoc ata_host_register() does not start the ports of the host, it requires them to have been started already: /* host must have been started */ if (!(host->flags & ATA_HOST_STARTED)) { dev_err(host->dev, "BUG: trying to register unstarted host\n"); WARN_ON(1); return -EINVAL; } Fix the kdoc accordingly, and fix the grammar of the last sentence while at it. Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260915084127.692494-14-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/libata-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 197f5d6e93ce..682c58889715 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6237,9 +6237,9 @@ 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 From e137035fcd66cb454c479bb989968561bbc60341 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 17 Sep 2026 18:07:07 +0300 Subject: [PATCH 14/26] ata: sata_dwc_460ex: Don't enable interrupt if libATA core registration fails Recent 4bbc16a353a9 commit changed the timing when interrupts are enabled. However it didn't account for the libATA registration step. So it's possible that interrupts are enabled when the device is not registered with libATA core. Resolve the issue by failing the entire registration process in case of failure to register with libATA. Fixes: 4bbc16a353a9 ("ata: sata_dwc_460ex: enable SATA interrupts only after IRQ handler is registered") Signed-off-by: Nikolay Borisov Link: https://lore.kernel.org/r/20260917150707.1643330-1-nik.borisov@suse.com Signed-off-by: Niklas Cassel --- drivers/ata/sata_dwc_460ex.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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: From a51e0770b79bb7a9cc313043ae074b069ba1c0d5 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 17 Sep 2026 15:03:14 -0400 Subject: [PATCH 15/26] ata: pata_arasan_cf: use standard DMA engine API DMA engine consumers must not call the channel's callback function pointers directly. Use the standard dmaengine wrapper APIs instead. Signed-off-by: Frank Li Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260917190315.1188379-1-Frank.Li@oss.nxp.com Signed-off-by: Niklas Cassel --- drivers/ata/pata_arasan_cf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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)) { From 3bab8c7e0c6ba187a47163a1f71477213a278c48 Mon Sep 17 00:00:00 2001 From: Hui Peng Date: Mon, 21 Sep 2026 03:25:55 +0000 Subject: [PATCH 16/26] ata: libata-scsi: abort multi-sector pass-through commands when multi_count is 0 When an ATA_12, ATA_16, or ATA_32 pass-through command with a multi-sector PIO command opcode (ATA_CMD_READ_MULTI, ATA_CMD_WRITE_MULTI, ATA_CMD_READ_MULTI_EXT, ATA_CMD_WRITE_MULTI_EXT, or ATA_CMD_WRITE_MULTI_FUA_EXT) is submitted via SG_IO on a device where multiple-sector mode is not configured (dev->multi_count == 0), ata_scsi_pass_thru() currently logs a warning and still dispatches the taskfile. When the DRQ interrupt fires, ata_pio_sectors() triggers WARN_ON_ONCE(qc->dev->multi_count == 0) and computes nsect = min((qc->nbytes - qc->curbytes) / qc->sect_size, 0) = 0, failing to transfer any sectors: ata1.00: invalid multi_count 1 ignored WARNING: drivers/ata/libata-sff.c:666 at ata_pio_sectors+0x27d/0x300 Call Trace: ata_sff_hsm_move+0x211/0x22e0 __ata_sff_port_intr+0x1c8/0x520 ata_bmdma_port_intr+0xa1/0x5b0 ata_bmdma_interrupt+0x1f5/0x550 Per ACS-3 section 7.12.7.21, if IDENTIFY DEVICE word 59 bit 8 is cleared to zero (multi_count == 0) and a READ MULTIPLE or WRITE MULTIPLE command is received without a preceding successful SET MULTIPLE MODE command, the device returns command aborted (ABRT). Per the SAT specification, an ABRT error translates to the ABORTED COMMAND sense key with NO ADDITIONAL SENSE INFORMATION (0x00, 0x00). Fail multi-sector taskfile commands in ata_scsi_pass_thru() with ata_scsi_set_sense(dev, scmd, ABORTED_COMMAND, 0, 0) when dev->multi_count == 0. Fixes: 1dce589c38c3 ("libata passthru: support PIO multi commands") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Hui Peng Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260921032556.1160734-1-benquike@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/libata-scsi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 00e8878fb8a2..115b75a313bc 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3507,6 +3507,11 @@ 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, 0, 0); + return 1; + } + /* compare the passed through multi_count * with the cached multi_count of libata */ From 02178d4b697cb58cebefd9dccc56d614b1795e5f Mon Sep 17 00:00:00 2001 From: Danish Khateeb Date: Mon, 21 Sep 2026 13:24:48 -0500 Subject: [PATCH 17/26] ata: libata-sff: replace kmap_atomic() with kmap_local_page() kmap_atomic() is deprecated in favour of kmap_local_page(), as described in Documentation/mm/highmem.rst. ata_pio_xfer() and __atapi_pio_bytes() each map a single page, pass the address to ->sff_data_xfer() for one transfer and unmap it again, so the mapping never leaves the function that created it. Neither needs the pagefault_disable() and preempt_disable() implied by kmap_atomic(). ->sff_data_xfer() only moves data between the device and the mapped buffer, and both functions are reached only through ata_sff_hsm_move() with the host lock held, whether from the PIO task, an interrupt handler or EH. kmap_local_page() is fine in all of these. Assisted-by: LLM sparse Signed-off-by: Danish Khateeb Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260921182448.194307-1-danishkhateeb03@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/libata-sff.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index d5ef0338735c..a263f886abc7 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 @@ static int __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) 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; From 193f5b84a95c02456f51fe5067750ecf73d213d5 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Tue, 22 Sep 2026 11:38:01 +0800 Subject: [PATCH 18/26] ata: pata_parport: fix parport attach idr_alloc for idr_alloc() expects an inclusive start and an exclusive end, so passing port->number as both arguments produces an empty range. Because idr_alloc() only falls back to INT_MAX when end <= 0, the allocation succeeds solely for parport0 and returns -ENOSPC for every other port, which pata_parport_attach() silently ignores. As a result only parport0 gets probed and any higher-numbered parports are never attached. Use port->number + 1 as the end so the range [port->number, port->number + 1) contains exactly the desired ID. Fixes: 246a1c4c6b7f ("ata: pata_parport: add driver (PARIDE replacement)") Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/cee1a15d98cd0d55f41fd65ca9d222b68a74e7d3.1790047457.git.xiaopei01@kylinos.cn Signed-off-by: Niklas Cassel --- drivers/ata/pata_parport/pata_parport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c index cf81a6128f55..f0fe921a10b4 100644 --- a/drivers/ata/pata_parport/pata_parport.c +++ b/drivers/ata/pata_parport/pata_parport.c @@ -723,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); From 6b28e0e7a07353544de517c2c06357260637a6df Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 8 Sep 2026 14:06:00 -0500 Subject: [PATCH 19/26] Revert "ata: ahci: force 32-bit DMA for JMicron JMB582/JMB585" This reverts commit 105c42566a550e2d05fc14f763216a8765ee5d0e. The 32-bit quirk was incorrectly applied to JMicron JMicron JMB582/JMB585, but it turns out that the root cause was actually a problem with BIOS configuration of PCIe root ports, enabling a features that causes problems with 64-bit DMA. This problem has now been fixed in commit 4fde44822512 ("x86/PCI: Disable enhanced atomics on AMD NBIO 7.7 and 7.11"). Since the real root cause has now been fixed, we can revert this incorrect quirk. Cc: Arthur Husband Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20260908190600.226485-3-mario.limonciello@amd.com Signed-off-by: Niklas Cassel --- drivers/ata/ahci.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index d1cdbf86da78..4466191dd7f6 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -69,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, @@ -214,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 +945,6 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* Elkhart Lake AHCI */ 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), From cfce1dc635041d6b9dd5fad5367a331faaf644fe Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Sep 2026 11:05:43 -0700 Subject: [PATCH 20/26] ata: libata: Fix scsi_done() documentation In 2008 the SCSI core was converted to use the block layer timeout infrastructure instead of its own infrastructure (see also patch "block: unify request timeout handling"). Since then, calling scsi_done() while SCSI host recovery is in progress has no effect. Originally the REQ_ATOM_COMPLETE flag was used to prevent double completions. Today the SCMD_STATE_COMPLETE flag is used to prevent double completions. Instead of mentioning that calling scsi_done() is not allowed while host recovery is in progress, mention that it has no effect. Signed-off-by: Bart Van Assche Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/291519238580d795929d44437f3c863403d14e1f.1790359497.git.bvanassche@acm.org Signed-off-by: Niklas Cassel --- Documentation/driver-api/libata.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/libata.rst b/Documentation/driver-api/libata.rst index 28b8437f6e4f..c130b480180f 100644 --- a/Documentation/driver-api/libata.rst +++ b/Documentation/driver-api/libata.rst @@ -524,8 +524,8 @@ EH will know to handle it later. Then it calls low level libata driver's :c:func:`error_handler` callback. When the :c:func:`error_handler` callback is invoked it stops BMDMA and -completes the qc. Note that as we're currently in EH, we cannot call -scsi_done. As described in SCSI EH doc, a recovered scmd should be +completes the qc. Note that as we're currently in EH, scsi_done() has +no effect. As described in SCSI EH doc, a recovered scmd should be either retried with :c:func:`scsi_queue_insert` or finished with :c:func:`scsi_finish_command`. Here, we override ``qc->scsidone`` with :c:func:`scsi_finish_command` and calls :c:func:`ata_qc_complete`. From f658c7bd7880ab642e3531f15a5df73fc0519f5f Mon Sep 17 00:00:00 2001 From: Carlos Junior Date: Mon, 28 Sep 2026 10:28:18 -0300 Subject: [PATCH 21/26] ata: sata_mv: enable SoC SATA LED presence indication Enable the SoC SATA LED controller in mv_soc_enable_leds(). Set the LED controller enable and device presence multiplexing bits in the SoC SATA host-controller register. This enables SATA device presence indication on boards using the SoC SATA controller. Signed-off-by: Carlos Junior Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20260928133040.222155-1-cjuniorfox@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/sata_mv.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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, From 23eab9e877497397a0e2394ee9174d576816209e Mon Sep 17 00:00:00 2001 From: Kyrylo Biryuk Date: Mon, 12 Oct 2026 12:39:17 +0200 Subject: [PATCH 22/26] ata: libahci: re-enable FBS after error handling on a port multiplier ahci_do_softreset() disables FBS before resetting a device behind a port multiplier, as required by AHCI 1.2 section 9.3.9, and re-enables it only when that softreset succeeds. If it fails, for example because a hot-plugged drive is still spinning up ("softreset failed (device not ready)"), EH escalates to a hardreset of the PMP link, and nothing turns FBS back on: only ahci_pmp_attach() does, which runs at the next boot. The port keeps working with command-based switching until then. Seen on a Marvell 88SE9235 (1b4b:9235) with Marvell 88SM9705 port multipliers (1b4b:9705, in a Synology DX1222 expansion unit): after hot-plugging a drive, the kernel logged "FBS is disabled" followed by "softreset failed (device not ready)" and a PMP hardreset, and never "FBS is enabled" again (twice, on two different ports). At the end of ahci_error_handler(), re-enable FBS if a PMP is still attached and FBS is supported but disabled. Fixes: 89dafa20f3da ("ahci: disabled FBS prior to issuing software reset") Assisted-by: LLM Signed-off-by: Kyrylo Biryuk Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20261012103917.389-1-illofspeed@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/libahci.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 95e4a18fd459..d1afd8c59e18 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2221,6 +2221,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); } From 4153718a215eeb5bab3df50417bbd9602e84658c Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 13 Oct 2026 21:39:16 +0200 Subject: [PATCH 23/26] ata: libata-core: Fix NO_FUA quirks shadowed by earlier quirk entries ata_dev_quirks() uses the first entry in __ata_dev_quirks[] that matches the device and ignores all later entries. Commit 38d431229bfb ("ata: libata: exclude FUA support for known buggy drives") appended three ATA_QUIRK_NO_FUA entries to the end of the table, with model patterns meant to cover whole product lines. Earlier entries already match some or all of these drives, so NO_FUA is not always applied: - "INTEL*SSDSC2CT*" never takes effect. Every model it matches also matches the earlier "INTEL*SSD*" entry, so these drives only get ATA_QUIRK_ZERO_AFTER_TRIM. - "WDC*WD2500J*" does not take effect for "WDC WD2500JD-*" drives, which match the earlier ATA_QUIRK_WD_BROKEN_LPM entry. - "OCZ-VERTEX*" does not take effect for "OCZ-VERTEX" drives with firmware "1.30", which match the earlier ATA_QUIRK_BROKEN_HPA entry. Move the Intel entry in front of "INTEL*SSD*" and keep ATA_QUIRK_ZERO_AFTER_TRIM in it, so these drives stay on the ZERO_AFTER_TRIM whitelist. Add ATA_QUIRK_NO_FUA to the WD2500JD and OCZ Vertex 1.30 entries. The quirks of all other drives are unchanged. libata only uses FUA when it is enabled with libata.fua=1, so systems using the default setting are not affected. Fixes: 38d431229bfb ("ata: libata: exclude FUA support for known buggy drives") Assisted-by: LLM Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20261013193915.174460-2-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/libata-core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 682c58889715..00f86abc6bae 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4322,7 +4322,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 }, @@ -4458,6 +4459,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 }, @@ -4478,7 +4481,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 }, @@ -4503,7 +4507,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 */ { } From 014c29a8e3bdcaad8c6fdfcf1af2fdbbab3fc323 Mon Sep 17 00:00:00 2001 From: zhoumin Date: Thu, 15 Oct 2026 17:41:02 +0800 Subject: [PATCH 24/26] ata: ahci: preserve preset port masks ahci_get_port_mask() returns zero for absent or unmatched parameters, overwriting the mask set by the Marvell PATA quirk. Update the mask in place only when parsing a global value or a matching device entry. Leave the mask unchanged on parsing errors. Fixes: 70c1595c181c ("ata: ahci: Allow ignoring the external/hotplug capability of ports") Suggested-by: Niklas Cassel Signed-off-by: zhoumin Link: https://lore.kernel.org/r/tencent_26064A115A53E4C6D00433C89E1894E2D808@qq.com Signed-off-by: Niklas Cassel --- drivers/ata/ahci.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 4466191dd7f6..3c38ee3314ee 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1625,34 +1625,27 @@ MODULE_PARM_DESC(mask_port_ext, "where 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; } @@ -1687,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, @@ -1719,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); } From ea5a5af9159cc89e8bcb7a95336e02c8b70aa3d2 Mon Sep 17 00:00:00 2001 From: Rand Deeb Date: Thu, 15 Oct 2026 22:49:35 +0300 Subject: [PATCH 25/26] ata: libata-core: Ignore forced transfer modes the host does not support The libata.force parameter can force a transfer mode (pio0-6, mwdma0-4, udma0-7) for a port or device. ata_force_xfermask() applies it in ata_do_set_mode() right after ata_dev_xfermask() has limited the device masks to what the host supports. It replaces those masks with the forced mode without checking the host masks, so a mode the controller does not support is passed to the driver's ->set_piomode() or ->set_dmamode(). Several PATA drivers rely on the host masks to keep the mode number in range when indexing their timing tables. ata_piix, pata_efar, pata_it8213, pata_oldpiix, pata_radisys and pata_rdc declare ATA_PIO4 and ATA_MWDMA12_ONLY, index a five-entry PIO timing table with pio_mode - XFER_PIO_0, and index a three-entry needed_pio[] table with dma_mode - XFER_MW_DMA_0. Booting with libata.force=pio5, pio6, mwdma3 or mwdma4 makes them read past those tables, use the values read as further indices into the timing table, and write the result to the controller's timing registers. Make ata_force_xfermask() skip, with a warning, a forced transfer mode that is not in the port's PIO, MWDMA or UDMA mask, and keep looking for an earlier matching libata.force entry, as it already does for entries that do not apply to the device. With libata.force=pio4,1.0:pio5 on a PIO4 host, device 1.0 now gets pio4 instead of pio5. Forcing a mode the host supports still works as before, including a mode that ata_dev_xfermask() removed for the device or cable. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 332673257056 ("libata: implement libata.force module parameter") Signed-off-by: Rand Deeb Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20261015194935.1497-1-rand.sec96@gmail.com Signed-off-by: Niklas Cassel --- drivers/ata/libata-core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 00f86abc6bae..dadcaf9ff462 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) { From 69be770908ae57354a41d7e1cc99ab46278d2e86 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 20 Oct 2026 12:11:08 +0200 Subject: [PATCH 26/26] ata: libata-sff: fix race between IRQ deferral and ata_sff_flush_pio_task() Commit 4a2e5409472e ("ata: libata-sff: don't busy-wait for PIO data-in command completion") allowed the interrupt handler to queue ap->sff_pio_task from ata_sff_hsm_move() when a PIO data-in command is still busy after its final data block. ata_sff_flush_pio_task(), however, was written under the assumption that cancel_delayed_work_sync() is enough to prevent sff_pio_task from being queued again before the HSM state is reset to HSM_ST_IDLE and sff_pio_task_link is cleared. The flush runs early during error handling, at a point where the timed out command is still active, the port is not frozen yet, and interrupts are still being serviced. If the final data-block interrupt of a PIO data-in command is serviced in the window between cancel_delayed_work_sync() and the reset of hsm_task_state, the interrupt handler queues sff_pio_task after the cancel has completed. When that work item then executes on ata_sff_wq after the flush cleared ap->sff_pio_task_link, ata_sff_pio_task() hits BUG_ON(ap->sff_pio_task_link == NULL) while holding the host lock with interrupts disabled; alternatively it proceeds on a stale link while error handling owns the port. Fix this by resetting hsm_task_state to HSM_ST_IDLE before cancelling the work item. Once the state is IDLE, __ata_sff_port_intr() can no longer enter ata_sff_hsm_move(), so the interrupt handler can no longer queue the pio task, and cancel_delayed_work_sync() drains whatever was queued before the reset. Also make ata_sff_pio_task() bail out quietly when the HSM state was marked idle by the flush, both at entry and after re-acquiring the port lock following ata_msleep(), since a work item can already be running when the flush resets the state. Reported-by: sashiko-bot Link: https://lore.kernel.org/linux-ide/sashiko-outbox-177477@kernel.org/ Fixes: 4a2e5409472e ("ata: libata-sff: don't busy-wait for PIO data-in command completion") Cc: stable@vger.kernel.org Reviewed-by: Damien Le Moal Link: https://lore.kernel.org/r/20261020101107.1449635-2-cassel@kernel.org Signed-off-by: Niklas Cassel --- drivers/ata/libata-sff.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index a263f886abc7..cf2b49457456 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -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 @@ static void ata_sff_pio_task(struct work_struct *work) 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);