HOWTO-UIO This document briefly describes how to use UIO to develop userspace drivers. =============== Kernel Config =============== The following kernel configuration options must be enabled. CONFIG_UIO=y CONFIG_UIO_PDRV_GENIRQ=y ================ Boot Arguments ================ The following boot argument must be present. uio_pdrv_genirq.of_id=generic-uio ============= Device Tree ============= Each device must be specified in the device tree. Each device can have a single mapping or multiple mappings (up to 1048576). Each device can have maximum 1 interrupt associated with it. IMPORTANT: All mapping addresses must be page-aligned and the mapping sizes must be an exact multiple of pages. 0x400 and 0x1400 are examples of INVALID mapping sizes. 0x1000 and 0x2000 are examples of VALID mapping sizes. Here are some device tree entry examples. /* 1 mapping, no interrupt */ fpga-timer@0x43c15000 { compatible = "generic-uio"; reg = <0x43c15000 0x1000>; }; /* 1 mapping with interrupt */ fpga-dma@0x40400000 { compatible = "generic-uio"; reg = <0x40400000 0x1000>; interrupts = <0 33 1>; interrupt-parent = <&intc>; }; /* 2 mappings with interrupt */ fpga-leddisplay@0x43c16000 { compatible = "generic-uio"; reg = <0x43c16000 0x1000 0x43c18000 0x1000>; interrupts = <0 34 1>; interrupt-parent = <&intc>; }; Specifying multiple mappings for a single device should only be done if a single device really provides multiple memory regions. Although allowed, it is not recommended to consolidate multiple devices into a single device with multiple mappings since this tends to be confusing. ==================== Userspace - libuio ==================== A userspace library is available to allow simple usage of UIO: libuio. git clone https://github.com/Linutronix/libuio.git libuio uses glib by default. However, if glib is not already available on the embedded system, it is recommended to build libuio without this extra dependency $ ./configure --host=arm-none-linux-gnueabi --without-glib ========================= Userspace - application ========================= Below is a simple application demonstrating how to use libuio. The application uses a device with 2 mappings. For the first mapping, it makes use of the libuio access functions (uio_read/uio_write) to quickly access registers. For the second mapping, it casts the memory to a structure to allow access without any functions. It does not matter which method is used. The application simply illustrates both possibilities. The device also provides an interrupt. The application waits for 3 interrupts before finishing. IMPORTANT: It is the responsibility of the application to clear the hardware interrupt after each interrupt, if the hardware requires interrupt clearing. Using libuio, the application never needs to know specific information about the device (such as irq number, memory address, memory size). This information, which is specified in the device tree, is read by libuio dynamically each time the application is run. libuio provides many helper functions not shown here. By inspecting libuio.h or looking at the libuio source code it should be fairly easy to identify what the various functions do. (For example, uio_get_fd() is helpful if it is desired to use select/poll to wait for interrupts.) #include #include #include /* structure for map1 */ struct hwregs { uint32_t version; uint32_t ctrl; uint32_t irq_status; uint32_t oe; }; int main(void) { volatile struct hwregs *regs; struct uio_info_t *dev; uint32_t val; int i; /* get handle to UIO device */ dev = uio_find_by_uio_name("fpga-leddisplay"); if (!dev) { fprintf(stderr, "unable to find UIO device\n"); return 1; } /* open UIO device */ if (uio_open(dev) != 0) { fprintf(stderr, "unable to open UIO device\n"); return 1; } /* read 32-bit value at offset 0x40 of map0 using libuio */ if (uio_read32(dev, 0, 0x40, &val) != 0) { fprintf(stderr, "unable to read\n"); return 1; } printf("map0: 0x40 = 0x%x\n", val); /* use a struct to access registers of map1 */ regs = uio_get_mem_map(dev, 1); printf("map1: version = 0x%x\n", regs->version); /* make sure irq line is enabled */ uio_enable_irq(dev); /* process 3 interrupts */ for (i = 0; i < 3; i++) { /* blocking wait for interrupt */ if (uio_irqwait(dev) != 0) { fprintf(stderr, "irq wait failed: %s\n", strerror(errno)); return 1; } /* * Linux disabled the interrupt line. The userspace application * is responsible for acknowledging the interrupt in hardware * registers if the hardware requires that. Otherwise the * interrupt will fire again as soon as the interrupt line is * re-enabled. */ /* acknowledge/clear the interrupt */ regs->irq_status = 0x80; /* handle interrupt */ printf("irq detected!\n"); /* re-enable interrupt line */ uio_enable_irq(dev); } /* close UIO device */ uio_close(dev); return 0; }