#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; }