altos/lpc: Add pin interrupt driver
[fw/altos] / src / lpc / ao_exti_lpc.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_exti.h>
20
21 #define LPC_NUM_PINS    56
22 #define LPC_NUM_PINT    8
23
24 static void     (*ao_exti_callback[LPC_NUM_PINT])(void);
25
26 static uint8_t  ao_pint_map[LPC_NUM_PINS];
27 static uint8_t  ao_pint_mode[LPC_NUM_PINS];
28 static uint8_t  ao_pint_inuse;
29 static uint8_t  ao_pint_enabled;
30
31 static void
32 ao_exti_isr(uint8_t pint)
33 {
34         uint8_t mask = 1 << pint;
35
36         if (lpc_gpio_pin.ist & mask) {
37                 lpc_gpio_pin.ist = mask;
38
39                 (*ao_exti_callback) ();
40         }
41 }
42
43 #define pin_isr(n)      void lpc_pin_int ## n ## _isr(void) { ao_exti_isr(n); }
44 pin_isr(0)
45 pin_isr(1)
46 pin_isr(2)
47 pin_isr(3)
48 pin_isr(4)
49 pin_isr(5)
50 pin_isr(6)
51 pin_isr(7)
52
53 #define pin_id(port,pin)        ((port) * 24 + (pin));
54
55 void
56 ao_exti_setup (uint8_t port, uint8_t pin, uint8_t mode, void (*callback)(void)) {
57         uint8_t         id = pin_id(port,pin);
58         uint8_t         pint;
59         uint32_t        mask;
60         uint8_t         prio;
61
62         for (pint = 0; pint < LPC_NUM_PINT; pint++)
63                 if ((ao_pint_inuse & (1 << pint)) == 0)
64                         break;
65         if (pint == LPC_NUM_PINT)
66                 ao_panic(AO_PANIC_EXTI);
67
68         mask = (1 << pint);
69         ao_pint_inuse |= mask;
70         ao_pint_enabled &= ~mask;
71         
72         ao_pint_map[id] = pint;
73         ao_exti_callback[pin] = callback;
74
75         /* configure gpio to interrupt routing */
76         lpc_scb.pintsel[pint] = id;
77
78         ao_enable_input(port, pin, mode);
79
80         /* Set edge triggered */
81         lpc_gpio_pin.isel &= ~mask;
82
83         ao_exti_set_mode(port, pin, mode);
84
85         /* Set interrupt mask and rising/falling mode */
86
87         prio = AO_LPC_NVIC_MED_PRIORITY;
88         if (mode & AO_EXTI_PRIORITY_LOW)
89                 prio = AO_LPC_NVIC_LOW_PRIORITY;
90         else if (mode & AO_EXTI_PRIORITY_HIGH)
91                 prio = AO_LPC_NVIC_HIGH_PRIORITY;
92
93         /* Set priority and enable */
94         lpc_nvic_set_priority(LPC_ISR_PIN_INT0_POS + pint, prio);
95         lpc_nvic_set_enable(LPC_ISR_PIN_INT0_POS + pint);
96 }
97
98 void
99 ao_exti_set_mode(uint8_t port, uint8_t pin, uint8_t mode) {
100         uint8_t         id = pin_id(port,pin);
101         uint8_t         pint = ao_pint_map[id];
102         uint8_t         mask = 1 << pint;
103
104         ao_pint_mode[pint] = mode;
105
106         if (mode & AO_EXTI_MODE_RISING)
107                 lpc_gpio_pin.sienr = mask;
108         else
109                 lpc_gpio_pin.cienr = mask;
110                 
111         if (mode & AO_EXTI_MODE_FALLING)
112                 lpc_gpio_pin.sienf = mask;
113         else
114                 lpc_gpio_pin.cienf = mask;
115 }
116
117 void
118 ao_exti_set_callback(uint8_t port, uint8_t pin, void (*callback)()) {
119         uint8_t         id = pin_id(port,pin);
120         uint8_t         pint = ao_pint_map[id];
121
122         ao_exti_callback[pint] = callback;
123 }
124
125 void
126 ao_exti_enable(uint8_t port, uint8_t pin)
127 {
128         uint8_t         id = pin_id(port,pin);
129         uint8_t         pint = ao_pint_map[id];
130         uint8_t         mask = 1 << pint;
131
132         ao_pint_enabled |= mask;
133         ao_exti_set_mode(port, pin, ao_pint_mode[pint]);
134 }
135
136 void
137 ao_exti_disable(uint8_t port, uint8_t pin) {
138         uint8_t         id = pin_id(port,pin);
139         uint8_t         pint = ao_pint_map[id];
140         uint8_t         mask = 1 << pint;
141
142         ao_pint_enabled &= ~mask;
143         lpc_gpio_pin.cienr = mask;
144         lpc_gpio_pin.cienf = mask;
145 }
146
147 void
148 ao_exti_init(void)
149 {
150         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_PINT);
151 }