aboutsummaryrefslogtreecommitdiff
path: root/src/gpio.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-12-08 14:22:05 -0700
committerJosh Rahm <joshuarahm@gmail.com>2024-12-08 14:22:05 -0700
commitf89f75b5616de99865448f41b068a2783cd3648e (patch)
tree1e7599b8915d3fbb9a1a10892ba58fd44bea3f5d /src/gpio.c
parent546a5ccdba66dd8d8c19ce6d8486f46c84637cf2 (diff)
downloadch573-f89f75b5616de99865448f41b068a2783cd3648e.tar.gz
ch573-f89f75b5616de99865448f41b068a2783cd3648e.tar.bz2
ch573-f89f75b5616de99865448f41b068a2783cd3648e.zip
Cleaned up GPIO code quite a bit.
Diffstat (limited to 'src/gpio.c')
-rw-r--r--src/gpio.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/gpio.c b/src/gpio.c
new file mode 100644
index 0000000..e016179
--- /dev/null
+++ b/src/gpio.c
@@ -0,0 +1,168 @@
+#include "gpio.h"
+
+#include "ch573/pfic.h"
+#include "isr_vector.h"
+
+void configure_gpio(gpio_pin_t pin, gpio_config_t cfg)
+{
+ int ipin = pin;
+ struct ch573_gpio__gpio_port_t* port;
+ if (pin & IS_PORT_B) {
+ port = GPIO_PORT_B;
+ ipin &= ~IS_PORT_B;
+ } else {
+ port = GPIO_PORT_A;
+ }
+
+ switch (cfg) {
+ case PIN_CFG_INPUT_FLOATING:
+ GPIO_PORT.dir.set(port, DIR_IN, ipin);
+ GPIO_PORT.pu.set(port, DISABLED, ipin);
+ GPIO_PORT.pd_drv.set(port, 0, ipin);
+ break;
+ case PIN_CFG_INPUT_PULL_UP:
+ GPIO_PORT.dir.set(port, DIR_IN, ipin);
+ GPIO_PORT.pu.set(port, ENABLED, ipin);
+ GPIO_PORT.pd_drv.set(port, 0, ipin);
+ break;
+ case PIN_CFG_INPUT_PULL_DOWN:
+ GPIO_PORT.dir.set(port, DIR_IN, ipin);
+ GPIO_PORT.pu.set(port, DISABLED, ipin);
+ GPIO_PORT.pd_drv.set(port, 1, ipin);
+ break;
+ case PIN_CFG_OUTPUT_5mA:
+ GPIO_PORT.dir.set(port, DIR_OUT, ipin);
+ GPIO_PORT.pu.set(port, DISABLED, ipin);
+ GPIO_PORT.pd_drv.set(port, 0, ipin);
+ break;
+ case PIN_CFG_OUTPUT_20mA:
+ GPIO_PORT.dir.set(port, DIR_OUT, ipin);
+ GPIO_PORT.pu.set(port, DISABLED, ipin);
+ GPIO_PORT.pd_drv.set(port, 1, ipin);
+ break;
+ }
+}
+
+int read_gpio(gpio_pin_t pin)
+{
+ int ipin = pin;
+ struct ch573_gpio__gpio_port_t* port;
+ if (pin & IS_PORT_B) {
+ port = GPIO_PORT_B;
+ ipin &= ~IS_PORT_B;
+ } else {
+ port = GPIO_PORT_A;
+ }
+
+ return !!GPIO_PORT.pin.in.get(port, ipin);
+}
+
+void set_gpio(gpio_pin_t pin, int high)
+{
+ int ipin = pin;
+ struct ch573_gpio__gpio_port_t* port;
+ if (pin & IS_PORT_B) {
+ port = GPIO_PORT_B;
+ ipin &= ~IS_PORT_B;
+ } else {
+ port = GPIO_PORT_A;
+ }
+
+ GPIO_PORT.out.set(port, !!high, ipin);
+}
+
+void enable_alternate_function(alternate_function_t afn)
+{
+ GPIO_I.pin_alternate.set(GPIO, 1 << afn);
+}
+
+#define PFIC ch573_pfic__pfic
+static void enable_gpio_pfic(int irq)
+{
+ PFIC->interrupt_priority_threshold = 0x10;
+ PFIC->interrupt_enable |= irq;
+}
+
+void enable_gpio_interrupt(gpio_pin_t pin, interrupt_mode_t int_mode)
+{
+ int ipin = pin;
+ struct ch573_gpio__gpio_port_t* port;
+
+ volatile uint16_t* port_int_mode;
+ volatile uint16_t* port_int_enable;
+ volatile uint16_t* port_int_flag;
+
+ if (pin & IS_PORT_B) {
+ port = GPIO_PORT_B;
+ ipin &= ~IS_PORT_B;
+ port_int_mode = &GPIO->pb_interrupt_mode;
+ port_int_flag = &GPIO->pb_interrupt_flag;
+ port_int_enable = &GPIO->pb_interrupt_enable;
+
+ enable_gpio_pfic(IRQ_GpioB);
+ } else {
+ port = GPIO_PORT_A;
+ port_int_mode = &GPIO->pa_interrupt_mode;
+ port_int_flag = &GPIO->pa_interrupt_flag;
+ port_int_enable = &GPIO->pa_interrupt_enable;
+
+ enable_gpio_pfic(IRQ_GpioA);
+ }
+
+ switch (int_mode) {
+ case INT_MODE_FALLING_EDGE:
+ *port_int_mode |= 1 << ipin;
+ GPIO_PORT.clr.set(port, 1 << ipin);
+ break;
+
+ case INT_MODE_RISING_EDGE:
+ *port_int_mode |= 1 << ipin;
+ GPIO_PORT.out.set(port, 1, ipin);
+ break;
+
+ case INT_MODE_LEVEL_HIGH:
+ *port_int_mode &= ~(1 << ipin);
+ GPIO_PORT.out.set(port, 1, ipin);
+ break;
+
+ case INT_MODE_LEVEL_LOW:
+ *port_int_mode &= ~(1 << ipin);
+ GPIO_PORT.clr.set(port, 1 << ipin);
+ break;
+ }
+
+ *port_int_flag = 1 << ipin;
+ *port_int_enable |= 1 << ipin;
+}
+
+extern gpio_callback_t GPIO_LISTENERS_START;
+extern gpio_callback_t GPIO_LISTENERS_END;
+void fire_irq(volatile uint16_t* int_flag_ptr, int is_port_b)
+{
+ gpio_callback_t* cur = &GPIO_LISTENERS_START;
+
+ uint16_t pin = 0;
+ uint16_t intflag = *int_flag_ptr;
+
+ for (pin = 0; pin < 16; ++pin) {
+ if (intflag & (1 << pin)) {
+ // If this pin triggered an interrupt, fire the GPIO listeners.
+ for (cur = &GPIO_LISTENERS_START; cur < &GPIO_LISTENERS_END; ++cur) {
+ (*cur)(pin | is_port_b);
+ }
+ *int_flag_ptr |= 1 << pin; // Clear the interrupt for this pin.
+ }
+ }
+
+ *int_flag_ptr = 0xffff;
+}
+
+IRQ(gpio_b)
+{
+ fire_irq(&GPIO->pb_interrupt_flag, IS_PORT_B);
+}
+
+IRQ(gpio_a)
+{
+ fire_irq(&GPIO->pa_interrupt_flag, 0);
+}