/*
 *  arch/arm/mach-dmw/gpio.c
 *
 *  Copyright (c) 2010 DSPG Technologies GmbH
 *
 * GPIO extension for the XpandR III hardware platform.
 *
 * 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.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <asm/io.h>
#include <mach/irqs.h>
#include <asm/gpio.h>
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/clk.h>

static inline int _gpio_to_nr(unsigned gpio, struct gpio_usage *bank)
{
	return gpio & 0xFF; // & ~((u32)bank->pins);
}

static inline int _gpionr_in_use(int gpionr, struct gpio_usage *bank)
{
	return (bank->usage >> gpionr) & 0x01;
}

static inline int _gpionr_is_input(int gpionr, struct gpio_usage *bank)
{
	return (*(bank->regs + bank->direction) >> gpionr) & 0x01;
}

static struct gpio_usage *_find_regbank(unsigned gpio);

/*
 * Here we store the pointers of the different GPIO control registers
 *
 * Each GPIO bank (A..G on the DMW) has 32 pins, from 0..31
 */
static struct gpio_usage registers[] = {
	{ /* GPIO A */
		.data		= ((u32 ) DMW_GPIO_AGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_AGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_AGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_AGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_AGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_AGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_AGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_AGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_AGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_AGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_AGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_AGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_AGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_AGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_AGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_AGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_AGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_AGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_AGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_AGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_AGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_AGP_IN)        >> 2,
	},
	{ /* GPIO B */
		.data		= ((u32 ) DMW_GPIO_BGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_BGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_BGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_BGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_BGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_BGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_BGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_BGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_BGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_BGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_BGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_BGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_BGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_BGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_BGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_BGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_BGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_BGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_BGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_BGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_BGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_BGP_IN)        >> 2,
	},
	{ /* GPIO C */
		.data		= ((u32 ) DMW_GPIO_CGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_CGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_CGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_CGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_CGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_CGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_CGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_CGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_CGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_CGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_CGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_CGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_CGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_CGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_CGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_CGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_CGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_CGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_CGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_CGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_CGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_CGP_IN)        >> 2,
	},
	{ /* GPIO D */
		.data		= ((u32 ) DMW_GPIO_DGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_DGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_DGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_DGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_DGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_DGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_DGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_DGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_DGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_DGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_DGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_DGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_DGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_DGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_DGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_DGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_DGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_DGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_DGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_DGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_DGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_DGP_IN)        >> 2,
	},
	{ /* GPIO E */
		.data		= ((u32 ) DMW_GPIO_EGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_EGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_EGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_EGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_EGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_EGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_EGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_EGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_EGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_EGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_EGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_EGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_EGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_EGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_EGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_EGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_EGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_EGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_EGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_EGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_EGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_EGP_IN)        >> 2,
	},
	{ /* GPIO F */
		.data		= ((u32 ) DMW_GPIO_FGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_FGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_FGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_FGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_FGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_FGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_FGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_FGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_FGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_FGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_FGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_FGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_FGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_FGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_FGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_FGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_FGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_FGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_FGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_FGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_FGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_FGP_IN)        >> 2,
	},
	{ /* GPIO G */
		.data		= ((u32 ) DMW_GPIO_GGP_DATA)      >> 2,
		.data_set	= ((u32 ) DMW_GPIO_GGP_DATA_SET)  >> 2,
		.data_clr	= ((u32 ) DMW_GPIO_GGP_DATA_CLR)  >> 2,
		.direction	= ((u32 ) DMW_GPIO_GGP_DIR)       >> 2,
		.direction_out	= ((u32 ) DMW_GPIO_GGP_DIR_OUT)   >> 2,
		.direction_in	= ((u32 ) DMW_GPIO_GGP_DIR_IN)    >> 2,
		.enable		= ((u32 ) DMW_GPIO_GGP_EN)        >> 2,
		.enable_set	= ((u32 ) DMW_GPIO_GGP_EN_SET)    >> 2,
		.enable_clr	= ((u32 ) DMW_GPIO_GGP_EN_CLR)    >> 2,
		.pull_ctrl	= ((u32 ) DMW_GPIO_GGP_PULL_CTRL) >> 2,
		.pull_en	= ((u32 ) DMW_GPIO_GGP_PULL_EN)   >> 2,
		.pull_dis	= ((u32 ) DMW_GPIO_GGP_PULL_DIS)  >> 2,
		.pull_type	= ((u32 ) DMW_GPIO_GGP_PULL_TYPE) >> 2,
		.pull_up	= ((u32 ) DMW_GPIO_GGP_PULL_UP)   >> 2,
		.pull_down	= ((u32 ) DMW_GPIO_GGP_PULL_DOWN) >> 2,
		.keep		= ((u32 ) DMW_GPIO_GGP_KEEP)      >> 2,
		.keep_en	= ((u32 ) DMW_GPIO_GGP_KEEP_EN)   >> 2,
		.keep_dis	= ((u32 ) DMW_GPIO_GGP_KEEP_DIS)  >> 2,
		.od		= ((u32 ) DMW_GPIO_GGP_OD)        >> 2,
		.od_set		= ((u32 ) DMW_GPIO_GGP_OD_SET)    >> 2,
		.od_clr		= ((u32 ) DMW_GPIO_GGP_OD_CLR)    >> 2,
		.pins		= ((u32 ) DMW_GPIO_GGP_IN)        >> 2,
	},
};

/*
 * Try to request a GPIO from the subsystem
 */
int gpio_request(unsigned gpio, char *label)
{
	/* Find the correct register file */
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return -ENODEV;

	/* Strip away the bank register location */
	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	if (_gpionr_in_use(gpionr, bank)) {
		printk(KERN_INFO "%s: %s - the requested GPIO %u is already in use\n",
				__FUNCTION__, label, gpio);
		return -EBUSY;
	}

	/* Mark the GPIO as used and set the label */
	bank->usage |= (1<<gpionr);
	bank->labels[gpionr] = label;

	return 0;
}
EXPORT_SYMBOL(gpio_request);

/*
 * Free a GPIO
 */
void gpio_free(unsigned gpio)
{
	/* Find the correct register file */
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return;

	/* Strip away the bank register location */
	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);


	/* Mark the GPIO as unused and reset the label */
	bank->usage &= ~(1<<gpionr);
	bank->labels[gpionr] = NULL;
}
EXPORT_SYMBOL(gpio_free);

/* Tests whether the Pull-Up/Dn internal resistor can be disconnected,
 * in order to save power.
 */
static void
tst_pull_disconnect(unsigned gpio)
{
	unsigned int can_disconn = 0;

	if (! gpio_get_enable(gpio)) {
		/* Not a gpio */
		return;
	}

	if (gpio_get_pull_enable(gpio)) {
		/* A pull (Up or Down) resistor Is connected */

		if (gpio_get_direction(gpio) == 0) {
			/* Output gpio */

			/* Note:
			 * "Wired-OR" is achived by enabling "Open-Drain" and
			 * No Pull-Down resistor, but the user may connect
			 * a Pull-Up resistor, if no such External resistor
			 * exists. So "open-drain" must be excluded !
			 */
			if (! gpio_get_opendrain(gpio)) {
				/* Not open-drain */
				can_disconn = 1;
			}
		} else {
			/* Input gpio */

			if (gpio_get_keeper(gpio)) {
				/* Keeper circuit connected */
				can_disconn = 1;
			}
		}
	}

	if (can_disconn) {
		/* Disconnect the pull resistor */
		gpio_set_pull_enable(gpio, 0);
	}
}

/*
 * Returns a GPIO direction:
 * 0 - Output; 1 - Input
 */
int gpio_get_direction(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	return ((*(bank->regs + bank->direction) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_direction);

/*
 * Sets a GPIO as a Input gpio
 *
 * TODO: Check for special function
 */
int gpio_direction_input(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Reset the corresponding bit */
	*(bank->regs + bank->direction_in) = (1<<gpionr);

	/* Test for possible Pull resistor disconnection */
	tst_pull_disconnect(gpio);

	return 0;
}
EXPORT_SYMBOL(gpio_direction_input);

/*
 * Sets a GPIO direction as an Output gpio
 * and sets the output value
 */
int gpio_direction_output(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	*(bank->regs + bank->direction_out) = (1<<gpionr);

	gpio_set_value(gpio, value);

	/* Test for possible Pull resistor disconnection */
	tst_pull_disconnect(gpio);
	
	return 0;
}
EXPORT_SYMBOL(gpio_direction_output);

/*
 * Reads the value of a pin.
 * This can always be used on the DMW, no matter if it's
 * input or output gpio, or even if not a gpio (special functionality).
 *
 * Note:
 * Reading the gpio state through its Pin value (xGP_IN reg), rather then
 * through its Data buffer (xGP_DATA reg) is relevant Only for an Open-Drain Output
 * gpio ("Wired-OR" logic gate), where the Pin value may differ from the state it
 * has been set to.
 * For an Input gpio, there is no difference between reading the Pin state and
 * reading the buffered state.
 * So, in theory, it was software-wise correct to always read the Pin value,
 * No matter if it is an Output or an Input gpio.
 *
 * However, It does not(!) work as expected in dmw-96 GPIO module:
 * reading the Pin value for an Output gpio gives junk; that's why
 * the code was changed to read the Data buffer in this case.
 * (might as well read the Data buffer for an Input gpio).
 */
int gpio_get_value(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	if (_gpionr_is_input(gpionr, bank))
		return ((*(bank->regs + bank->pins) >> gpionr) & 0x1);
	return ((*(bank->regs + bank->data) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_value);

/*
 * Set the value of an output GPIO pin:
 * 0 - Low ('0'); Otherwise - High ('1')
 */
void gpio_set_value(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Only set if this is an output */
	if (_gpionr_is_input(gpionr, bank))
		return;

	/* Set the value */
	if (value)
		*(bank->regs + bank->data_set) = (1<<gpionr);
	else
		*(bank->regs + bank->data_clr) = (1<<gpionr);

}
EXPORT_SYMBOL(gpio_set_value);

/* 
 * Returns Pin functionality:
 * 0 - Other; 1 - gpio
 */
int gpio_get_enable(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	return ((*(bank->regs + bank->enable) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_enable);


/* 
 * Sets Pin functionality:
 * 0 - Other; Otherwise - gpio
 */
void gpio_set_enable(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Set the value */
	if (value)
		*(bank->regs + bank->enable_set) = (1<<gpionr);
	else
		*(bank->regs + bank->enable_clr) = (1<<gpionr);

	
	/* Test for possible Pull resistor disconnection */
	tst_pull_disconnect(gpio);
}
EXPORT_SYMBOL(gpio_set_enable);

/* 
 * Returns: internal Pull Up/Down resistor connection:
 * 0 - Disconnected; 1 - Connected
 */
int gpio_get_pull_enable(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	return ((*(bank->regs + bank->pull_ctrl) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_pull_enable);

/*
 * Sets internal Pull-Up/Down resistor connection:
 * 0 - Disconnect; Otherwise - Connect
 */
void gpio_set_pull_enable(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Set the value */
	if (value)
		*(bank->regs + bank->pull_en)  = (1<<gpionr);
	else
		*(bank->regs + bank->pull_dis) = (1<<gpionr);
}
EXPORT_SYMBOL(gpio_set_pull_enable);

/* 
 * Returns pull Type:
 * 0 - pull DOWN; 1- pull UP
 *
 * Note: Relevant only if "pull" is enabled !
 */
int gpio_get_pull_selection(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	return ((*(bank->regs + bank->pull_type) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_pull_selection);

/*
 * Chooses internal Pull resistor:
 * 0 - pull DOWN; Otherwise - pull UP
 */
void gpio_set_pull_selection(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Set the value */
	if (value)
		*(bank->regs + bank->pull_up)   = (1<<gpionr);
	else
		*(bank->regs + bank->pull_down) = (1<<gpionr);
}
EXPORT_SYMBOL(gpio_set_pull_selection);

/* 
 * Returns the Open-Drain configuration status, 
 * relevant only for an output gpio:
 * 0 - Normal push-pull; 1 - Open-drain
 */
int gpio_get_opendrain(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	return ((*(bank->regs + bank->od) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_opendrain);

/* 
 * Sets the Open-Drain configuration for an output gpio:
 * 0 - Normal push-pull; Otherwise - Open-drain
 */
void gpio_set_opendrain(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Set the value */
	if (value)
		*(bank->regs + bank->od_set) = (1<<gpionr);
	else
		*(bank->regs + bank->od_clr) = (1<<gpionr);

	/* Note: When setting a "Wired-OR" logic gate, make sure that
	 * your code also Disconnects the interrnal Pull-DOWN resistor !
	 */
}
EXPORT_SYMBOL(gpio_set_opendrain);


/* "Keeper" stands for a "bootstrap" circuit, relevant only to an Input gpio.
 * It does:
 * - prevent oscillation when the gpio is floating (tristate).
 * - smooth very short glitches.
 * - improve the slew-rate if the driving-source output is open-drain ("wired-OR").
 */

/* Returns the Keeper connectivity status:
 * 0 - Disabled (disconnected); 1 - Enabled (connected)
 */
int gpio_get_keeper(unsigned gpio)
{
	struct gpio_usage *bank = _find_regbank(gpio);

	int gpionr;

	if (!bank)
		return -ENODEV;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	return ((*(bank->regs + bank->keep) >> gpionr) & 0x1);
}
EXPORT_SYMBOL(gpio_get_keeper);

/* Sets the Keeper connectivity status:
 * 0 - Disabled (disconnected); Otherwise - Enabled (connected)
 */
void gpio_set_keeper(unsigned gpio, int value)
{
	struct gpio_usage *bank = _find_regbank(gpio);
	int gpionr;

	if (!bank)
		return;

	gpionr = _gpio_to_nr(gpio, bank);
	BUG_ON(gpionr < 0 || gpionr > 31);

	/* Set the value */
	if (value)
		*(bank->regs + bank->keep_en)  = (1<<gpionr);
	else
		*(bank->regs + bank->keep_dis) = (1<<gpionr);

	/* Test for possible Pull resistor disconnection */
	tst_pull_disconnect(gpio);
}
EXPORT_SYMBOL(gpio_set_keeper);

/* 
 * GPIO dumping function, this will print out the GPIO registers
 */
void gpio_dump()
{
	int i,j;

	for (i = 0; i < ARRAY_SIZE(registers); i++) {
		printk(KERN_INFO "BANK %c\n", 'A'+i);
		printk(KERN_INFO "data: %X (reg 0x%X)\n", *(registers[i].regs + registers[i].data), (registers[i].regs + registers[i].data));
		printk(KERN_INFO "direction: %X\n", *(registers[i].regs + registers[i].direction));
		printk(KERN_INFO "enable: %X\n", *(registers[i].regs + registers[i].enable));
		printk(KERN_INFO "pull up/down enable: %X\n", *(registers[i].regs + registers[i].pull_en));
		printk(KERN_INFO "pull up/down selection: %X\n", *(registers[i].regs + registers[i].pull_type));
		printk(KERN_INFO "keeper: %X\n", *(registers[i].regs + registers[i].keep));
		printk(KERN_INFO "open-drain mode: %X\n", *(registers[i].regs + registers[i].od));
		printk(KERN_INFO "pins: %X\n", *(registers[i].regs + registers[i].pins));
		printk(KERN_INFO "gpio ownership:\n");
		for (j=0 ; j<32 ; j++)
		{
			if (registers[i].usage & (1<<j) )
			 	printk(KERN_INFO "%s -> GPIO%c%d\n", registers[i].labels[j], 'A'+i, j);
			
		}
	}

}
EXPORT_SYMBOL(gpio_dump);

int gpio_to_irq(unsigned int gpio)
{
	if(((DMW_GPIO_FGP_DATA >> 2) << 8)^(gpio & (~0xff)))
	{
		return -1;
	}
	return (gpio&0xff) - 16 + DMW_IID_EXTERNAL_REQUEST_0;
}

EXPORT_SYMBOL(gpio_to_irq);

/* Helper function to find the correct GPIO Bank location */
static struct gpio_usage *_find_regbank(unsigned gpio)
{
	int i;
	unsigned addr;
	struct gpio_usage *bank = NULL;
	unsigned search = (gpio >> 8) & 0xFF;

	for (i = 0; i < ARRAY_SIZE(registers); i++) {
		addr = (u32)registers[i].data;
		if (addr == search) {
			bank = &registers[i];
			break;
		}
	}

	BUG_ON(!bank);
	return bank;
}

/* Initialization */
static int __init gpio_init(void)
{
	u32 *map;
	struct resource *mres;
	int i;

	/*
	 * Map the space correctly as iomem
	 */
	resource_size_t start = (resource_size_t) (DMW_GPIO_BASE +
	                        registers[0].data);
	resource_size_t end   = (resource_size_t) (DMW_GPIO_BASE +
	                        registers[ARRAY_SIZE(registers)-1].pins + 1);
	resource_size_t len;

	len = (end-start);

	mres = request_mem_region(start, len, "dmw-gpio");
	if (!mres) {
		printk(KERN_ERR
			"%s: Can't reserve the GPIO memory space (%X-%X)!\n",
				__FUNCTION__, start, end);

		return -ENODEV;
	}

	map = ioremap_nocache(start, len);
	if (!map) {
		printk(KERN_ERR "%s: Can't remap the GPIO memory space\n",
				__FUNCTION__);
		release_mem_region(start, len);
		return -ENOMEM;
	}

	for (i = 0; i < ARRAY_SIZE(registers); i++)
		registers[i].regs = map;

	printk(KERN_NOTICE "%s: Registered DW GPIO device\n", __FUNCTION__);

	return 0;
}
arch_initcall(gpio_init);
