diff options
author | Victor Mignot <victor@vmignot.fr> | 2025-07-30 21:22:47 +0200 |
---|---|---|
committer | Victor Mignot <victor@vmignot.fr> | 2025-08-11 14:07:27 +0200 |
commit | 31eb5b5de15908fecf3a615910b3273de62baeeb (patch) | |
tree | 9ea253c9a695a85257caa36df26299424ccc21fe /src/gpio.c | |
parent | c4314b35efe14fd0c1c57c8c3577431b380e323e (diff) | |
download | nanji-31eb5b5de15908fecf3a615910b3273de62baeeb.tar.gz |
nrf52832: add GPIO drivers
Diffstat (limited to 'src/gpio.c')
-rw-r--r-- | src/gpio.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/gpio.c b/src/gpio.c new file mode 100644 index 0000000..93fc7a6 --- /dev/null +++ b/src/gpio.c @@ -0,0 +1,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; +} |