summaryrefslogtreecommitdiff
path: root/schulung_tools/notes/uio.txt
blob: 65d017658ca71d2c347bda79c1f9ff28f716769b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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;
}