summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ogness <john.ogness@linutronix.de>2019-02-28 16:24:43 +0106
committerJohn Ogness <john.ogness@linutronix.de>2019-02-28 16:24:43 +0106
commit67ab994696ddb43158af3a951fd420c2a6a243ad (patch)
tree34786a54a8df1c9c0b88a587f4a0c78bd6c14b45
parent6b8e3545c3d46a72e0ebd0f0cc53841e26ceee25 (diff)
notes: uio.txt
This text document goes through examples of how to use the uio_pdrv_genirq driver for UIO drivers. It covers the device tree entries for various scenarios as well as userspace code using libuio to access/map the devices. Signed-off-by: John Ogness <john.ogness@linutronix.de>
-rw-r--r--schulung_tools/notes/uio.txt195
1 files changed, 195 insertions, 0 deletions
diff --git a/schulung_tools/notes/uio.txt b/schulung_tools/notes/uio.txt
new file mode 100644
index 0000000..65d0176
--- /dev/null
+++ b/schulung_tools/notes/uio.txt
@@ -0,0 +1,195 @@
+HOWTO-UIO <john.ogness@linutronix.de>
+
+
+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 <stdio.h>
+#include <errno.h>
+#include <libuio.h>
+
+/* 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;
+}