diff options
Diffstat (limited to 'arch/arm/mach-mx3/iomux.c')
-rw-r--r-- | arch/arm/mach-mx3/iomux.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/arch/arm/mach-mx3/iomux.c b/arch/arm/mach-mx3/iomux.c new file mode 100644 index 000000000000..24ede6286b9c --- /dev/null +++ b/arch/arm/mach-mx3/iomux.c @@ -0,0 +1,259 @@ +/* + * Copyright 2004-2007 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 + * + * @brief I/O Muxing control functions + * + * @ingroup GPIO_MX31 + */ + +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/hardware.h> +#include <asm/arch/gpio.h> +#include "iomux.h" + +/*! + * 4 control fields per MUX register + */ +#define MUX_CTL_FIELDS 4 + +/*! + * 3 control fields per PAD register + */ +#define PAD_CTL_FIELDS 3 + +/*! + * 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) + +/*! + * Number of pad controls = + * (highest pad ctl reg - lowest pad ctl reg + 1) * (3 pins/reg) + */ +#define PAD_CTL_NUM_MAX \ + (((u32 *)IOMUXSW_PAD_END - (u32 *)IOMUXSW_PAD_CTL + 1) * PAD_CTL_FIELDS) + +#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)) + +/*! + * 8 bits for each MUX control field + */ +#define MUX_CTL_BIT_LEN 8 + +/*! + * 10 bits for each PAD control field + */ +#define MUX_PAD_BIT_LEN 10 + +/*! + * IOMUX register (base) addresses + */ +enum iomux_reg_addr { + IOMUXGPR = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x008, /*!< General purpose */ + IOMUXSW_MUX_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x00C, /*!< MUX control */ + IOMUXSW_MUX_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x150, /*!< last MUX control register */ + IOMUXSW_PAD_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x154, /*!< Pad control */ + IOMUXSW_PAD_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x308, /*!< last Pad control register */ + IOMUXINT_OBS1 = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x000, /*!< Observe interrupts 1 */ + IOMUXINT_OBS2 = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x004, /*!< Observe interrupts 2 */ +}; + +/* 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 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 + */ +int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_ocfg_t out, + iomux_pin_icfg_t in) +{ + u32 reg, 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; + + 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 = (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; +} + +/*! + * 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_request_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out, + iomux_pin_icfg_t in) +{ + int ret = iomux_config_mux(pin, out, in); + if (out == OUTPUTCONFIG_GPIO && in == INPUTCONFIG_GPIO) { + ret |= mxc_request_gpio(pin); + } + return ret; +} + +/*! + * 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) +{ + 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; + + 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) { + mxc_free_gpio(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) +{ + u32 reg, 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); + + 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); +} + +/* + * FIXED ME: for backward compatible. to be removed! + */ +void iomux_config_pad(iomux_pin_name_t pin, u32 config) +{ + mxc_iomux_set_pad(pin, config); +} + +/*! + * 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(iomux_gp_func_t gp, bool en) +{ + u32 l; + + spin_lock(&gpio_mux_lock); + l = __raw_readl(IOMUXGPR); + if (en) { + l |= gp; + } 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); |