aboutsummaryrefslogtreecommitdiff
path: root/src/gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpio.c')
-rw-r--r--src/gpio.c254
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;
+}