Use available info from register rather than shift and mask in gpio_read()
[lpc82x] / drivers / gpio.c
1 /****************************************************************************
2  *  drivers/gpio.c
3  *
4  * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *************************************************************************** */
21 /***************************************************************************** */
22 /*                GPIOs and GPIO Interrupts                                    */
23 /***************************************************************************** */
25 /* Driver for GPIO configuration and access (including GPIO interrupts) on the LPC82x.
26  * Refer to LPC82x documentation (UM10800.pdf) for more information.
27  */
30 #include "core/system.h"
31 #include "core/pio.h"
32 #include "drivers/gpio.h"
33 #include "lib/errno.h"
37 /***************************************************************************** */
38 /*   GPIO setup   */
40 void gpio_on(void)
41 {
42         /* Provide power to GPIO control blocks */
43         subsystem_power(LPC_SYS_ABH_CLK_CTRL_GPIO, 1);
44 }
45 void gpio_off(void)
46 {
47         /* Remove power from GPIO control blocks */
48         subsystem_power(LPC_SYS_ABH_CLK_CTRL_GPIO, 0);
49 }
51 /*
52  * This function calls the config_pio() function for the gpio with the given
53  * mode, configures the direction of the pin and sets the initial state.
54  */
55 void config_gpio(const struct pio* gpio, uint32_t mode, uint8_t dir, uint8_t ini_val)
56 {
57         struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0);
59         /* Configure as GPIO */
60         config_pio(gpio, NULL, mode);
62         if (dir == GPIO_DIR_IN) {
63                 gpio_port->data_dir &= ~(1 << gpio->pin);
64         } else {
65                 gpio_port->data_dir |= (1 << gpio->pin);
66                 if (ini_val == 0) {
67                         gpio_port->clear = (1 << gpio->pin);
68                 } else {
69                         gpio_port->set = (1 << gpio->pin);
70                 }
71         }
72 }
74 /***************************************************************************** */
75 /* GPIO Interrupts Callbacks */
76 #define NB_PININT_INTERRUPTS   8
77 static void (*gpio_callbacks[NB_PININT_INTERRUPTS]) (uint32_t) = {0};
79 /* Return the pin interrupt number if OK, or a negative value in case of error */
80 int set_gpio_callback(void (*callback) (uint32_t), const struct pio* gpio, uint8_t sense)
81 {
82         struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0);
83         struct lpc_pin_int* pinint = LPC_GPIO_INTERRUPTS;
84         struct lpc_sys_config* sys_conf = LPC_SYS_CONFIG;
85         uint32_t irq = 0;
87         if (gpio->pin >= PORT0_NB_PINS)
88                 return -EINVAL;
90         /* Get the next available interrupt and register the callback if empty slot found. */
91         for (irq = 0; irq < NB_PININT_INTERRUPTS; irq++) {
92                 if (gpio_callbacks[irq] == NULL) {
93                         gpio_callbacks[irq] = callback;
94                         break;
95                 }
96         }
97         /* If not found, return. */
98         if (irq == NB_PININT_INTERRUPTS) {
99                 return -ENODEV;
100         }
102         /* Power on the Pin interrupt subsystem */
103         subsystem_power(LPC_SYS_ABH_CLK_CTRL_GPIO, 1);
105         /* Configure the pin as interrupt source */
106         gpio_port->data_dir &= ~(1 << gpio->pin); /* Input */
107         config_pio(gpio, NULL, 0);
108         sys_conf->pin_int_sel[irq] = gpio->pin;
110         /* Setup the înint as requested */
111         switch (sense) {
112                 case EDGES_BOTH:
113                         pinint->mode &= ~(0x01 << irq);
114                         pinint->lvl_rising_set_en = (0x01 << irq);
115                         pinint->lvl_falling_set_en = (0x01 << irq);
116                         break;
117                 case EDGE_RISING:
118                         pinint->mode &= ~(0x01 << irq);
119                         pinint->lvl_rising_set_en = (0x01 << irq);
120                         pinint->lvl_falling_clr_en = (0x01 << irq);
121                         break;
122                 case EDGE_FALLING:
123                         pinint->mode &= ~(0x01 << irq);
124                         pinint->lvl_rising_clr_en = (0x01 << irq);
125                         pinint->lvl_falling_set_en = (0x01 << irq);
126                         break;
127                 case LEVEL_LOW:
128                         pinint->mode |= (0x01 << irq);
129                         pinint->lvl_rising_set_en = (0x01 << irq);
130                         pinint->lvl_falling_clr_en = (0x01 << irq); /* Active low */
131                         break;
132                 case LEVEL_HIGH:
133                         pinint->mode |= (0x01 << irq);
134                         pinint->lvl_rising_set_en = (0x01 << irq);
135                         pinint->lvl_falling_set_en = (0x01 << irq); /* Active high */
136                         break;
137                 default: /* Not handled, do not activate the interrupt */
138                         return -EINVAL;
139         }
140         NVIC_EnableIRQ(irq + PININT0_IRQ);
141         return irq;
144 void remove_gpio_callback(unsigned int irq)
146         struct lpc_pin_int* pinint = LPC_GPIO_INTERRUPTS;
147         struct lpc_sys_config* sys_conf = LPC_SYS_CONFIG;
149         /* Remove the handler */
150         if (irq > NB_PININT_INTERRUPTS)
151                 return;
152         gpio_callbacks[irq] = NULL;
154         /* And disable the interrupt */
155         sys_conf->pin_int_sel[irq] = 0xFF;
156         pinint->mode &= ~(0x01 << irq);
157         pinint->lvl_rising_clr_en = (0x01 << irq);
158         pinint->lvl_falling_clr_en = (0x01 << irq);
160         NVIC_DisableIRQ(irq + PININT0_IRQ);
164 /* Interrupt Handlers */
165 #define DECLARE_PIO_INT_HANDLER(x) \
166         void PININT_ ## x ## _Handler(void) \
167         { \
168                 struct lpc_pin_int* pinint = LPC_GPIO_INTERRUPTS; \
169                 if (gpio_callbacks[x] != NULL) { \
170                         gpio_callbacks[x](x); \
171                 } \
172                 pinint->status = (0x01 << x); \
173         }
175 DECLARE_PIO_INT_HANDLER(0);
176 DECLARE_PIO_INT_HANDLER(1);
177 DECLARE_PIO_INT_HANDLER(2);
178 DECLARE_PIO_INT_HANDLER(3);
179 DECLARE_PIO_INT_HANDLER(4);
180 DECLARE_PIO_INT_HANDLER(5);
181 DECLARE_PIO_INT_HANDLER(6);
182 DECLARE_PIO_INT_HANDLER(7);