/*! * Copyright (C) 2001-2010 by egnite Software GmbH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /*! * $Id: gpio.c,v 1.4 2009/01/18 16:45:28 haraldkipp Exp $ */ #include #include #include #include #include /* * Convert the external bank to the AVR32 representation. */ static int get_avr32_bank(int bank) { int avr32_bank = 0; switch (bank) { default: case NUTGPIO_PORT: case NUTGPIO_PORTA: avr32_bank = 0; break; case NUTGPIO_PORTB: avr32_bank = 1; break; case NUTGPIO_PORTC: avr32_bank = 2; break; case NUTGPIO_PORTD: avr32_bank = 3; break; case NUTGPIO_PORTE: avr32_bank = 4; break; case NUTGPIO_PORTF: avr32_bank = 5; break; case NUTGPIO_PORTG: avr32_bank = 6; break; case NUTGPIO_PORTH: avr32_bank = 7; break; case NUTGPIO_PORTI: avr32_bank = 8; break; case NUTGPIO_PORTJ: avr32_bank = 9; break; case NUTGPIO_PORTK: avr32_bank = 10; break; case NUTGPIO_PORTL: avr32_bank = 11; break; } if (avr32_bank >= AVR32_GPIO_PORT_LENGTH) { avr32_bank = AVR32_GPIO_PORT_LENGTH - 1; } return (avr32_bank); } /*! \internal Switch GPIO Peripheral Mux to specified \a function where function is one of the GPIO_CFG_PERIPHERALx values. Note that GPIO_CFG_PERIPHERALx values are mutually exclusive. */ static int enable_module_pin(int avr32_bank, int mask, unsigned int function) { volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; // Enable the correct function. switch (function) { case GPIO_CFG_PERIPHERAL0: // A function. gpio_port->pmr0c = mask; gpio_port->pmr1c = mask; #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = mask; #endif break; case GPIO_CFG_PERIPHERAL1: // B function. gpio_port->pmr0s = mask; gpio_port->pmr1c = mask; #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = mask; #endif break; case GPIO_CFG_PERIPHERAL2: // C function. gpio_port->pmr0c = mask; gpio_port->pmr1s = mask; #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = mask; #endif break; default: return -1; } // Disable GPIO control. gpio_port->gperc = mask; return 0; } /*! \internal Switch GPIO Peripheral Mux to specified \a function where function is one of the AVR32 compiler header values. This function is intended to be used by drivers */ void gpio_enable_module_pin(unsigned int pin, unsigned int function) { unsigned int peripheral = 0; switch ( function ) { case 0: peripheral = GPIO_CFG_PERIPHERAL0; break; case 1: peripheral = GPIO_CFG_PERIPHERAL1; break; case 2: peripheral = GPIO_CFG_PERIPHERAL2; break; default: while(1); // Unrecognized peripheral choice break; } enable_module_pin(pin >> 5, _BV(pin & 0x1F), peripheral); } int avr32_enable_module_pin(unsigned int pin, unsigned int function) { volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5]; // Enable the correct function. switch (function) { case 0: // A function. gpio_port->pmr0c = 1 << (pin & 0x1F); gpio_port->pmr1c = 1 << (pin & 0x1F); #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = 1 << (pin & 0x1F); #endif break; case 1: // B function. gpio_port->pmr0s = 1 << (pin & 0x1F); gpio_port->pmr1c = 1 << (pin & 0x1F); #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = 1 << (pin & 0x1F); #endif break; case 2: // C function. gpio_port->pmr0c = 1 << (pin & 0x1F); gpio_port->pmr1s = 1 << (pin & 0x1F); #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = 1 << (pin & 0x1F); #endif break; case 3: // D function. gpio_port->pmr0s = 1 << (pin & 0x1F); gpio_port->pmr1s = 1 << (pin & 0x1F); #ifdef AVR32_GPIO_PMR2C gpio_port->pmr2c = 1 << (pin & 0x1F); #endif break; #ifdef AVR32_GPIO_PMR2C case 4: // E function. gpio_port->pmr0c = 1 << (pin & 0x1F); gpio_port->pmr1c = 1 << (pin & 0x1F); gpio_port->pmr2s = 1 << (pin & 0x1F); break; case 5: // F function. gpio_port->pmr0s = 1 << (pin & 0x1F); gpio_port->pmr1c = 1 << (pin & 0x1F); gpio_port->pmr2s = 1 << (pin & 0x1F); break; case 6: // G function. gpio_port->pmr0c = 1 << (pin & 0x1F); gpio_port->pmr1s = 1 << (pin & 0x1F); gpio_port->pmr2s = 1 << (pin & 0x1F); break; case 7: // H function. gpio_port->pmr0s = 1 << (pin & 0x1F); gpio_port->pmr1s = 1 << (pin & 0x1F); gpio_port->pmr2s = 1 << (pin & 0x1F); break; #endif default: return -1; } // Disable GPIO control. gpio_port->gperc = 1 << (pin & 0x1F); return 0; } /*! * \brief Get pin level. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. * * \return 1 if the pin level is high. 0 is returned if the pin level * is low or if the pin is undefined. */ int GpioPinGet(int bank, int bit) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; return (gpio_port->pvr >> (bit & 0x1F)) & 1; } /*! * \brief Set pin level to low. * * Trying to set undefined pins is silently ignored. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. */ void GpioPinSetLow(int bank, int bit) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; gpio_port->ovrc = 1 << (bit & 0x1F); // Value to be driven on the I/O line: 0. } /*! * \brief Set pin level to high. * * Trying to set undefined pins is silently ignored. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. */ void GpioPinSetHigh(int bank, int bit) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; gpio_port->ovrs = 1 << (bit & 0x1F); // Value to be driven on the I/O line: 1. } /*! * \brief Set pin level. * * Trying to set undefined pins is silently ignored. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. * \param value Level, 0 for low or any other value for high. */ void GpioPinSet(int bank, int bit, int value) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; if (value) { gpio_port->ovrs = 1 << (bit & 0x1F); // Value to be driven on the I/O line: 1. } else { gpio_port->ovrc = 1 << (bit & 0x1F); // Value to be driven on the I/O line: 0. } } /*! * \brief Get all pin levels of a specified bank/port. * * \param bank GPIO bank/port number. * * \return Pin levels. 0 is returned for unknown banks and pins. */ unsigned int GpioPortGet(int bank) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; return gpio_port->pvr; } /*! * \brief Set multiple pin levels of a bank/port to low. * * \param bank GPIO bank/port number. * \param mask Pin levels are set to low, if the corresponding * bit in this mask is 1. * * \return Levels. */ void GpioPortSetLow(int bank, unsigned int mask) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; gpio_port->ovrc = mask; // Value to be driven on the I/O line: 0. } /*! * \brief Set multiple pin levels of a bank/port to high. * * Trying to set undefined ports is silently ignored. * * \param bank GPIO bank/port number. * \param mask Pin levels are set to high, if the corresponding * bit in this mask is 1. */ void GpioPortSetHigh(int bank, unsigned int mask) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; gpio_port->ovrs = mask; // Value to be driven on the I/O line: 1. } /*! * \brief Set all pin levels of a bank/port. * * This routine can be used to simultaneously set all pins of a specific * port. However, in some implementations the high bit values will be * set before the low bit values. If this is a problem, you should use * GpioPortSetHigh() and GpioPortSetLow(). * * \param bank GPIO bank/port number. * \param value Pin levels are set to high, if the corresponding * bit in this mask is 1. All other pin levels are * set to low. */ void GpioPortSet(int bank, unsigned int value) { int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; gpio_port->ovr = value; } /*! * \brief Get pin configuration. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. * * \return Attribute flags of the pin. */ uint32_t GpioPinConfigGet(int bank, int bit) { uint32_t rc = 0; uint8_t is_gpio = 0; int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; if ((gpio_port->gper & _BV(bit)) == 0) { rc |= GPIO_CFG_DISABLED; } else { is_gpio = 1; } if (gpio_port->oder & _BV(bit)) { rc |= GPIO_CFG_OUTPUT; } if (gpio_port->gfer & _BV(bit)) { rc |= GPIO_CFG_DEBOUNCE; } if (gpio_port->puer & _BV(bit)) { rc |= GPIO_CFG_PULLUP; } if (is_gpio == 0) { if (gpio_port->pmr1 & _BV(bit)) { if (gpio_port->pmr0 & _BV(bit)) { rc |= GPIO_CFG_PERIPHERAL3; } else { rc |= GPIO_CFG_PERIPHERAL2; } } else { if (gpio_port->pmr0 & _BV(bit)) { rc |= GPIO_CFG_PERIPHERAL1; } else { rc |= GPIO_CFG_PERIPHERAL0; } } } return rc; } /*! * \brief Set port wide pin configuration. * * \note This function does not check for undefined ports and pins or * invalid attributes. If this is required, use GpioPinConfigSet(). * * \param bank GPIO bank/port number. * \param mask The given attributes are set for a specific pin, if the * corresponding bit in this mask is 1. * \param flags Attribute flags to set. * * \return Always 0. */ int GpioPortConfigSet(int bank, unsigned int mask, uint32_t flags) { #define PERIPHERALS_MASK (GPIO_CFG_PERIPHERAL0|GPIO_CFG_PERIPHERAL1|GPIO_CFG_PERIPHERAL2|GPIO_CFG_PERIPHERAL3) int avr32_bank = get_avr32_bank(bank); volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[avr32_bank]; if (flags & GPIO_CFG_PULLUP) { gpio_port->puers = mask; } else { gpio_port->puerc = mask; } if (flags & GPIO_CFG_DEBOUNCE) { gpio_port->gfers = mask; } else { gpio_port->gferc = mask; } if (flags & GPIO_CFG_OUTPUT) { gpio_port->oders = mask; } else { gpio_port->oderc = mask; } if (flags & PERIPHERALS_MASK) { enable_module_pin(avr32_bank, mask, flags & PERIPHERALS_MASK); } if (flags & (GPIO_CFG_DISABLED | PERIPHERALS_MASK)) { gpio_port->gperc = mask; } else { gpio_port->gpers = mask; } #undef PERIPHERALS_MASK return 0; } /*! * \brief Set pin configuration. * * Applications may also use this function to make sure, that a specific * attribute is available for a specific pin. * * \note GPIO pins are typically initialized to a safe state after power * up. This routine is not able to determine the consequences of * changing pin configurations. In the worst case you may permanently * damage your hardware by bad pin settings. * * \param bank GPIO bank/port number. * \param bit Bit number of the specified bank/port. * \param flags Attribute flags. * * \return 0 if all attributes had been set, -1 otherwise. */ int GpioPinConfigSet(int bank, int bit, uint32_t flags) { GpioPortConfigSet(bank, _BV(bit), flags); /* Check the result. */ if (GpioPinConfigGet(bank, bit) != flags) { return -1; } return 0; } /*! * \brief Register a GPIO pin interrupt handler. * * Generating interrupts on GPIO pin changes is not supported on all * platforms. In this case dedicated external interrupt pins may * be used with NutRegisterIrqHandler(). * * Interrupts are triggered on rising and falling edges. Level triggering * or triggering on specific edges is not supported. * * After registering, interrupts are disabled. Calling GpioIrqEnable() * is required to activate the interrupt. * * The following code fragment registers an interrupt handler which is * called on each change of bit 4 of the first GPIO port: * \code * #include * * static void PinChange(void *arg) * { * ... * } * * { * ... * GpioPinConfigSet(0, 4, GPIO_CFG_PULLUP); * GpioRegisterIrqHandler(&sig_GPIO, 4, PinChange, NULL); * GpioIrqEnable(&sig_GPIO, 30); * ... * } * \endcode * * \param sig Bank/port interrupt to be associated with this handler. * \param bit Bit number of the specified bank/port. * \param handler This routine will be called by Nut/OS, when the specified * pin changes its state. * \param arg Argument to be passed to the interrupt handler routine. * * \return 0 on success, -1 otherwise. */ int GpioRegisterIrqHandler(GPIO_SIGNAL * sig, int bit, void (*handler) (void *), void *arg) { int rc = 0; if (sig->ios_vector == 0) { /* This is the first call. Allocate the vector table. */ sig->ios_vector = malloc(sizeof(GPIO_VECTOR) * 32); if (sig->ios_vector) { memset(sig->ios_vector, 0, sizeof(GPIO_VECTOR) * 32); if (sig->ios_ctl) rc = (sig->ios_ctl) (NUT_IRQCTL_INIT, NULL, bit); } else { rc = -1; } } sig->ios_vector[bit].iov_handler = handler; sig->ios_vector[bit].iov_arg = arg; return rc; } /*! * \brief Enable a specified GPIO interrupt. * * A related interrupt handler must have been registered before calling * this function. See GpioRegisterIrqHandler(). * * \param sig Interrupt to enable. * \param bit Bit number of the specified bank/port. * * \return 0 on success, -1 otherwise. */ int GpioIrqEnable(GPIO_SIGNAL * sig, int bit) { return (sig->ios_ctl) (NUT_IRQCTL_ENABLE, NULL, bit); } /*! * \brief Disable a specified GPIO interrupt. * * \param sig Interrupt to disable. * \param bit Bit number of the specified bank/port. * * \return 0 on success, -1 otherwise. */ int GpioIrqDisable(GPIO_SIGNAL * sig, int bit) { return (sig->ios_ctl) (NUT_IRQCTL_DISABLE, NULL, bit); } /*! * \brief Set a specified GPIO interrupt mode. * * \param sig Interrupt to change mode. * \param bit Bit number of the specified bank/port. * \param mode Possible choices: * NUT_IRQMODE_RISINGEDGE * NUT_IRQMODE_FALLINGEDGE * NUT_IRQMODE_BOTHEDGE * * \return 0 on success, -1 otherwise. */ int GpioIrqSetMode(GPIO_SIGNAL* sig, int bit, int mode) { return (sig->ios_ctl) (NUT_IRQCTL_SETMODE, &mode, bit); }