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
|
#include "gpio.h"
#include "register.h"
#include "types.h"
#define P0_BASE_ADDR 0x50000000
#define WRITE_PIN_CNF(pin, value) WRITE_GPIO_REG(pin, value)
#define READ_PIN_CNF(pin) READ_GPIO_REG(pin)
#define WRITE_GPIO_REG(reg, value) WRITE_REGISTER(P0_BASE_ADDR, reg, value)
#define READ_GPIO_REG(reg) READ_REGISTER(P0_BASE_ADDR, reg)
/**
* Available behaviours for the DETECT signal.
* `PIN_DETECT` combines every individuals PIN's DETECT signal.
* `LDETECT` uses the LDETECT signal.
*/
enum gpio_detect_mode {
PIN_DETECT = 0,
LDETECT = 1,
};
/**
* Memory-mapped register for the GPIO peripheral with their offset from its base address.
* Check the nrf52832's manual for their description.
*/
enum gpio_register {
OUT = 0x504,
OUTSET = 0x508,
OUTCLR = 0x50c,
IN = 0x510,
DIR = 0x514,
DIRSET = 0x518,
DIRCLR = 0x51c,
LATCH = 0x520,
DETECTMODE = 0x524,
PIN_CNF_0 = 0x700,
PIN_CNF_1 = 0x704,
PIN_CNF_2 = 0x708,
PIN_CNF_3 = 0x70c,
PIN_CNF_4 = 0x71c,
PIN_CNF_5 = 0x714,
PIN_CNF_6 = 0x718,
PIN_CNF_7 = 0x71c,
PIN_CNF_8 = 0x720,
PIN_CNF_9 = 0x724,
PIN_CNF_10 = 0x728,
PIN_CNF_11 = 0x72c,
PIN_CNF_12 = 0x730,
PIN_CNF_13 = 0x734,
PIN_CNF_14 = 0x738,
PIN_CNF_15 = 0x73c,
PIN_CNF_16 = 0x740,
PIN_CNF_17 = 0x744,
PIN_CNF_18 = 0x748,
PIN_CNF_19 = 0x74c,
PIN_CNF_20 = 0x750,
PIN_CNF_21 = 0x754,
PIN_CNF_22 = 0x758,
PIN_CNF_23 = 0x75c,
PIN_CNF_24 = 0x760,
PIN_CNF_25 = 0x764,
PIN_CNF_26 = 0x768,
PIN_CNF_27 = 0x76c,
PIN_CNF_28 = 0x770,
PIN_CNF_29 = 0x774,
PIN_CNF_30 = 0x778,
PIN_CNF_31 = 0x77c,
};
/**
* The offset of each pin configuration register from the GPIO base address.
*/
static const u32 cnf_offsets[] = {
[PIN_0] = PIN_CNF_0,
[PIN_1] = PIN_CNF_1,
[PIN_2] = PIN_CNF_2,
[PIN_3] = PIN_CNF_3,
[PIN_4] = PIN_CNF_4,
[PIN_5] = PIN_CNF_5,
[PIN_6] = PIN_CNF_6,
[PIN_7] = PIN_CNF_7,
[PIN_8] = PIN_CNF_8,
[PIN_9] = PIN_CNF_9,
[PIN_10] = PIN_CNF_10,
[PIN_11] = PIN_CNF_11,
[PIN_12] = PIN_CNF_12,
[PIN_13] = PIN_CNF_13,
[PIN_14] = PIN_CNF_14,
[PIN_15] = PIN_CNF_15,
[PIN_16] = PIN_CNF_16,
[PIN_17] = PIN_CNF_17,
[PIN_18] = PIN_CNF_18,
[PIN_19] = PIN_CNF_19,
[PIN_20] = PIN_CNF_20,
[PIN_21] = PIN_CNF_21,
[PIN_22] = PIN_CNF_22,
[PIN_23] = PIN_CNF_23,
[PIN_24] = PIN_CNF_24,
[PIN_25] = PIN_CNF_25,
[PIN_26] = PIN_CNF_26,
[PIN_27] = PIN_CNF_27,
[PIN_28] = PIN_CNF_28,
[PIN_29] = PIN_CNF_29,
[PIN_30] = PIN_CNF_30,
[PIN_31] = PIN_CNF_31,
};
/**
* We consider that a pin is inactive if it's in `INPUT` mode
* and has its detect mechanism disabled (the SENSE field of
* its configuration register is 0).
*
* Warning: that does not mean the pin is not already used elsewhere.
* Peripherals or drivers can put a pin in this state for whatever reason.
* Always make sure that the pin is not currently is use before doing
* anything with it.
*/
static u8 is_pin_active(enum gpio_pin pin) {
u32 conf = READ_PIN_CNF(cnf_offsets[pin]);
u32 dir = conf & 0x1;
u8 sense = (conf >> 16) & 0x3;
return (dir != 0) || (sense != 0);
}
void gpio_input_pin_init(enum gpio_pin pin, enum gpio_pull_configuration pull_config, enum gpio_level sensibility) {
u32 pin_reg = cnf_offsets[pin];
u32 pin_reg_value = 0x0;
pin_reg_value |= (pull_config & 0x3) << 2;
pin_reg_value |= (sensibility & 0x3) << 16;
WRITE_PIN_CNF(pin_reg, pin_reg_value);
}
void gpio_output_pin_init(enum gpio_pin pin, enum gpio_drive_configuration drive_config) {
u32 pin_reg = cnf_offsets[pin];
u32 pin_reg_value = 0x1;
pin_reg_value |= (drive_config & 0x7) << 8;
WRITE_PIN_CNF(pin_reg, pin_reg_value);
}
u8 gpio_pin_disable(enum gpio_pin pin) {
if (!is_pin_active(pin)) {
return 0;
}
/* Configuration register reset value */
WRITE_PIN_CNF(cnf_offsets[pin], 0x2);
return 1;
}
enum gpio_level gpio_pin_get_level(enum gpio_pin pin) {
u32 pins_level = READ_GPIO_REG(IN);
return (pins_level >> pin) & 0x1;
}
void gpio_pin_set_level(enum gpio_pin pin, enum gpio_level level) {
enum gpio_register reg = level == HIGH ? OUTSET : OUTCLR;
WRITE_GPIO_REG(reg, (1 << pin));
}
|