/*
 * Copyright 2004-2008 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/io.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <mach/hardware.h>
#include <mach/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);
