/* * * Macros for external SMP-safe access to the PMC MSP71xx reference * board GPIO pins * * Copyright 2010 PMC-Sierra, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __MSP_GPIO_MACROS_H__ #define __MSP_GPIO_MACROS_H__ #include #include #ifdef CONFIG_PMC_MSP7120_GW #define MSP_NUM_GPIOS 20 #else #define MSP_NUM_GPIOS 28 #endif /* -- GPIO Enumerations -- */ enum msp_gpio_data { MSP_GPIO_LO = 0, MSP_GPIO_HI = 1, MSP_GPIO_NONE, /* Special - Means pin is out of range */ MSP_GPIO_TOGGLE, /* Special - Sets pin to opposite */ }; enum msp_gpio_mode { MSP_GPIO_INPUT = 0x0, /* MSP_GPIO_ INTERRUPT = 0x1, Not supported yet */ MSP_GPIO_UART_INPUT = 0x2, /* Only GPIO 4 or 5 */ MSP_GPIO_OUTPUT = 0x8, MSP_GPIO_UART_OUTPUT = 0x9, /* Only GPIO 2 or 3 */ MSP_GPIO_PERIF_TIMERA = 0x9, /* Only GPIO 0 or 1 */ MSP_GPIO_PERIF_TIMERB = 0xa, /* Only GPIO 0 or 1 */ MSP_GPIO_UNKNOWN = 0xb, /* No such GPIO or mode */ }; /* -- Static Tables -- */ /* Maps pins to data register */ static volatile u32 * const MSP_GPIO_DATA_REGISTER[] = { /* GPIO 0 and 1 on the first register */ GPIO_DATA1_REG, GPIO_DATA1_REG, /* GPIO 2, 3, 4, and 5 on the second register */ GPIO_DATA2_REG, GPIO_DATA2_REG, GPIO_DATA2_REG, GPIO_DATA2_REG, /* GPIO 6, 7, 8, and 9 on the third register */ GPIO_DATA3_REG, GPIO_DATA3_REG, GPIO_DATA3_REG, GPIO_DATA3_REG, /* GPIO 10, 11, 12, 13, 14, and 15 on the fourth register */ GPIO_DATA4_REG, GPIO_DATA4_REG, GPIO_DATA4_REG, GPIO_DATA4_REG, GPIO_DATA4_REG, GPIO_DATA4_REG, /* GPIO 16 - 23 on the first strange EXTENDED register */ EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, /* GPIO 24 - 27 on the second strange EXTENDED register */ EXTENDED_GPIO2_REG, EXTENDED_GPIO2_REG, EXTENDED_GPIO2_REG, EXTENDED_GPIO2_REG, }; /* Maps pins to mode register */ static volatile u32 * const MSP_GPIO_MODE_REGISTER[] = { /* GPIO 0 and 1 on the first register */ GPIO_CFG1_REG, GPIO_CFG1_REG, /* GPIO 2, 3, 4, and 5 on the second register */ GPIO_CFG2_REG, GPIO_CFG2_REG, GPIO_CFG2_REG, GPIO_CFG2_REG, /* GPIO 6, 7, 8, and 9 on the third register */ GPIO_CFG3_REG, GPIO_CFG3_REG, GPIO_CFG3_REG, GPIO_CFG3_REG, /* GPIO 10, 11, 12, 13, 14, and 15 on the fourth register */ GPIO_CFG4_REG, GPIO_CFG4_REG, GPIO_CFG4_REG, GPIO_CFG4_REG, GPIO_CFG4_REG, GPIO_CFG4_REG, /* GPIO 16 - 23 on the first strange EXTENDED register */ EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, EXTENDED_GPIO1_REG, /* GPIO 24 - 27 on the second strange EXTENDED register */ EXTENDED_GPIO2_REG, EXTENDED_GPIO2_REG, EXTENDED_GPIO2_REG, EXTENDED_GPIO2_REG, }; /* Maps 'basic' pins to relative offset from 0 per register */ static int MSP_GPIO_OFFSET[] = { /* GPIO 0 and 1 on the first register */ 0, 0, /* GPIO 2, 3, 4, and 5 on the second register */ 2, 2, 2, 2, /* GPIO 6, 7, 8, and 9 on the third register */ 6, 6, 6, 6, /* GPIO 10, 11, 12, 13, 14, and 15 on the fourth register */ 10, 10, 10, 10, 10, 10, }; /* Maps MODE to allowed pin mask */ static unsigned int MSP_GPIO_MODE_ALLOWED[] = { 0xffffffff, /* Mode 0 - INPUT */ 0x00000, /* Mode 1 - INTERRUPT */ 0x00030, /* Mode 2 - UART_INPUT (GPIO 4, 5)*/ 0, 0, 0, 0, 0, /* Modes 3, 4, 5, 6, and 7 are reserved */ 0xffffffff, /* Mode 8 - OUTPUT */ 0x0000f, /* Mode 9 - UART_OUTPUT/ PERF_TIMERA (GPIO 0, 1, 2, 3) */ 0x00003, /* Mode a - PERF_TIMERB (GPIO 0, 1) */ 0x00000, /* Mode b - Not really a mode! */ }; /* -- Bit masks -- */ /* This gives you the 'register relative offset gpio' number */ #define OFFSET_GPIO_NUMBER(gpio) (gpio - MSP_GPIO_OFFSET[gpio]) /* These take the 'register relative offset gpio' number */ #define BASIC_DATA_REG_MASK(ogpio) (1 << ogpio) #define BASIC_MODE_REG_VALUE(mode, ogpio) \ (mode << BASIC_MODE_REG_SHIFT(ogpio)) #define BASIC_MODE_REG_MASK(ogpio) \ BASIC_MODE_REG_VALUE(0xf, ogpio) #define BASIC_MODE_REG_SHIFT(ogpio) (ogpio * 4) #define BASIC_MODE_REG_FROM_REG(data, ogpio) \ ((data & BASIC_MODE_REG_MASK(ogpio)) >> BASIC_MODE_REG_SHIFT(ogpio)) /* These take the actual GPIO number (0 through 15) */ #define BASIC_DATA_MASK(gpio) \ BASIC_DATA_REG_MASK(OFFSET_GPIO_NUMBER(gpio)) #define BASIC_MODE_MASK(gpio) \ BASIC_MODE_REG_MASK(OFFSET_GPIO_NUMBER(gpio)) #define BASIC_MODE(mode, gpio) \ BASIC_MODE_REG_VALUE(mode, OFFSET_GPIO_NUMBER(gpio)) #define BASIC_MODE_SHIFT(gpio) \ BASIC_MODE_REG_SHIFT(OFFSET_GPIO_NUMBER(gpio)) #define BASIC_MODE_FROM_REG(data, gpio) \ BASIC_MODE_REG_FROM_REG(data, OFFSET_GPIO_NUMBER(gpio)) /* * Each extended GPIO register is 32 bits long and is responsible for up to * eight GPIOs. The least significant 16 bits contain the set and clear bit * pair for each of the GPIOs. The most significant 16 bits contain the * disable and enable bit pair for each of the GPIOs. For example, the * extended GPIO reg for GPIOs 16-23 is as follows: * * 31: GPIO23_DISABLE * ... * 19: GPIO17_DISABLE * 18: GPIO17_ENABLE * 17: GPIO16_DISABLE * 16: GPIO16_ENABLE * ... * 3: GPIO17_SET * 2: GPIO17_CLEAR * 1: GPIO16_SET * 0: GPIO16_CLEAR */ /* This gives the 'register relative offset gpio' number */ #define EXTENDED_OFFSET_GPIO(gpio) (gpio < 24 ? gpio - 16 : gpio - 24) /* These take the 'register relative offset gpio' number */ #define EXTENDED_REG_DISABLE(ogpio) (0x2 << ((ogpio * 2) + 16)) #define EXTENDED_REG_ENABLE(ogpio) (0x1 << ((ogpio * 2) + 16)) #define EXTENDED_REG_SET(ogpio) (0x2 << (ogpio * 2)) #define EXTENDED_REG_CLR(ogpio) (0x1 << (ogpio * 2)) /* These take the actual GPIO number (16 through 27) */ #define EXTENDED_DISABLE(gpio) \ EXTENDED_REG_DISABLE(EXTENDED_OFFSET_GPIO(gpio)) #define EXTENDED_ENABLE(gpio) \ EXTENDED_REG_ENABLE(EXTENDED_OFFSET_GPIO(gpio)) #define EXTENDED_SET(gpio) \ EXTENDED_REG_SET(EXTENDED_OFFSET_GPIO(gpio)) #define EXTENDED_CLR(gpio) \ EXTENDED_REG_CLR(EXTENDED_OFFSET_GPIO(gpio)) #define EXTENDED_FULL_MASK (0xffffffff) /* -- API inline-functions -- */ /* * Gets the current value of the specified pin */ static inline enum msp_gpio_data msp_gpio_pin_get(unsigned int gpio) { u32 pinhi_mask = 0, pinhi_mask2 = 0; if (gpio >= MSP_NUM_GPIOS) return MSP_GPIO_NONE; if (gpio < 16) { pinhi_mask = BASIC_DATA_MASK(gpio); } else { /* * Two cases are possible with the EXTENDED register: * - In output mode (ENABLED flag set), check the CLR bit * - In input mode (ENABLED flag not set), check the SET bit */ pinhi_mask = EXTENDED_ENABLE(gpio) | EXTENDED_CLR(gpio); pinhi_mask2 = EXTENDED_SET(gpio); } if (((*MSP_GPIO_DATA_REGISTER[gpio] & pinhi_mask) == pinhi_mask) || (*MSP_GPIO_DATA_REGISTER[gpio] & pinhi_mask2)) return MSP_GPIO_HI; else return MSP_GPIO_LO; } /* Sets the specified pin to the specified value */ static inline void msp_gpio_pin_set(enum msp_gpio_data data, unsigned int gpio) { if (gpio >= MSP_NUM_GPIOS) return; if (gpio < 16) { if (data == MSP_GPIO_TOGGLE) toggle_reg32(MSP_GPIO_DATA_REGISTER[gpio], BASIC_DATA_MASK(gpio)); else if (data == MSP_GPIO_HI) set_reg32(MSP_GPIO_DATA_REGISTER[gpio], BASIC_DATA_MASK(gpio)); else clear_reg32(MSP_GPIO_DATA_REGISTER[gpio], BASIC_DATA_MASK(gpio)); } else { if (data == MSP_GPIO_TOGGLE) { /* Special ugly case: * We have to read the CLR bit. * If set, we write the CLR bit. * If not, we write the SET bit. */ u32 tmpdata; custom_read_reg32(MSP_GPIO_DATA_REGISTER[gpio], tmpdata); if (tmpdata & EXTENDED_CLR(gpio)) tmpdata = EXTENDED_CLR(gpio); else tmpdata = EXTENDED_SET(gpio); custom_write_reg32(MSP_GPIO_DATA_REGISTER[gpio], tmpdata); } else { u32 newdata; if (data == MSP_GPIO_HI) newdata = EXTENDED_SET(gpio); else newdata = EXTENDED_CLR(gpio); set_value_reg32(MSP_GPIO_DATA_REGISTER[gpio], EXTENDED_FULL_MASK, newdata); } } } /* Sets the specified pin to the specified value */ static inline void msp_gpio_pin_hi(unsigned int gpio) { msp_gpio_pin_set(MSP_GPIO_HI, gpio); } /* Sets the specified pin to the specified value */ static inline void msp_gpio_pin_lo(unsigned int gpio) { msp_gpio_pin_set(MSP_GPIO_LO, gpio); } /* Sets the specified pin to the opposite value */ static inline void msp_gpio_pin_toggle(unsigned int gpio) { msp_gpio_pin_set(MSP_GPIO_TOGGLE, gpio); } /* Gets the mode of the specified pin */ static inline enum msp_gpio_mode msp_gpio_pin_get_mode(unsigned int gpio) { enum msp_gpio_mode retval = MSP_GPIO_UNKNOWN; uint32_t data; if (gpio >= MSP_NUM_GPIOS) return retval; data = *MSP_GPIO_MODE_REGISTER[gpio]; if (gpio < 16) { retval = BASIC_MODE_FROM_REG(data, gpio); } else { /* Extended pins can only be either INPUT or OUTPUT */ if (data & EXTENDED_ENABLE(gpio)) retval = MSP_GPIO_OUTPUT; else retval = MSP_GPIO_INPUT; } return retval; } /* * Sets the specified mode on the requested pin * Returns 0 on success, or -1 if that mode is not allowed on this pin */ static inline int msp_gpio_pin_mode(enum msp_gpio_mode mode, unsigned int gpio) { u32 modemask, newmode; if ((1 << gpio) & ~MSP_GPIO_MODE_ALLOWED[mode]) return -1; if (gpio >= MSP_NUM_GPIOS) return -1; if (gpio < 16) { modemask = BASIC_MODE_MASK(gpio); newmode = BASIC_MODE(mode, gpio); } else { modemask = EXTENDED_FULL_MASK; if (mode == MSP_GPIO_INPUT) newmode = EXTENDED_DISABLE(gpio); else newmode = EXTENDED_ENABLE(gpio); } /* Do the set atomically */ set_value_reg32(MSP_GPIO_MODE_REGISTER[gpio], modemask, newmode); return 0; } #endif /* __MSP_GPIO_MACROS_H__ */