diff options
Diffstat (limited to 'arch/arm/mach-mx3/iomux.c')
-rw-r--r-- | arch/arm/mach-mx3/iomux.c | 317 |
1 files changed, 198 insertions, 119 deletions
diff --git a/arch/arm/mach-mx3/iomux.c b/arch/arm/mach-mx3/iomux.c index c66ccbcdc11b..9f6841ec2fdf 100644 --- a/arch/arm/mach-mx3/iomux.c +++ b/arch/arm/mach-mx3/iomux.c @@ -1,181 +1,260 @@ /* - * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de> - * Copyright (C) 2009 by Valentin Longchamp <valentin.longchamp@epfl.ch> + * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup GPIO_MX31 Board GPIO and Muxing Setup + * @ingroup MSL_MX31 + */ +/*! + * @file mach-mx3/iomux.c * - * 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 program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * @brief I/O Muxing control functions * - * 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., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. + * @ingroup GPIO_MX31 */ +#include <linux/io.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/io.h> -#include <linux/kernel.h> #include <mach/hardware.h> #include <mach/gpio.h> -#include <mach/iomux-mx3.h> +#include "iomux.h" -/* - * IOMUX register (base) addresses +/*! + * 4 control fields per MUX register */ -#define IOMUX_BASE IO_ADDRESS(IOMUXC_BASE_ADDR) -#define IOMUXINT_OBS1 (IOMUX_BASE + 0x000) -#define IOMUXINT_OBS2 (IOMUX_BASE + 0x004) -#define IOMUXGPR (IOMUX_BASE + 0x008) -#define IOMUXSW_MUX_CTL (IOMUX_BASE + 0x00C) -#define IOMUXSW_PAD_CTL (IOMUX_BASE + 0x154) +#define MUX_CTL_FIELDS 4 -static DEFINE_SPINLOCK(gpio_mux_lock); +/*! + * 3 control fields per PAD register + */ +#define PAD_CTL_FIELDS 3 -#define IOMUX_REG_MASK (IOMUX_PADNUM_MASK & ~0x3) +/*! + * Maximum number of MUX pins + * Number of pins = (highest iomux reg - lowest iomux reg + 1) * (4 pins/reg) + */ +#define MUX_PIN_NUM_MAX \ + (((u32 *)IOMUXSW_MUX_END - (u32 *)IOMUXSW_MUX_CTL + 1) * MUX_CTL_FIELDS) -unsigned long mxc_pin_alloc_map[NB_PORTS * 32 / BITS_PER_LONG]; -/* - * set the mode for a IOMUX pin. +/*! + * Number of pad controls = + * (highest pad ctl reg - lowest pad ctl reg + 1) * (3 pins/reg) */ -int mxc_iomux_mode(unsigned int pin_mode) -{ - u32 field, l, mode, ret = 0; - void __iomem *reg; +#define PAD_CTL_NUM_MAX \ + (((u32 *)IOMUXSW_PAD_END - (u32 *)IOMUXSW_PAD_CTL + 1) * PAD_CTL_FIELDS) - reg = IOMUXSW_MUX_CTL + (pin_mode & IOMUX_REG_MASK); - field = pin_mode & 0x3; - mode = (pin_mode & IOMUX_MODE_MASK) >> IOMUX_MODE_SHIFT; +#define PIN_TO_IOMUX_INDEX(pin) ((pin >> MUX_I) & ((1 << (MUX_F - MUX_I)) - 1)) +#define PIN_TO_IOMUX_FIELD(pin) ((pin >> MUX_F) & ((1 << (PAD_I - MUX_F)) - 1)) - spin_lock(&gpio_mux_lock); +/*! + * 8 bits for each MUX control field + */ +#define MUX_CTL_BIT_LEN 8 - l = __raw_readl(reg); - l &= ~(0xff << (field * 8)); - l |= mode << (field * 8); - __raw_writel(l, reg); +/*! + * 10 bits for each PAD control field + */ +#define MUX_PAD_BIT_LEN 10 - spin_unlock(&gpio_mux_lock); +/*! + * IOMUX register (base) addresses + */ +#define IOMUXGPR (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x008) /*!< General purpose */ +#define IOMUXSW_MUX_CTL (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x00C) /*!< MUX control */ +#define IOMUXSW_MUX_END (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x150) /*!< last MUX control register */ +#define IOMUXSW_PAD_CTL (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x154) /*!< Pad control */ +#define IOMUXSW_PAD_END (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x308) /*!< last Pad control register */ +#define IOMUXINT_OBS1 (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x000) /*!< Observe interrupts 1 */ +#define IOMUXINT_OBS2 (IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x004) /*!< Observe interrupts 2 */ - return ret; -} -EXPORT_SYMBOL(mxc_iomux_mode); +/* len - mask bit length; fld - mask bit field. Example, to have the mask: + * 0xFF000000, use GET_FIELD_MASK(8, 3). Translate in plain language: + * "set the 3rd (0-based) 8-bit-long field to all 1's */ +#define GET_FIELD_MASK(len, fld) (((1 << len) - 1) << (len * fld)) +static DEFINE_SPINLOCK(gpio_mux_lock); +static u8 iomux_pin_res_table[MUX_PIN_NUM_MAX]; -/* - * This function configures the pad value for a IOMUX pin. +/*! + * This function is used to configure a pin through the IOMUX module. + * FIXED ME: for backward compatible. Will be static function! + * @param pin a pin number as defined in \b #iomux_pin_name_t + * @param out an output function as defined in \b #iomux_pin_ocfg_t + * @param in an input function as defined in \b #iomux_pin_icfg_t + * + * @return 0 if successful; Non-zero otherwise */ -void mxc_iomux_set_pad(enum iomux_pins pin, u32 config) +int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_ocfg_t out, + iomux_pin_icfg_t in) { - u32 field, l; void __iomem *reg; + u32 l, ret = 0; + u32 mux_index = PIN_TO_IOMUX_INDEX(pin); + u32 mux_field = PIN_TO_IOMUX_FIELD(pin); + u32 mux_mask = GET_FIELD_MASK(MUX_CTL_BIT_LEN, mux_field); + u8 *rp; - pin &= IOMUX_PADNUM_MASK; - reg = IOMUXSW_PAD_CTL + (pin + 2) / 3 * 4; - field = (pin + 2) % 3; - - pr_debug("%s: reg offset = 0x%x, field = %d\n", - __func__, (pin + 2) / 3, field); + BUG_ON((mux_index > (MUX_PIN_NUM_MAX / MUX_CTL_FIELDS - 1)) || + (mux_field >= MUX_CTL_FIELDS)); + reg = IOMUXSW_MUX_CTL + (mux_index * 4); spin_lock(&gpio_mux_lock); - l = __raw_readl(reg); - l &= ~(0x1ff << (field * 10)); - l |= config << (field * 10); + l = (l & (~mux_mask)) | + (((out << 4) | in) << (mux_field * MUX_CTL_BIT_LEN)); __raw_writel(l, reg); - + /* + * Log a warning if a pin changes ownership + */ + rp = iomux_pin_res_table + mux_index * MUX_CTL_FIELDS + mux_field; + if (out & *rp && *rp != ((out << 4) | in)) { + /* + * Don't call printk if we're tweaking the console uart or + * we'll deadlock. + */ + if (pin != MX31_PIN_CTS1 && + pin != MX31_PIN_RTS1 && + pin != MX31_PIN_DCD_DCE1 && + pin != MX31_PIN_DSR_DTE1 && + pin != MX31_PIN_DTR_DTE1 && + pin != MX31_PIN_RI_DCE1 && + pin != MX31_PIN_DSR_DCE1 && + pin != MX31_PIN_DTR_DCE1 && + pin != MX31_PIN_RXD1 && pin != MX31_PIN_TXD1) { + printk(KERN_ERR "iomux_config_mux: Warning: iomux pin" + " config changed, index=%d field=%d, " + " prev=0x%x new=0x%x\n", mux_index, mux_field, + *rp, (out << 4) | in); + } + ret = -EINVAL; + } + *rp = (out << 4) | in; spin_unlock(&gpio_mux_lock); + + return ret; } -EXPORT_SYMBOL(mxc_iomux_set_pad); -/* - * allocs a single pin: - * - reserves the pin so that it is not claimed by another driver - * - setups the iomux according to the configuration +/*! + * Request ownership for an IO pin. This function has to be the first one + * being called before that pin is used. The caller has to check the + * return value to make sure it returns 0. + * + * @param pin a name defined by \b iomux_pin_name_t + * @param out an output function as defined in \b #iomux_pin_ocfg_t + * @param in an input function as defined in \b #iomux_pin_icfg_t + * + * @return 0 if successful; Non-zero otherwise */ -int mxc_iomux_alloc_pin(const unsigned int pin, const char *label) +int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out, + iomux_pin_icfg_t in) { - unsigned pad = pin & IOMUX_PADNUM_MASK; - - if (pad >= (PIN_MAX + 1)) { - printk(KERN_ERR "mxc_iomux: Attempt to request nonexistant pin %u for \"%s\"\n", - pad, label ? label : "?"); - return -EINVAL; - } - - if (test_and_set_bit(pad, mxc_pin_alloc_map)) { - printk(KERN_ERR "mxc_iomux: pin %u already used. Allocation for \"%s\" failed\n", - pad, label ? label : "?"); - return -EBUSY; + int ret = iomux_config_mux(pin, out, in); + if (out == OUTPUTCONFIG_GPIO && in == INPUTCONFIG_GPIO) { + ret |= gpio_request(IOMUX_TO_GPIO(pin), NULL); } - mxc_iomux_mode(pin); - - return 0; + return ret; } -EXPORT_SYMBOL(mxc_iomux_alloc_pin); -int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count, - const char *label) +/*! + * Release ownership for an IO pin + * + * @param pin a name defined by \b iomux_pin_name_t + * @param out an output function as defined in \b #iomux_pin_ocfg_t + * @param in an input function as defined in \b #iomux_pin_icfg_t + */ +void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out, + iomux_pin_icfg_t in) { - unsigned int *p = pin_list; - int i; - int ret = -EINVAL; - - for (i = 0; i < count; i++) { - ret = mxc_iomux_alloc_pin(*p, label); - if (ret) - goto setup_error; - p++; - } - return 0; + u32 mux_index = PIN_TO_IOMUX_INDEX(pin); + u32 mux_field = PIN_TO_IOMUX_FIELD(pin); + u8 *rp = iomux_pin_res_table + mux_index * MUX_CTL_FIELDS + mux_field; -setup_error: - mxc_iomux_release_multiple_pins(pin_list, i); - return ret; + BUG_ON((mux_index > (MUX_PIN_NUM_MAX / MUX_CTL_FIELDS - 1)) || + (mux_field >= MUX_CTL_FIELDS)); + + *rp = 0; + if (out == OUTPUTCONFIG_GPIO && in == INPUTCONFIG_GPIO) { + gpio_free(IOMUX_TO_GPIO(pin)); + } } -EXPORT_SYMBOL(mxc_iomux_setup_multiple_pins); -void mxc_iomux_release_pin(const unsigned int pin) +/*! + * This function configures the pad value for a IOMUX pin. + * + * @param pin a pin number as defined in \b #iomux_pin_name_t + * @param config the ORed value of elements defined in \b #iomux_pad_config_t + */ +void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config) { - unsigned pad = pin & IOMUX_PADNUM_MASK; + void __iomem *reg; + u32 l; + u32 pad_index = (pin >> PAD_I) & ((1 << (PAD_F - PAD_I)) - 1); + u32 pad_field = (pin >> PAD_F) & ((1 << (MUX_IO_I - PAD_F)) - 1); + u32 pad_mask = GET_FIELD_MASK(MUX_PAD_BIT_LEN, pad_field); - if (pad < (PIN_MAX + 1)) - clear_bit(pad, mxc_pin_alloc_map); + BUG_ON((pad_index > (PAD_CTL_NUM_MAX / PAD_CTL_FIELDS - 1)) || + (pad_field >= PAD_CTL_FIELDS)); + + reg = IOMUXSW_PAD_CTL + (pad_index * 4); + spin_lock(&gpio_mux_lock); + l = __raw_readl(reg); + l = (l & (~pad_mask)) | (config << (pad_field * MUX_PAD_BIT_LEN)); + __raw_writel(l, reg); + spin_unlock(&gpio_mux_lock); } -EXPORT_SYMBOL(mxc_iomux_release_pin); -void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count) +/* + * FIXED ME: for backward compatible. to be removed! + */ +void iomux_config_pad(iomux_pin_name_t pin, u32 config) { - unsigned int *p = pin_list; - int i; - - for (i = 0; i < count; i++) { - mxc_iomux_release_pin(*p); - p++; - } + mxc_iomux_set_pad(pin, config); } -EXPORT_SYMBOL(mxc_iomux_release_multiple_pins); -/* +/*! * This function enables/disables the general purpose function for a particular * signal. + * + * @param gp one signal as defined in \b #iomux_gp_func_t + * @param en \b #true to enable; \b #false to disable */ -void mxc_iomux_set_gpr(enum iomux_gp_func gp, bool en) +void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en) { u32 l; spin_lock(&gpio_mux_lock); l = __raw_readl(IOMUXGPR); - if (en) + if (en) { l |= gp; - else + } else { l &= ~gp; - + } __raw_writel(l, IOMUXGPR); spin_unlock(&gpio_mux_lock); } + +/*! + * FIXED ME: for backward compatible. to be removed! + */ +void iomux_config_gpr(iomux_gp_func_t gp, bool en) +{ + mxc_iomux_set_gpr(gp, en); +} + +EXPORT_SYMBOL(mxc_request_iomux); +EXPORT_SYMBOL(mxc_free_iomux); +EXPORT_SYMBOL(mxc_iomux_set_pad); EXPORT_SYMBOL(mxc_iomux_set_gpr); +EXPORT_SYMBOL(iomux_config_pad); +EXPORT_SYMBOL(iomux_config_gpr); +EXPORT_SYMBOL(iomux_config_mux); |