[PATCH] misc: add /dev/port device for I/O ports

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Jun 17 04:42:00 PDT 2024


For MMIO, we have /dev/mem and the iomem command, but for I/O ports, we
only have the ioport command and no way to access I/O ports from shell.

Add a /dev/port device that interacts directly with I/O ports.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 drivers/misc/Kconfig  |   4 ++
 drivers/misc/Makefile |   1 +
 drivers/misc/port.c   | 158 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 163 insertions(+)
 create mode 100644 drivers/misc/port.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 78c9c193d835..40482f8b88a7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -24,6 +24,10 @@ config STATE_DRV
 config DEV_MEM
         bool "Generic memory I/O device (/dev/mem)"
 
+config DEV_PORT
+	bool "Generic port I/O device (/dev/port)"
+	depends on X86 || SANDBOX || COMPILE_TEST
+
 config UBOOTVAR
 	bool "U-Boot environment storage"
 	depends on OFTREE
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 986f7b1b381b..871af85d5920 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_JTAG)		+= jtag.o
 obj-$(CONFIG_SRAM)		+= sram.o
 obj-$(CONFIG_STATE_DRV)		+= state.o
 obj-$(CONFIG_DEV_MEM)		+= mem.o
+obj-$(CONFIG_DEV_PORT)		+= port.o
 obj-$(CONFIG_UBOOTVAR)		+= ubootvar.o
 obj-$(CONFIG_STARFIVE_PWRSEQ)	+= starfive-pwrseq.o
 obj-$(CONFIG_STORAGE_BY_UUID)	+= storage-by-uuid.o
diff --git a/drivers/misc/port.c b/drivers/misc/port.c
new file mode 100644
index 000000000000..ddc443eb16df
--- /dev/null
+++ b/drivers/misc/port.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2011 Sascha Hauer <s.hauer at pengutronix.de>, Pengutronix
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <fcntl.h>
+#include <init.h>
+
+static ssize_t port_read(struct cdev *cdev, void *buf, size_t count, loff_t offset,
+		 unsigned long flags)
+{
+	struct device *dev = cdev->dev;
+	ssize_t size;
+	int rwsize = (flags & (O_RWSIZE_1 | O_RWSIZE_2 | O_RWSIZE_4));
+
+	if (!dev)
+		return -1;
+
+
+	if (!dev || dev->num_resources < 1)
+		return -1;
+
+	if (resource_size(&dev->resource[0]) > 0 || offset != 0)
+		count = min_t(size_t, count,
+			      resource_size(&dev->resource[0]) - offset);
+
+	size = count;
+
+	/* no rwsize specification given. Use maximum */
+	if (!rwsize)
+		rwsize = 4;
+
+	rwsize = rwsize >> O_RWSIZE_SHIFT;
+
+	count = ALIGN_DOWN(count, rwsize);
+
+	while (count) {
+		switch (rwsize) {
+		case 1:
+			*((u8 *)buf) = inb(offset);
+			break;
+		case 2:
+			*((u16 *)buf) = inw(offset);
+			break;
+		case 4:
+			*((u32  *)buf) = inl(offset);
+			break;
+		}
+		buf += rwsize;
+		offset += rwsize;
+		count -= rwsize;
+	}
+
+	return size;
+}
+
+static ssize_t port_write(struct cdev *cdev, const void *buf, size_t count,
+			  loff_t offset, unsigned long flags)
+{
+	struct device *dev = cdev->dev;
+	ssize_t size;
+	int rwsize = (flags & (O_RWSIZE_1 | O_RWSIZE_2 | O_RWSIZE_4));
+
+	if (!dev)
+		return -1;
+
+	if (!dev || dev->num_resources < 1)
+		return -1;
+
+	if (resource_size(&dev->resource[0]) > 0 || offset != 0)
+		count = min_t(size_t, count, resource_size(&dev->resource[0]) - offset);
+	size = count;
+
+	/* no rwsize specification given. Use maximum */
+	if (!rwsize)
+		rwsize = 4;
+
+	rwsize = rwsize >> O_RWSIZE_SHIFT;
+
+	count = ALIGN_DOWN(count, rwsize);
+
+	while (count) {
+		switch (rwsize) {
+		case 1:
+			outb(*((u8 *)buf), offset);
+			break;
+		case 2:
+			outw(*((u16 *)buf), offset);
+			break;
+		case 4:
+			outl(*((u32 *)buf), offset);
+			break;
+		}
+		buf += rwsize;
+		offset += rwsize;
+		count -= rwsize;
+	}
+
+	return size;
+}
+
+static struct cdev_operations portops = {
+	.read  = port_read,
+	.write = port_write,
+};
+
+static int port_probe(struct device *dev)
+{
+	struct cdev *cdev;
+
+	cdev = xzalloc(sizeof (*cdev));
+	dev->priv = cdev;
+
+	cdev->name = (char*)dev->resource[0].name;
+	cdev->size = resource_size(&dev->resource[0]);
+
+	cdev->ops = &portops;
+	cdev->dev = dev;
+
+	devfs_create(cdev);
+
+	return 0;
+}
+
+static struct driver port_drv = {
+	.name  = "port",
+	.probe = port_probe,
+};
+
+static int port_init(void)
+{
+	struct device *dev;
+	struct resource res = {
+		.start = 0,
+		.end = IO_SPACE_LIMIT,
+		.flags = IORESOURCE_IO,
+		.name = "port",
+	};
+	int ret;
+
+	dev = device_alloc("port", DEVICE_ID_DYNAMIC);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->resource = xmemdup(&res, sizeof(res));
+	dev->num_resources = 1;
+
+	ret = platform_device_register(dev);
+	if (ret)
+		return ret;
+
+	pr_notice("I/O port base %p\n", PCI_IOBASE);
+
+	return platform_driver_register(&port_drv);
+}
+device_initcall(port_init);
-- 
2.39.2




More information about the barebox mailing list