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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
|
This readme describes the linux kernel gpio framework as it deploys on the
DE10-Nano target environment. You may find the following references useful for
more information on this topic as well.
<linux-source-tree>/Documentation/gpio/gpio.txt
<linux-source-tree>/Documentation/gpio/sysfs.txt
<linux-source-tree>/Documentation/devicetree/bindings/gpio/gpio.txt
<linux-source-tree>/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
<linux-source-tree>/Documentation/devicetree/bindings/gpio/gpio-altera.txt
If you cut and paste the following function into a console running on the
DE10-Nano target you can extract the useful information contained in the run
time devicetree maintained by the kernel in the procfs.
################################################################################
# find gpio controllers in device tree
################################################################################
function find_gpio_controllers_dt ()
{
for NEXT in $(find -L /proc/device-tree -name "compatible" | sort);
do
cat ${NEXT} | grep -xz "snps,dw-apb-gpio" > /dev/null && {
GPIO_DIRNAME="$(dirname ${NEXT})";
echo ${GPIO_DIRNAME};
GPIO_COMPATIBLE="$(cat ${GPIO_DIRNAME}/compatible)";
echo -e "\tcompatible = '${GPIO_COMPATIBLE}'";
WF="${GPIO_DIRNAME}/gpio-controller@0/snps,nr-gpios";
GPIO_HEX_WIDTH="$(hexdump -v -e '"0x"' -e '4/1 "%02x"' "${WF}")";
GPIO_WIDTH=$(printf "%d" ${GPIO_HEX_WIDTH});
echo -e "\t width = '${GPIO_WIDTH}'"
};
cat ${NEXT} | grep -e "altr,pio" > /dev/null && {
GPIO_DIRNAME="$(dirname ${NEXT})";
echo ${GPIO_DIRNAME};
GPIO_COMPATIBLE="$(cat ${GPIO_DIRNAME}/compatible)";
echo -e "\tcompatible = '${GPIO_COMPATIBLE}'";
WF="${GPIO_DIRNAME}/altr,gpio-bank-width";
GPIO_HEX_WIDTH="$(hexdump -v -e '"0x"' -e '4/1 "%02x"' "${WF}")";
GPIO_WIDTH=$(printf "%d" ${GPIO_HEX_WIDTH});
echo -e "\t width = '${GPIO_WIDTH}'"
};
done
}
################################################################################
The function above is provided in the file 'find_gpio_controllers_dt.src', which
you can source into your environment by running
'source find_gpio_controllers_dt.src'.
When we run the function above on the DE10-Nano target it searches for nodes
containing the 'compatible' string fragments of 'snps,dw-apb-gpio' or 'altr,pio'
which are the identifiers for the hard GPIO controllers and the soft GPIO
controllers, respectively, in our system. The function then prints the path to
the node that it found and extracts the compatible string and width of the gpio
controller and prints those statistics out as well. It does this for all gpio
controller nodes that it locates in the device tree.
root@DE10-Nano:~# find_gpio_controllers_dt
/proc/device-tree/soc/bridge@0xc0000000/gpio@0x100003000
compatible = 'altr,pio-15.0altr,pio-1.0'
width = '8'
/proc/device-tree/soc/bridge@0xc0000000/gpio@0x100004000
compatible = 'altr,pio-15.0altr,pio-1.0'
width = '4'
/proc/device-tree/soc/bridge@0xc0000000/gpio@0x100005000
compatible = 'altr,pio-15.0altr,pio-1.0'
width = '2'
/proc/device-tree/soc/gpio@ff708000
compatible = 'snps,dw-apb-gpio'
width = '29'
/proc/device-tree/soc/gpio@ff709000
compatible = 'snps,dw-apb-gpio'
width = '29'
/proc/device-tree/soc/gpio@ff70a000
compatible = 'snps,dw-apb-gpio'
width = '27'
Now the run time binary device tree blob does not contain some of the useful
labels that were used in the original DTS source files that can help us
understand what gpio controllers belong to what functions on the board. These
fragments out of the original DTS source file contain the labels which show
the functions of the various gpio controllers a little better:
led_pio: gpio@0x100003000 {
};
dipsw_pio: gpio@0x100004000 {
};
button_pio: gpio@0x100005000 {
};
gpio@ff708000 {
gpio0: gpio-controller@0 {
};
};
gpio@ff709000 {
gpio1: gpio-controller@0 {
};
};
gpio@ff70a000 {
gpio2: gpio-controller@0 {
};
};
This is how these gpio controllers are implemented on the DE10-Nano target:
led_pio - 8-bit output, fpga based gpio, registered as 'gpio-leds' in the device
tree to be used in the gpio-leds framework to drive LED0-LED7 on the
DE10-Nano board.
dipsw_pio - 4-bit input, fpga based gpio, registered as 'gpio-keys' in the
device tree to be used in the gpio-keys framework to receive input
events from switches SW0-SW3 on the DE10-Nano board.
button_pio - 2-bit input, fpga based gpio, receives input from push buttons
KEY0 and KEY1 on the DE10-Nano board.
gpio0 - 29-bit, hps based gpio controller, no interfaces connected to these pins
on the Altas board.
gpio1 - 29-bit, hps based gpio controller, the HPS GPIO53 port served by this
controller is registered as 'gpio-leds' in the device tree to be used in
the gpio-leds framework to drive the USERLED on the Altas board. The
HPS GPIO54 port served by this controller receives input from push
button KEY2 on the DE10-Nano board.
gpio2 - 27-bit, hps based gpio controller, no interfaces connected to these pins
on the DE10-Nano board.
The kernel gpio framework creates some sysfs entries for all the gpio
controllers that we can interact with to gain access to and manipulate the gpio
ports that we are interested in. These sysfs entries are located in
'/sys/class/gpio', like this:
root@DE10-Nano:~# ls /sys/class/gpio
export gpiochip415 gpiochip427 gpiochip483
gpiochip413 gpiochip419 gpiochip454 unexport
Each of the gpio controllers is represented by a gpiochip* entry in the display
above. To determine which gpiochip* entry corresponds to which gpio controller
we can run a loop like this to discover them:
for NEXT in $(find /sys/class/gpio/ -name "gpiochip*" | sort)
do
echo ${NEXT} = $(cat ${NEXT}/label)
done
/sys/class/gpio/gpiochip413 = /soc/bridge@0xc0000000/gpio@0x100005000
/sys/class/gpio/gpiochip415 = /soc/bridge@0xc0000000/gpio@0x100004000
/sys/class/gpio/gpiochip419 = /soc/bridge@0xc0000000/gpio@0x100003000
/sys/class/gpio/gpiochip427 = ff70a000.gpio
/sys/class/gpio/gpiochip454 = ff709000.gpio
/sys/class/gpio/gpiochip483 = ff708000.gpio
You should be able to correlate the string from the 'label' file with the output
that we saw from the device tree entries. We can also extract the width of each
gpio controller from the sysfs entries like this:
for NEXT in $(find /sys/class/gpio/ -name "gpiochip*" | sort)
do
echo ${NEXT} = $(cat ${NEXT}/ngpio)
done
/sys/class/gpio/gpiochip413 = 2
/sys/class/gpio/gpiochip415 = 4
/sys/class/gpio/gpiochip419 = 8
/sys/class/gpio/gpiochip427 = 27
/sys/class/gpio/gpiochip454 = 29
/sys/class/gpio/gpiochip483 = 29
These widths should correlate to what was reported out of the device tree.
Now if we want to work with a raw gpio we can use the gpio framework to do so.
There are a few raw gpios on the DE10-Nano system that we can demonstrate. We
cannot demonstrate any interaction with the gpios that were registered with the
gpio-leds or gpio-keys framework since those frameworks are in control of those
gpio ports, but those are demonstrated in their own specific readme files.
Let's try to read the state of the 'button_pio' ports that are controlled by the
'gpio@0x100005000' controller which maps into 'gpiochip157' for the 2-bits that
are connected to the KEY0 and KEY1 push buttons. To being we 'export' the two
gpio ports that we're interested in like this:
root@DE10-Nano:~# echo 413 > /sys/class/gpio/export
root@DE10-Nano:~# echo 414 > /sys/class/gpio/export
And now you can see that we have two new entires in the sysfs, each representing
the individual gpio port that we exported.
root@DE10-Nano:~# ls /sys/class/gpio
export gpio414 gpiochip415 gpiochip427 gpiochip483
gpio413 gpiochip413 gpiochip419 gpiochip454 unexport
Each of these individual gpio directories contain the following files:
root@DE10-Nano:~# ls /sys/class/gpio/gpio413
active_low direction edge power subsystem uevent value
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/active_low
0
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/direction
in
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/edge
none
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/value
1
We can see from above that this port is defined as an active high input with no
edge detection interrupt currently enabled and the value of the port is
currently 1. Reading the 'value' sysfs file will always return the live state
of this gpio port. If we read the 'value' file a few times as we press and
release the KEY0 push button we can see the live state change:
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/value
1
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/value
0
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/value
1
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/value
0
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/value
1
Now if we write the phrase 'falling' into the 'edge' file then the edge capture
interrupt functionality will be enabled for this gpio:
root@DE10-Nano:~# echo falling > /sys/class/gpio/gpio413/edge
root@DE10-Nano:~# cat /sys/class/gpio/gpio413/edge
falling
At this point two things occur, if we could call poll() or select() against the
file descriptor for the 'value' file, it would block until the interrupt fired,
if we simply read() the 'value' file then we will continue to see the live state
of the port. The second thing that we can observe is that the
'/proc/interrupts' file shows our newly active IRQ for this PIO that has been
registered:
root@DE10-Nano:~# cat /proc/interrupts | grep 144
144: 1 0 altera-gpio 0 gpiolib
If we press and release the KEY0 push button a few times we should see the
interrupt count increase.
root@DE10-Nano:~# cat /proc/interrupts | grep 144
144: 5 0 altera-gpio 0 gpiolib
Now the gpio414 that we exported for the KEY1 push button will operate the same
way. If we press and release KEY1 while we monitor the 'value' file for gpio414
we should see the same behavior appear:
root@DE10-Nano:~# cat /sys/class/gpio/gpio414/value
1
root@DE10-Nano:~# cat /sys/class/gpio/gpio414/value
0
root@DE10-Nano:~# cat /sys/class/gpio/gpio414/value
1
root@DE10-Nano:~# cat /sys/class/gpio/gpio414/value
0
There is one other raw gpio on the DE10-Nano board that we could interact with,
it's the KEY2 push button connected to the HPS GPIO54 input which comes in
through the gpio1 controller that is assigned to gpiochip198. To export this
gpio port we calculate it's position in gpio1 by using this equation:
root@DE10-Nano:~# expr 454 + 54 - 29
479
^ ^ ^
| | |
| | +--- the number of HPS GPIO ports in gpio0
| +-------- the HPS GPIO we want
+-------------- the base gpio in gpiochip454
So we want to export gpio 479 to gain access to this port, like this:
root@DE10-Nano:~# echo 479 > /sys/class/gpio/export
root@DE10-Nano:~# ls /sys/class/gpio/
export gpio414 gpiochip413 gpiochip419 gpiochip454 unexport
gpio413 gpio479 gpiochip415 gpiochip427 gpiochip483
And we see the gpio479 file in the sysfs now. We can interact with this gpio
the same way that we did the others:
root@DE10-Nano:~# cat /sys/class/gpio/gpio479/value
1
root@DE10-Nano:~# cat /sys/class/gpio/gpio479/value
0
root@DE10-Nano:~# cat /sys/class/gpio/gpio479/value
1
And if we enable interrupts for this port like we did on the other port:
root@DE10-Nano:~# echo falling > /sys/class/gpio/gpio479/edge
root@DE10-Nano:~# cat /sys/class/gpio/gpio479/edge
falling
Then we see this gpio controller appear in the '/proc/interrupts' listing:
root@DE10-Nano:~# cat /proc/interrupts | grep 116
116: 0 0 gpio-dwapb 3 0-0053
When we are done using these gpio ports that we have exported into user space we
can 'unexport' them by passing the same values into the 'unexport' sysfs file
that we used to export the ports originally:
root@DE10-Nano:~# echo 413 > /sys/class/gpio/unexport
root@DE10-Nano:~# echo 414 > /sys/class/gpio/unexport
root@DE10-Nano:~# echo 479 > /sys/class/gpio/unexport
root@DE10-Nano:~# ls /sys/class/gpio
export gpiochip415 gpiochip427 gpiochip483
gpiochip413 gpiochip419 gpiochip454 unexport
And we can see that the exported entries have been removed from the sysfs.
--------------------------------------------------------------------------------
Example programs and scripts
--------------------------------------------------------------------------------
This directory contains a few examples to demonstrate how to interact with the
push buttons on the DE10-Nano board that have been registered in the gpio
framework. There is a shell script called 'show_KEY0_pb_state.sh' and a C
program called 'show_KEY0_pb_state.c'. Each of these examples query the gpio
state in exactly the same way. Then there is a C program called
'poll_KEY0_pb_state.c' which simply adds a poll() call to the
'show_KEY0_pb_state.sh' program to demonstrate the interrupt functionality
provided by the gpio framework to detect the push button press via a hardware
interrupt, which can only be accomplished with the poll() or select() type of
system call.
To build the 'show_KEY0_pb_state.c' application simply run the
'build_show_KEY0_pb_state.sh' shell script. That will compile the
'show_KEY0_pb_state.c' source file and produce the executable
'show_KEY0_pb_state' application. Refer to the 'build_show_KEY0_pb_state.sh'
script to see how the application is actually compiled and refer to the C
program source file for more details on how it actually works.
To build the 'poll_KEY0_pb_state.c' application simply run the
'build_poll_KEY0_pb_state.sh' shell script. That will compile the
'poll_KEY0_pb_state.c' source file and produce the executable
'poll_KEY0_pb_state' application. Refer to the 'build_poll_KEY0_pb_state.sh'
script to see how the application is actually compiled and refer to the C
program source file for more details on how it actually works.
Refer to the 'show_KEY0_pb_state.sh' source file for more details on how it
actually works.
Once you've built the applications, you can run both the script and the
applications like this:
./show_KEY0_pb_state.sh <<< to run the script
./show_KEY0_pb_state <<< to run the program
./poll_KEY0_pb_state <<< to run the program with poll()
The 'show' program and script will read the current state of the KEY0 push
button input and print it out, and then exit. The 'poll' program will enable
the KEY0 push button to generate a falling edge interrupt when it is pressed and
then ask you to press the KEY0 push button. When the 'poll' program detects the
interrupt event it prints that status out and exits.
Both the programs and the script monitor the push buttons by interacting with
the linux gpio controller framework.
|