aboutsummaryrefslogtreecommitdiff
path: root/src/gpio.c
blob: e016179feb224f1a4e1d16ce94a542a2e59283a9 (plain) (blame)
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
#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);
}