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
|
#include "gpio.h"
#include "peripherals.h"
#include "types.h"
#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, reg, value)
#define READ_GPIO_REG(reg) READ_REGISTER(P0, 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,
};
/**
* GPIO direction for each pin.
* When a GPIO is in disabled state, we forcefully put it in INPUT mode.
*/
enum gpio_direction {
INPUT = 0,
OUTPUT = 1,
};
/**
* The pull configuration for a GPIO line.
*/
enum gpio_pull_configuration {
/** Keep the GPIO in a floating state when the circuit is open */
NO_PULL = 0,
/** Force the signal to the `LOW` level when the circuit is open */
PULL_DOWN = 1,
/** Force the signal to the `HIGH` level when the circuit is open */
PULL_UP = 3,
};
/**
* The drive configuration for a GPIO line.
*/
enum gpio_drive_configuration {
/** Standard 0, Standard 1 */
S0S1 = 0,
/** High drive 0, Standard 1 */
H0S1 = 1,
/** Standard 0, High drive 1 */
S0H1 = 2,
/** High drive 0, High Drive 1 */
H0H1 = 3,
/** Disconnect 0, Standard 1 */
D0S1 = 4,
/** Disconnect 0, High drive 1 */
D0H1 = 5,
/** Standard 0, Disconnect 1 */
S0D1 = 6,
/** High drive 0, Disconnect 1 */
H0D1 = 7,
};
struct pin_configuration {
enum gpio_direction direction;
union {
struct {
enum gpio_pull_configuration pull_config;
enum gpio_level sensibility;
} input;
struct {
enum gpio_drive_configuration drive_config;
} output;
} config;
};
/**
* The hardcoded configuration for each GPIO pin.
*/
static const struct pin_configuration pins_configs[32] = {
[PIN_DISPLAY_SPI_CLOCK] = {
.direction = OUTPUT,
.config = {
.output = {
.drive_config = S0S1,
},
},
},
};
/**
* 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 active if it's in `INPUT` mode
* but 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]);
enum gpio_direction dir = conf & 0x1;
u8 sense = (conf >> 16) & 0x3;
return (dir != INPUT) || (sense != 0);
}
void gpio_init_pin(enum gpio_pin pin) {
u32 pin_reg = cnf_offsets[pin];
struct pin_configuration pin_config = pins_configs[pin];
u32 pin_reg_value = 0;
pin_reg_value |= pin_config.direction & 0x1;
if (pin_config.direction == INPUT) {
pin_reg_value |= (pin_config.config.input.pull_config & 0x3) << 2;
pin_reg_value |= (pin_config.config.input.sensibility & 0x3) << 16;
} else {
pin_reg_value |= (pin_config.config.output.drive_config & 0x7) << 8;
}
WRITE_PIN_CNF(pin_reg, pin_reg_value);
}
u8 gpio_disable_pin(enum gpio_pin pin) {
u32 pin_cnf_value = 0x0;
if (!is_pin_active(pin)) {
return 0;
}
/* INPUT mode with detection mechanism disabled */
pin_cnf_value |= INPUT & 0x1;
WRITE_PIN_CNF(cnf_offsets[pin], pin_cnf_value);
return 1;
}
enum gpio_level gpio_read_pin(enum gpio_pin pin) {
u32 pins_level = READ_GPIO_REG(IN);
return (pins_level >> pin) & 0x1;
}
u8 gpio_write_pin(enum gpio_pin pin, enum gpio_level level) {
enum gpio_register reg = level == HIGH ? OUTSET : OUTCLR;
if (pins_configs[pin].direction != OUTPUT) {
return 1;
}
WRITE_GPIO_REG(reg, (1 << pin));
return 0;
}
|