Index: meson/arch/amd64/meson.build
===================================================================
--- meson/arch/amd64/meson.build	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ meson/arch/amd64/meson.build	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -77,4 +77,5 @@
 	'drv/block/ahci',
 	'drv/block/isa-ide',
+	'drv/block/pci-ide',
 	'drv/block/usbmast',
 	'drv/block/virtio-blk',
Index: meson/arch/ia32/meson.build
===================================================================
--- meson/arch/ia32/meson.build	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ meson/arch/ia32/meson.build	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -83,4 +83,5 @@
 	'drv/block/ahci',
 	'drv/block/isa-ide',
+	'drv/block/pci-ide',
 	'drv/block/usbmast',
 	'drv/block/virtio-blk',
Index: uspace/drv/block/isa-ide/isa-ide.c
===================================================================
--- uspace/drv/block/isa-ide/isa-ide.c	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/drv/block/isa-ide/isa-ide.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -34,6 +34,4 @@
  * @file
  * @brief ISA IDE driver
- *
- * The driver services a single IDE channel.
  */
 
Index: uspace/drv/block/isa-ide/main.c
===================================================================
--- uspace/drv/block/isa-ide/main.c	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/drv/block/isa-ide/main.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -66,8 +66,9 @@
 };
 
-static errno_t ata_get_res(ddf_dev_t *dev, isa_ide_hwres_t *ata_res)
+static errno_t isa_ide_get_res(ddf_dev_t *dev, isa_ide_hwres_t *res)
 {
 	async_sess_t *parent_sess;
 	hw_res_list_parsed_t hw_res;
+	hw_res_flags_t flags;
 	errno_t rc;
 
@@ -75,4 +76,21 @@
 	if (parent_sess == NULL)
 		return ENOMEM;
+
+	rc = hw_res_get_flags(parent_sess, &flags);
+	if (rc != EOK)
+		return rc;
+
+	/*
+	 * Prevent attaching to the legacy ISA IDE register block
+	 * on a system with PCI not to conflict with PCI IDE.
+	 *
+	 * XXX This is a simplification. If we had a PCI-based system without
+	 * PCI-IDE or with PCI-IDE disabled and would still like to use
+	 * an ISA IDE controller, this would prevent us from doing so.
+	 */
+	if (flags & hwf_isa_bridge) {
+		ddf_msg(LVL_NOTE, "Will not attach to PCI/ISA bridge.");
+		return EIO;
+	}
 
 	hw_res_list_parsed_init(&hw_res);
@@ -92,8 +110,8 @@
 	addr_range_t *cmd2_rng = &hw_res.io_ranges.ranges[2];
 	addr_range_t *ctl2_rng = &hw_res.io_ranges.ranges[3];
-	ata_res->cmd1 = RNGABS(*cmd1_rng);
-	ata_res->ctl1 = RNGABS(*ctl1_rng);
-	ata_res->cmd2 = RNGABS(*cmd2_rng);
-	ata_res->ctl2 = RNGABS(*ctl2_rng);
+	res->cmd1 = RNGABS(*cmd1_rng);
+	res->ctl1 = RNGABS(*ctl1_rng);
+	res->cmd2 = RNGABS(*cmd2_rng);
+	res->ctl2 = RNGABS(*ctl2_rng);
 
 	if (RNGSZ(*ctl1_rng) < sizeof(ata_ctl_t)) {
@@ -119,13 +137,13 @@
 	/* IRQ */
 	if (hw_res.irqs.count > 0) {
-		ata_res->irq1 = hw_res.irqs.irqs[0];
+		res->irq1 = hw_res.irqs.irqs[0];
 	} else {
-		ata_res->irq1 = -1;
+		res->irq1 = -1;
 	}
 
 	if (hw_res.irqs.count > 1) {
-		ata_res->irq2 = hw_res.irqs.irqs[1];
+		res->irq2 = hw_res.irqs.irqs[1];
 	} else {
-		ata_res->irq2 = -1;
+		res->irq2 = -1;
 	}
 
@@ -147,5 +165,5 @@
 	errno_t rc;
 
-	rc = ata_get_res(dev, &res);
+	rc = isa_ide_get_res(dev, &res);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
Index: uspace/drv/block/pci-ide/doc/doxygroups.h
===================================================================
--- uspace/drv/block/pci-ide/doc/doxygroups.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/doc/doxygroups.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,4 @@
+/** @addtogroup pci-ide pci-ide
+ * @brief PCI IDE driver
+ * @ingroup drvs
+ */
Index: uspace/drv/block/pci-ide/main.c
===================================================================
--- uspace/drv/block/pci-ide/main.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/main.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2024 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pci-ide
+ * @{
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <ddf/driver.h>
+#include <ddf/log.h>
+#include <device/hw_res_parsed.h>
+
+#include "pci-ide.h"
+#include "pci-ide_hw.h"
+#include "main.h"
+
+static errno_t pci_ide_dev_add(ddf_dev_t *dev);
+static errno_t pci_ide_dev_remove(ddf_dev_t *dev);
+static errno_t pci_ide_dev_gone(ddf_dev_t *dev);
+static errno_t pci_ide_fun_online(ddf_fun_t *fun);
+static errno_t pci_ide_fun_offline(ddf_fun_t *fun);
+
+static void pci_ide_connection(ipc_call_t *, void *);
+
+static driver_ops_t driver_ops = {
+	.dev_add = &pci_ide_dev_add,
+	.dev_remove = &pci_ide_dev_remove,
+	.dev_gone = &pci_ide_dev_gone,
+	.fun_online = &pci_ide_fun_online,
+	.fun_offline = &pci_ide_fun_offline
+};
+
+static driver_t pci_ide_driver = {
+	.name = NAME,
+	.driver_ops = &driver_ops
+};
+
+static errno_t pci_ide_get_res(ddf_dev_t *dev, pci_ide_hwres_t *res)
+{
+	async_sess_t *parent_sess;
+	hw_res_list_parsed_t hw_res;
+	errno_t rc;
+
+	parent_sess = ddf_dev_parent_sess_get(dev);
+	if (parent_sess == NULL)
+		return ENOMEM;
+
+	hw_res_list_parsed_init(&hw_res);
+	rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
+	if (rc != EOK)
+		return rc;
+
+	if (hw_res.io_ranges.count != 1) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	/* Legacty ISA I/O ranges are fixed */
+
+	res->cmd1 = pci_ide_ata_cmd_p;
+	res->ctl1 = pci_ide_ata_ctl_p;
+	res->cmd2 = pci_ide_ata_cmd_s;
+	res->ctl2 = pci_ide_ata_ctl_s;
+
+	/* PCI I/O range */
+	addr_range_t *bmregs_rng = &hw_res.io_ranges.ranges[0];
+	res->bmregs = RNGABS(*bmregs_rng);
+
+	ddf_msg(LVL_NOTE, "sizes: %zu", RNGSZ(*bmregs_rng));
+
+	if (RNGSZ(*bmregs_rng) < sizeof(pci_ide_regs_t)) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	/* IRQ */
+	if (hw_res.irqs.count > 0) {
+		res->irq1 = hw_res.irqs.irqs[0];
+	} else {
+		res->irq1 = -1;
+	}
+
+	if (hw_res.irqs.count > 1) {
+		res->irq2 = hw_res.irqs.irqs[1];
+	} else {
+		res->irq2 = -1;
+	}
+
+	return EOK;
+error:
+	hw_res_list_parsed_clean(&hw_res);
+	return rc;
+}
+
+/** Add new device
+ *
+ * @param  dev New device
+ * @return     EOK on success or an error code.
+ */
+static errno_t pci_ide_dev_add(ddf_dev_t *dev)
+{
+	pci_ide_ctrl_t *ctrl;
+	pci_ide_hwres_t res;
+	errno_t rc;
+
+	rc = pci_ide_get_res(dev, &res);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
+		return EINVAL;
+	}
+
+	ctrl = ddf_dev_data_alloc(dev, sizeof(pci_ide_ctrl_t));
+	if (ctrl == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating soft state.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ctrl->dev = dev;
+
+	rc = pci_ide_channel_init(ctrl, &ctrl->channel[0], 0, &res);
+	if (rc == ENOENT)
+		goto error;
+
+	rc = pci_ide_channel_init(ctrl, &ctrl->channel[1], 1, &res);
+	if (rc == ENOENT)
+		goto error;
+
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed initializing ATA controller.");
+		rc = EIO;
+		goto error;
+	}
+
+	return EOK;
+error:
+	return rc;
+}
+
+static char *pci_ide_fun_name(pci_ide_channel_t *chan, unsigned idx)
+{
+	char *fun_name;
+
+	if (asprintf(&fun_name, "c%ud%u", chan->chan_id, idx) < 0)
+		return NULL;
+
+	return fun_name;
+}
+
+errno_t pci_ide_fun_create(pci_ide_channel_t *chan, unsigned idx, void *charg)
+{
+	errno_t rc;
+	char *fun_name = NULL;
+	ddf_fun_t *fun = NULL;
+	pci_ide_fun_t *ifun = NULL;
+	bool bound = false;
+
+	fun_name = pci_ide_fun_name(chan, idx);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	fun = ddf_fun_create(chan->ctrl->dev, fun_exposed, fun_name);
+	if (fun == NULL) {
+		ddf_msg(LVL_ERROR, "Failed creating DDF function.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/* Allocate soft state */
+	ifun = ddf_fun_data_alloc(fun, sizeof(pci_ide_fun_t));
+	if (ifun == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating softstate.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ifun->fun = fun;
+	ifun->charg = charg;
+
+	/* Set up a connection handler. */
+	ddf_fun_set_conn_handler(fun, pci_ide_connection);
+
+	rc = ddf_fun_bind(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
+		    fun_name, str_error(rc));
+		goto error;
+	}
+
+	bound = true;
+
+	rc = ddf_fun_add_to_category(fun, "disk");
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed adding function %s to "
+		    "category 'disk': %s", fun_name, str_error(rc));
+		goto error;
+	}
+
+	free(fun_name);
+	return EOK;
+error:
+	if (bound)
+		ddf_fun_unbind(fun);
+	if (fun != NULL)
+		ddf_fun_destroy(fun);
+	if (fun_name != NULL)
+		free(fun_name);
+
+	return rc;
+}
+
+errno_t pci_ide_fun_remove(pci_ide_channel_t *chan, unsigned idx)
+{
+	errno_t rc;
+	char *fun_name;
+	pci_ide_fun_t *ifun = chan->fun[idx];
+
+	fun_name = pci_ide_fun_name(chan, idx);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "pci_ide_fun_remove(%p, '%s')", ifun, fun_name);
+	rc = ddf_fun_offline(ifun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error offlining function '%s'.", fun_name);
+		goto error;
+	}
+
+	rc = ddf_fun_unbind(ifun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
+		goto error;
+	}
+
+	ddf_fun_destroy(ifun->fun);
+	free(fun_name);
+	return EOK;
+error:
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+errno_t pci_ide_fun_unbind(pci_ide_channel_t *chan, unsigned idx)
+{
+	errno_t rc;
+	char *fun_name;
+	pci_ide_fun_t *ifun = chan->fun[idx];
+
+	fun_name = pci_ide_fun_name(chan, idx);
+	if (fun_name == NULL) {
+		ddf_msg(LVL_ERROR, "Out of memory.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "pci_ide_fun_unbind(%p, '%s')", ifun, fun_name);
+	rc = ddf_fun_unbind(ifun->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
+		goto error;
+	}
+
+	ddf_fun_destroy(ifun->fun);
+	free(fun_name);
+	return EOK;
+error:
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+static errno_t pci_ide_dev_remove(ddf_dev_t *dev)
+{
+	pci_ide_ctrl_t *ctrl = (pci_ide_ctrl_t *)ddf_dev_data_get(dev);
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "pci_ide_dev_remove(%p)", dev);
+
+	rc = pci_ide_channel_fini(&ctrl->channel[0]);
+	if (rc != EOK)
+		return rc;
+
+	rc = pci_ide_channel_fini(&ctrl->channel[1]);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static errno_t pci_ide_dev_gone(ddf_dev_t *dev)
+{
+	pci_ide_ctrl_t *ctrl = (pci_ide_ctrl_t *)ddf_dev_data_get(dev);
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "pci_ide_dev_gone(%p)", dev);
+
+	rc = pci_ide_channel_fini(&ctrl->channel[0]);
+	if (rc != EOK)
+		return rc;
+
+	rc = pci_ide_channel_fini(&ctrl->channel[1]);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static errno_t pci_ide_fun_online(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "pci_ide_fun_online()");
+	return ddf_fun_online(fun);
+}
+
+static errno_t pci_ide_fun_offline(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "pci_ide_fun_offline()");
+	return ddf_fun_offline(fun);
+}
+
+static void pci_ide_connection(ipc_call_t *icall, void *arg)
+{
+	pci_ide_fun_t *ifun;
+
+	ifun = (pci_ide_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
+	ata_connection(icall, ifun->charg);
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS PCI IDE device driver\n");
+	ddf_log_init(NAME);
+	return ddf_driver_main(&pci_ide_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/pci-ide/main.h
===================================================================
--- uspace/drv/block/pci-ide/main.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/main.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2024 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pci-ide
+ * @{
+ */
+/** @file PCI IDE driver main module
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "pci-ide.h"
+
+extern errno_t pci_ide_fun_create(pci_ide_channel_t *, unsigned, void *);
+extern errno_t pci_ide_fun_remove(pci_ide_channel_t *, unsigned);
+extern errno_t pci_ide_fun_unbind(pci_ide_channel_t *, unsigned);
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/block/pci-ide/meson.build
===================================================================
--- uspace/drv/block/pci-ide/meson.build	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/meson.build	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,30 @@
+#
+# Copyright (c) 2024 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+deps = [ 'ata', 'scsi' ]
+src = files('pci-ide.c', 'main.c')
Index: uspace/drv/block/pci-ide/pci-ide.c
===================================================================
--- uspace/drv/block/pci-ide/pci-ide.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/pci-ide.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2024 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pci-ide
+ * @{
+ */
+
+/**
+ * @file
+ * @brief PCI IDE driver
+ */
+
+#include <ddi.h>
+#include <ddf/interrupt.h>
+#include <ddf/log.h>
+#include <async.h>
+#include <as.h>
+#include <fibril_synch.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <str.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "pci-ide.h"
+#include "main.h"
+
+static errno_t pci_ide_init_io(pci_ide_channel_t *);
+static void pci_ide_fini_io(pci_ide_channel_t *);
+static errno_t pci_ide_init_irq(pci_ide_channel_t *);
+static void pci_ide_fini_irq(pci_ide_channel_t *);
+static void pci_ide_irq_handler(ipc_call_t *, void *);
+
+static void pci_ide_write_data_16(void *, uint16_t *, size_t);
+static void pci_ide_read_data_16(void *, uint16_t *, size_t);
+static void pci_ide_write_cmd_8(void *, uint16_t, uint8_t);
+static uint8_t pci_ide_read_cmd_8(void *, uint16_t);
+static void pci_ide_write_ctl_8(void *, uint16_t, uint8_t);
+static uint8_t pci_ide_read_ctl_8(void *, uint16_t);
+static errno_t pci_ide_irq_enable(void *);
+static errno_t pci_ide_irq_disable(void *);
+static errno_t pci_ide_add_device(void *, unsigned, void *);
+static errno_t pci_ide_remove_device(void *, unsigned);
+static void pci_ide_msg_debug(void *, char *);
+static void pci_ide_msg_note(void *, char *);
+static void pci_ide_msg_warn(void *, char *);
+static void pci_ide_msg_error(void *, char *);
+
+static const irq_pio_range_t pci_ide_irq_ranges[] = {
+	{
+		.base = 0,
+		.size = sizeof(ata_cmd_t)
+	}
+};
+
+/** IDE interrupt pseudo code. */
+static const irq_cmd_t pci_ide_irq_cmds[] = {
+	{
+		.cmd = CMD_PIO_READ_8,
+		.addr = NULL,  /* will be patched in run-time */
+		.dstarg = 1
+	},
+	{
+		.cmd = CMD_ACCEPT
+	}
+};
+
+/** Initialize PCI IDE channel. */
+errno_t pci_ide_channel_init(pci_ide_ctrl_t *ctrl, pci_ide_channel_t *chan,
+    unsigned chan_id, pci_ide_hwres_t *res)
+{
+	errno_t rc;
+	bool irq_inited = false;
+	ata_params_t params;
+
+	ddf_msg(LVL_DEBUG, "pci_ide_ctrl_init()");
+
+	chan->ctrl = ctrl;
+	chan->chan_id = chan_id;
+	fibril_mutex_initialize(&chan->lock);
+	if (chan_id == 0) {
+		chan->cmd_physical = res->cmd1;
+		chan->ctl_physical = res->ctl1;
+		chan->irq = res->irq1;
+	} else {
+		chan->cmd_physical = res->cmd2;
+		chan->ctl_physical = res->ctl2;
+		chan->irq = res->irq2;
+	}
+
+	ddf_msg(LVL_NOTE, "I/O address %p/%p", (void *) chan->cmd_physical,
+	    (void *) chan->ctl_physical);
+
+	ddf_msg(LVL_DEBUG, "Init I/O");
+	rc = pci_ide_init_io(chan);
+	if (rc != EOK)
+		return rc;
+
+	ddf_msg(LVL_DEBUG, "Init IRQ");
+	rc = pci_ide_init_irq(chan);
+	if (rc != EOK) {
+		ddf_msg(LVL_NOTE, "init IRQ failed");
+		return rc;
+	}
+
+	irq_inited = true;
+
+	ddf_msg(LVL_DEBUG, "pci_ide_ctrl_init(): Initialize IDE channel");
+
+	params.arg = (void *)chan;
+	params.have_irq = (chan->irq >= 0) ? true : false;
+	params.write_data_16 = pci_ide_write_data_16;
+	params.read_data_16 = pci_ide_read_data_16;
+	params.write_cmd_8 = pci_ide_write_cmd_8;
+	params.read_cmd_8 = pci_ide_read_cmd_8;
+	params.write_ctl_8 = pci_ide_write_ctl_8;
+	params.read_ctl_8 = pci_ide_read_ctl_8;
+	params.irq_enable = pci_ide_irq_enable;
+	params.irq_disable = pci_ide_irq_disable;
+	params.add_device = pci_ide_add_device;
+	params.remove_device = pci_ide_remove_device;
+	params.msg_debug = pci_ide_msg_debug;
+	params.msg_note = pci_ide_msg_note;
+	params.msg_warn = pci_ide_msg_warn;
+	params.msg_error = pci_ide_msg_error;
+
+	rc = ata_channel_create(&params, &chan->channel);
+	if (rc != EOK)
+		goto error;
+
+	rc = ata_channel_initialize(chan->channel);
+	if (rc != EOK)
+		goto error;
+
+	ddf_msg(LVL_DEBUG, "pci_ide_ctrl_init: DONE");
+	return EOK;
+error:
+	if (irq_inited)
+		pci_ide_fini_irq(chan);
+	pci_ide_fini_io(chan);
+	return rc;
+}
+
+/** Finalize PCI IDE channel. */
+errno_t pci_ide_channel_fini(pci_ide_channel_t *chan)
+{
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, ": pci_ide_ctrl_remove()");
+
+	fibril_mutex_lock(&chan->lock);
+
+	rc = ata_channel_destroy(chan->channel);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&chan->lock);
+		return rc;
+	}
+
+	pci_ide_fini_irq(chan);
+	pci_ide_fini_io(chan);
+	fibril_mutex_unlock(&chan->lock);
+
+	return EOK;
+}
+
+/** Enable device I/O. */
+static errno_t pci_ide_init_io(pci_ide_channel_t *chan)
+{
+	errno_t rc;
+	void *vaddr;
+
+	rc = pio_enable((void *) chan->cmd_physical, sizeof(ata_cmd_t), &vaddr);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
+		return rc;
+	}
+
+	chan->cmd = vaddr;
+
+	rc = pio_enable((void *) chan->ctl_physical, sizeof(ata_ctl_t), &vaddr);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
+		return rc;
+	}
+
+	chan->ctl = vaddr;
+	return EOK;
+}
+
+/** Clean up device I/O. */
+static void pci_ide_fini_io(pci_ide_channel_t *chan)
+{
+	(void) chan;
+	/* XXX TODO */
+}
+
+/** Initialize IRQ. */
+static errno_t pci_ide_init_irq(pci_ide_channel_t *chan)
+{
+	irq_code_t irq_code;
+	irq_pio_range_t *ranges;
+	irq_cmd_t *cmds;
+	errno_t rc;
+
+	if (chan->irq < 0)
+		return EOK;
+
+	ranges = malloc(sizeof(pci_ide_irq_ranges));
+	if (ranges == NULL)
+		return ENOMEM;
+
+	cmds = malloc(sizeof(pci_ide_irq_cmds));
+	if (cmds == NULL) {
+		free(cmds);
+		return ENOMEM;
+	}
+
+	memcpy(ranges, &pci_ide_irq_ranges, sizeof(pci_ide_irq_ranges));
+	ranges[0].base = chan->cmd_physical;
+	memcpy(cmds, &pci_ide_irq_cmds, sizeof(pci_ide_irq_cmds));
+	cmds[0].addr = &chan->cmd->status;
+
+	irq_code.rangecount = sizeof(pci_ide_irq_ranges) / sizeof(irq_pio_range_t);
+	irq_code.ranges = ranges;
+	irq_code.cmdcount = sizeof(pci_ide_irq_cmds) / sizeof(irq_cmd_t);
+	irq_code.cmds = cmds;
+
+	ddf_msg(LVL_NOTE, "IRQ %d", chan->irq);
+	rc = register_interrupt_handler(chan->ctrl->dev, chan->irq,
+	    pci_ide_irq_handler, (void *)chan, &irq_code, &chan->ihandle);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error registering IRQ.");
+		goto error;
+	}
+
+	ddf_msg(LVL_DEBUG, "Interrupt handler registered");
+	free(ranges);
+	free(cmds);
+	return EOK;
+error:
+	free(ranges);
+	free(cmds);
+	return rc;
+}
+
+/** Clean up IRQ. */
+static void pci_ide_fini_irq(pci_ide_channel_t *chan)
+{
+	errno_t rc;
+	async_sess_t *parent_sess;
+
+	parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
+
+	rc = hw_res_disable_interrupt(parent_sess, chan->irq);
+	if (rc != EOK)
+		ddf_msg(LVL_ERROR, "Error disabling IRQ.");
+
+	(void) unregister_interrupt_handler(chan->ctrl->dev, chan->ihandle);
+}
+
+/** Interrupt handler.
+ *
+ * @param call Call data
+ * @param arg Argument (pci_ide_channel_t *)
+ */
+static void pci_ide_irq_handler(ipc_call_t *call, void *arg)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	uint8_t status;
+	async_sess_t *parent_sess;
+
+	status = ipc_get_arg1(call);
+	ata_channel_irq(chan->channel, status);
+
+	parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
+	hw_res_clear_interrupt(parent_sess, chan->irq);
+}
+
+/** Write the data register callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param data Data
+ * @param nwords Number of words to write
+ */
+static void pci_ide_write_data_16(void *arg, uint16_t *data, size_t nwords)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	size_t i;
+
+	for (i = 0; i < nwords; i++)
+		pio_write_16(&chan->cmd->data_port, data[i]);
+}
+
+/** Read the data register callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param buf Destination buffer
+ * @param nwords Number of words to read
+ */
+static void pci_ide_read_data_16(void *arg, uint16_t *buf, size_t nwords)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	size_t i;
+
+	for (i = 0; i < nwords; i++)
+		buf[i] = pio_read_16(&chan->cmd->data_port);
+}
+
+/** Write command register callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param off Register offset
+ * @param value Value to write to command register
+ */
+static void pci_ide_write_cmd_8(void *arg, uint16_t off, uint8_t value)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+
+	pio_write_8(((ioport8_t *)chan->cmd) + off, value);
+}
+
+/** Read command register callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param off Register offset
+ * @return value Value read from command register
+ */
+static uint8_t pci_ide_read_cmd_8(void *arg, uint16_t off)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+
+	return pio_read_8(((ioport8_t *)chan->cmd) + off);
+}
+
+/** Write control register callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param off Register offset
+ * @param value Value to write to control register
+ */
+static void pci_ide_write_ctl_8(void *arg, uint16_t off, uint8_t value)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+
+	pio_write_8(((ioport8_t *)chan->ctl) + off, value);
+}
+
+/** Read control register callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param off Register offset
+ * @return value Value read from control register
+ */
+static uint8_t pci_ide_read_ctl_8(void *arg, uint16_t off)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+
+	return pio_read_8(((ioport8_t *)chan->ctl) + off);
+}
+
+/** Enable IRQ callback handler
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @return EOK on success or an error code
+ */
+static errno_t pci_ide_irq_enable(void *arg)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	async_sess_t *parent_sess;
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "Enable IRQ %d for channel %u",
+	    chan->irq, chan->chan_id);
+
+	parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
+
+	rc = hw_res_enable_interrupt(parent_sess, chan->irq);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error enabling IRQ.");
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Disable IRQ callback handler
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @return EOK on success or an error code
+ */
+static errno_t pci_ide_irq_disable(void *arg)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	async_sess_t *parent_sess;
+	errno_t rc;
+
+	ddf_msg(LVL_DEBUG, "Disable IRQ");
+
+	parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
+
+	rc = hw_res_disable_interrupt(parent_sess, chan->irq);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error disabling IRQ.");
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Add ATA device callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param idx Device index
+ * $param charg Connection handler argument
+ * @return EOK on success or an error code
+ */
+static errno_t pci_ide_add_device(void *arg, unsigned idx, void *charg)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	return pci_ide_fun_create(chan, idx, charg);
+}
+
+/** Remove ATA device callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param idx Device index
+ * @return EOK on success or an error code
+ */
+static errno_t pci_ide_remove_device(void *arg, unsigned idx)
+{
+	pci_ide_channel_t *chan = (pci_ide_channel_t *)arg;
+	return pci_ide_fun_remove(chan, idx);
+}
+
+/** Debug message callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param msg Message
+ */
+static void pci_ide_msg_debug(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_DEBUG, "%s", msg);
+}
+
+/** Notice message callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param msg Message
+ */
+static void pci_ide_msg_note(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_NOTE, "%s", msg);
+}
+
+/** Warning message callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param msg Message
+ */
+static void pci_ide_msg_warn(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_WARN, "%s", msg);
+}
+
+/** Error message callback handler.
+ *
+ * @param arg Argument (pci_ide_channel_t *)
+ * @param msg Message
+ */
+static void pci_ide_msg_error(void *arg, char *msg)
+{
+	(void)arg;
+	ddf_msg(LVL_ERROR, "%s", msg);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/pci-ide/pci-ide.h
===================================================================
--- uspace/drv/block/pci-ide/pci-ide.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/pci-ide.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2024 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pci-ide
+ * @{
+ */
+/** @file PCI IDE driver definitions.
+ */
+
+#ifndef PCI_IDE_H
+#define PCI_IDE_H
+
+#include <ata/ata.h>
+#include <ata/ata_hw.h>
+#include <ddf/driver.h>
+#include <fibril_synch.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define NAME "pci-ide"
+
+/** PCI IDE hardware resources */
+typedef struct {
+	uintptr_t bmregs; /** PCI Bus Master register block base address. */
+	uintptr_t cmd1;	/**< Primary channel command block base address. */
+	uintptr_t ctl1;	/**< Primary channel control block base address. */
+	uintptr_t cmd2;	/**< Secondary channel command block base address. */
+	uintptr_t ctl2;	/**< Secondary channel control block base address. */
+	int irq1;	/**< Primary channel IRQ */
+	int irq2;	/**< Secondary channel IRQ */
+} pci_ide_hwres_t;
+
+/** PCI IDE channel */
+typedef struct pci_ide_channel {
+	/** Parent controller */
+	struct pci_ide_ctrl *ctrl;
+	/** I/O base address of the command registers */
+	uintptr_t cmd_physical;
+	/** I/O base address of the control registers */
+	uintptr_t ctl_physical;
+
+	/** Command registers */
+	ata_cmd_t *cmd;
+	/** Control registers */
+	ata_ctl_t *ctl;
+	/** IRQ (-1 if not used) */
+	int irq;
+	/** IRQ handle */
+	cap_irq_handle_t ihandle;
+
+	/** Synchronize controller access */
+	fibril_mutex_t lock;
+	/** Value of status register read by interrupt handler */
+	uint8_t irq_status;
+
+	/** Libata ATA channel */
+	ata_channel_t *channel;
+	struct pci_ide_fun *fun[2];
+
+	/** Channel ID */
+	unsigned chan_id;
+} pci_ide_channel_t;
+
+/** ISA IDE controller */
+typedef struct pci_ide_ctrl {
+	/** DDF device */
+	ddf_dev_t *dev;
+
+	/** Primary and secondary channel */
+	pci_ide_channel_t channel[2];
+} pci_ide_ctrl_t;
+
+/** PCI IDE function */
+typedef struct pci_ide_fun {
+	ddf_fun_t *fun;
+	void *charg;
+} pci_ide_fun_t;
+
+extern errno_t pci_ide_channel_init(pci_ide_ctrl_t *, pci_ide_channel_t *,
+    unsigned, pci_ide_hwres_t *);
+extern errno_t pci_ide_channel_fini(pci_ide_channel_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/block/pci-ide/pci-ide.ma
===================================================================
--- uspace/drv/block/pci-ide/pci-ide.ma	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/pci-ide.ma	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,1 @@
+10 pci/ven=8086&dev=7010
Index: uspace/drv/block/pci-ide/pci-ide_hw.h
===================================================================
--- uspace/drv/block/pci-ide/pci-ide_hw.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
+++ uspace/drv/block/pci-ide/pci-ide_hw.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2024 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pci-ide
+ * @{
+ */
+/** @file PCI IDE hardware protocol (registers, data structures).
+ *
+ * Based on Intel 82371AB PCI-TO-ISA / IDE XCELERATOR (PIIX4) document.
+ */
+
+#ifndef PCI_IDE_HW_H
+#define PCI_IDE_HW_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/** PCI Bus Master IDE I/O Registers */
+typedef struct {
+	/** Bus Master IDE Command (primary) */
+	uint8_t bmicp;
+	uint8_t rsvd1;
+	/** Bus Master IDE Command (secondary) */
+	uint8_t bmisp;
+	uint8_t rsvd3;
+	/** Bus Master IDE Descriptor Table Pointer (primary) */
+	uint32_t bmidtpp;
+	/** Bus Master IDE Status (secondary) */
+	uint8_t bmics;
+	uint8_t rsvd9;
+	/** Bus Master IDE Status (secondary) */
+	uint8_t bmiss;
+	uint8_t rsvd11;
+	/** Bus Master IDE Descriptor Table Pointer (secondary) */
+	uint32_t bmidtps;
+} pci_ide_regs_t;
+
+enum pci_ide_bmicx_bits {
+	/** Bus Master Read/Write Control */
+	bmicx_rwcon = 0x08,
+	/** Start/Stop Bus Master */
+	bmicx_ssbm = 0x01
+};
+
+enum pci_ide_bmisx_bits {
+	/** Drive 1 DMA Capable */
+	bmisx_dma1cap = 0x40,
+	/** Drive 0 DMA Capable */
+	bmisx_dma0cap = 0x20,
+	/** IDE Interrupte Status */
+	bmisx_ideints = 0x04,
+	/** IDE DMA Error */
+	bmisx_idedmaerr = 0x02,
+	/** Bus Master IDR Active */
+	bmisx_bmidea = 0x01
+};
+
+#define PCI_IDE_CFG_IDETIM 0x40
+#define PCI_IDE_CFG_SIDETIM 0x44
+#define PCI_IDE_CFG_UDMACTL 0x48
+#define PCI_IDE_CFG_UDMATIM 0x4a
+
+/*
+ * For PIIX we need to use ATA ports at fixed legacy ISA addresses.
+ * There are no corresponding PCI I/O ranges and these adresses are
+ * fixed and cannot be reconfigured.
+ */
+enum {
+	pci_ide_ata_cmd_p = 0x01f0,
+	pci_ide_ata_ctl_p = 0x03f4,
+	pci_ide_ata_cmd_s = 0x0170,
+	pci_ide_ata_ctl_s = 0x0374
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/drv/bus/isa/isa.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -76,4 +76,5 @@
 typedef struct {
 	fibril_mutex_t mutex;
+	bool pci_isa_bridge;
 	uint16_t pci_vendor_id;
 	uint16_t pci_device_id;
@@ -92,4 +93,5 @@
 	hw_resource_list_t hw_resources;
 	link_t bus_link;
+	isa_bus_t *bus;
 } isa_fun_t;
 
@@ -204,4 +206,18 @@
 }
 
+static errno_t isa_fun_get_flags(ddf_fun_t *fnode, hw_res_flags_t *rflags)
+{
+	isa_fun_t *fun = isa_fun(fnode);
+	hw_res_flags_t flags;
+
+	flags = 0;
+	if (fun->bus->pci_isa_bridge)
+		flags |= hwf_isa_bridge;
+
+	ddf_msg(LVL_NOTE, "isa_fun_get_flags: returning 0x%x", flags);
+	*rflags = flags;
+	return EOK;
+}
+
 static hw_res_ops_t isa_fun_hw_res_ops = {
 	.get_resource_list = isa_fun_get_resources,
@@ -211,4 +227,5 @@
 	.dma_channel_setup = isa_fun_setup_dma,
 	.dma_channel_remain = isa_fun_remain_dma,
+	.get_flags = isa_fun_get_flags
 };
 
@@ -264,4 +281,5 @@
 	fibril_mutex_initialize(&fun->mutex);
 	fun->hw_resources.resources = fun->resources;
+	fun->bus = isa;
 
 	fun->fnode = fnode;
@@ -743,8 +761,12 @@
 	if (rc != EOK) {
 		ddf_msg(LVL_NOTE, "Cannot read PCI config. Assuming ISA classic.");
+		isa->pci_isa_bridge = false;
 		isa->pci_vendor_id = 0;
 		isa->pci_device_id = 0;
 		isa->pci_class = BASE_CLASS_BRIDGE;
 		isa->pci_subclass = SUB_CLASS_BRIDGE_ISA;
+	} else {
+		ddf_msg(LVL_NOTE, "ISA Bridge");
+		isa->pci_isa_bridge = true;
 	}
 
Index: uspace/drv/meson.build
===================================================================
--- uspace/drv/meson.build	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/drv/meson.build	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -35,4 +35,5 @@
 	'block/ddisk',
 	'block/isa-ide',
+	'block/pci-ide',
 	'block/usbmast',
 	'block/virtio-blk',
Index: uspace/lib/c/generic/device/hw_res.c
===================================================================
--- uspace/lib/c/generic/device/hw_res.c	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/lib/c/generic/device/hw_res.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2024 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * All rights reserved.
@@ -161,4 +162,28 @@
 }
 
+/** Get bus flags.
+ *
+ * @param sess HW res session
+ * @param rflags Place to store the flags
+ *
+ * @return Error code.
+ *
+ */
+errno_t hw_res_get_flags(async_sess_t *sess, hw_res_flags_t *rflags)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	sysarg_t flags;
+	const errno_t ret = async_req_1_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
+	    HW_RES_GET_FLAGS, &flags);
+
+	async_exchange_end(exch);
+
+	if (ret == EOK)
+		*rflags = flags;
+
+	return ret;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/device/hw_res.h
===================================================================
--- uspace/lib/c/include/device/hw_res.h	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/lib/c/include/device/hw_res.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2024 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * All rights reserved.
@@ -56,4 +57,5 @@
 	HW_RES_DMA_CHANNEL_SETUP,
 	HW_RES_DMA_CHANNEL_REMAIN,
+	HW_RES_GET_FLAGS
 } hw_res_method_t;
 
@@ -116,4 +118,9 @@
 }
 
+typedef enum {
+	/** This is an PCI/ISA bridge, not 'classic' ISA bus */
+	hwf_isa_bridge = 0x1
+} hw_res_flags_t;
+
 extern errno_t hw_res_get_resource_list(async_sess_t *, hw_resource_list_t *);
 extern errno_t hw_res_enable_interrupt(async_sess_t *, int);
@@ -124,4 +131,5 @@
     uint32_t, uint8_t);
 extern errno_t hw_res_dma_channel_remain(async_sess_t *, unsigned, size_t *);
+extern errno_t hw_res_get_flags(async_sess_t *, hw_res_flags_t *);
 
 #endif
Index: uspace/lib/drv/generic/remote_hw_res.c
===================================================================
--- uspace/lib/drv/generic/remote_hw_res.c	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/lib/drv/generic/remote_hw_res.c	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2024 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * Copyright (c) 2011 Jan Vesely
@@ -47,4 +48,5 @@
 static void remote_hw_res_dma_channel_setup(ddf_fun_t *, void *, ipc_call_t *);
 static void remote_hw_res_dma_channel_remain(ddf_fun_t *, void *, ipc_call_t *);
+static void remote_hw_res_get_flags(ddf_fun_t *, void *, ipc_call_t *);
 
 static const remote_iface_func_ptr_t remote_hw_res_iface_ops [] = {
@@ -55,4 +57,5 @@
 	[HW_RES_DMA_CHANNEL_SETUP] = &remote_hw_res_dma_channel_setup,
 	[HW_RES_DMA_CHANNEL_REMAIN] = &remote_hw_res_dma_channel_remain,
+	[HW_RES_GET_FLAGS] = &remote_hw_res_get_flags
 };
 
@@ -171,4 +174,19 @@
 }
 
+static void remote_hw_res_get_flags(ddf_fun_t *fun, void *ops,
+    ipc_call_t *call)
+{
+	hw_res_ops_t *hw_res_ops = ops;
+
+	if (hw_res_ops->get_flags == NULL) {
+		async_answer_0(call, ENOTSUP);
+		return;
+	}
+
+	hw_res_flags_t flags = 0;
+	const errno_t ret = hw_res_ops->get_flags(fun, &flags);
+	async_answer_1(call, ret, flags);
+}
+
 /**
  * @}
Index: uspace/lib/drv/include/ops/hw_res.h
===================================================================
--- uspace/lib/drv/include/ops/hw_res.h	(revision 180100544b0cbf469b1ecf4d671b10bf08de9919)
+++ uspace/lib/drv/include/ops/hw_res.h	(revision 443695ef37d4e47d223158ab3b07f84e2d5df943)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2024 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * Copyright (c) 2011 Jan Vesely
@@ -49,4 +50,5 @@
 	errno_t (*dma_channel_setup)(ddf_fun_t *, unsigned, uint32_t, uint32_t, uint8_t);
 	errno_t (*dma_channel_remain)(ddf_fun_t *, unsigned, size_t *);
+	errno_t (*get_flags)(ddf_fun_t *, hw_res_flags_t *);
 } hw_res_ops_t;
 
