/*
 * (C) Copyright 2007-2008 Andre McCurdy, NXP Semiconductors
 *
 * 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 <common.h>
#include <stb225_gpio.h>

/*****************************************************************************
*****************************************************************************/

#ifndef GPIO_REGS_BASE
#define GPIO_REGS_BASE		0xB7E0F000
#endif

#define REG_PIO_IN		0x000	/* State of the PIO pins */
#define REG_PIO_OUT		0x004	/* Value driven on the PIO pins */
#define REG_PIO_DIR		0x008	/* Direction of the PIO port */
#define REG_PIO_SEL		0x014	/* IO or alternate function */
#define REG_PIO_INT_EDGE	0x020	/* Enable edge interrupts */
#define REG_PIO_INT_HI		0x024	/* Enable high level interrupts */
#define REG_PIO_INT_LO		0x028	/* Enable low level interrupts */
#define REG_PIO_DEB		0x044	/* Debounce control */
#define REG_CAPTURE_0		0x04C	/* Captured value on PIO[10] */
#define REG_CAPTURE_1		0x050	/* Captured value on PIO[11] */
#define REG_CAPTURE_CTRL	0x054	/* Capture control */
#define REG_CAPTURE_TIMER	0x058	/* Capture timer configuration */
#define REG_PIO_IN2		0x05C	/* State of the PIO pins */
#define REG_PIO_OUT2		0x060	/* Value driven on the PIO pins */
#define REG_PIO_DIR2		0x064	/* Direction of the PIO port */
#define REG_PIO_SEL2		0x068	/* IO or alternate function */
#define REG_FG0_PRESET		0x06C	/* 32bit Preset for FG0 */
#define REG_FG1_PRESET		0x070	/* 32bit Preset for FG1 */
#define REG_FG2_PRESET		0x074	/* 32bit Preset for FG2 */
#define REG_FG3_PRESET		0x078	/* 32bit Preset for FG3 */
#define REG_FG4_PRESET		0x07C	/* 32bit Preset for FG4 */
#define REG_FG5_PRESET		0x080	/* 32bit Preset for FG5 */
#define REG_FG6_PRESET		0x084	/* 32bit Preset for FG6 */
#define REG_FG7_PRESET		0x088	/* 32bit Preset for FG7 */
#define REG_FG8_PRESET		0x08C	/* 32bit Preset for FG8 */
#define REG_FG9_PRESET		0x090	/* 32bit Preset for FG9 */
#define REG_FG10_PRESET		0x094	/* 32bit Preset for FG10 */
#define REG_FG11_PRESET		0x098	/* 32bit Preset for FG11 */
#define REG_FG_FREQ_SEL		0x09C	/* Waveform frequency selection */
#define REG_FG_FREQ_SEL2	0x0A0	/* Waveform frequency selection */
#define REG_FG_DATA_SEL		0x0A4	/* Waveform input selection */
#define REG_PIO_INT_STATUS	0xFE0	/* GPIO interrupt status */
#define REG_PIO_INT_ENABLE	0xFE4	/* GPIO interrupt enable */
#define REG_PIO_INT_CLEAR	0xFE8	/* GPIO interrupt clear */
#define REG_PIO_INT_SET		0xFEC	/* GPIO interrupt set */
#define REG_PIO_RESET		0xFF4	/* GPIO reset */
#define REG_PIO_MODULE_ID	0xFFC	/* GPIO module ID */


/****************************************************************************
****************************************************************************/

#define GPIO_PIN2BLOCK(pin)     ((pin) >> 5)
#define GPIO_PIN2BITMASK(pin)   (1 << ((pin) & 0x1F))


/****************************************************************************
****************************************************************************/

static unsigned int gpio_reg_read (unsigned int reg_offset)
{
	return *((volatile unsigned int *) (GPIO_REGS_BASE + reg_offset));
}

static void gpio_reg_write (unsigned int reg_offset, unsigned int value)
{
	*((volatile unsigned int *) (GPIO_REGS_BASE + reg_offset)) = value;
}

static void reg_bitset (unsigned int reg_offset, unsigned int bitmask)
{
	gpio_reg_write (reg_offset, gpio_reg_read (reg_offset) | bitmask);
}

static void reg_bitclear (unsigned int reg_offset, unsigned int bitmask)
{
	gpio_reg_write (reg_offset, gpio_reg_read (reg_offset) & ~bitmask);
}


/****************************************************************************
****************************************************************************/

int gpio_is_input (gpio_pin_t pin)
{
	unsigned int mask = GPIO_PIN2BITMASK(pin);
	unsigned int block = GPIO_PIN2BLOCK(pin);
	unsigned int pio_dir = (block == 0) ? REG_PIO_DIR : REG_PIO_DIR2;
	unsigned int pio_sel = (block == 0) ? REG_PIO_SEL : REG_PIO_SEL2;
	unsigned int dir, sel;

	sel = gpio_reg_read (pio_sel) & mask ? 1 : 0;		/* Sel: 0 == GPIO mode, 1 == Alternative function */
	dir = gpio_reg_read (pio_dir) & mask ? 1 : 0;		/* Dir: 0 == Input, 1 == Output */

	return ((sel == 0) && (dir == 0)) ? 1 : 0;
}

void gpio_set_as_input (gpio_pin_t pin)
{
	unsigned int mask = GPIO_PIN2BITMASK(pin);
	unsigned int block = GPIO_PIN2BLOCK(pin);
	unsigned int pio_dir = (block == 0) ? REG_PIO_DIR : REG_PIO_DIR2;
	unsigned int pio_sel = (block == 0) ? REG_PIO_SEL : REG_PIO_SEL2;

	reg_bitclear (pio_dir, mask);
	reg_bitclear (pio_sel, mask);
}

void gpio_set_as_output (gpio_pin_t pin, int initial_value)
{
	unsigned int mask = GPIO_PIN2BITMASK(pin);
	unsigned int block = GPIO_PIN2BLOCK(pin);
	unsigned int pio_out = (block == 0) ? REG_PIO_OUT : REG_PIO_OUT2;
	unsigned int pio_dir = (block == 0) ? REG_PIO_DIR : REG_PIO_DIR2;
	unsigned int pio_sel = (block == 0) ? REG_PIO_SEL : REG_PIO_SEL2;

	if (initial_value)
		reg_bitset (pio_out, mask);
	else
		reg_bitclear (pio_out, mask);

	reg_bitset (pio_dir, mask);
	reg_bitclear (pio_sel, mask);
}

void gpio_set_high (gpio_pin_t pin)
{
	gpio_set_as_output (pin, 1);
}

void gpio_set_low (gpio_pin_t pin)
{
	gpio_set_as_output (pin, 0);
}

void gpio_set_as_function (gpio_pin_t pin)
{
	unsigned int mask = GPIO_PIN2BITMASK(pin);
	unsigned int block = GPIO_PIN2BLOCK(pin);
	unsigned int pio_sel = (block == 0) ? REG_PIO_SEL : REG_PIO_SEL2;

	reg_bitset (pio_sel, mask);
}

unsigned int gpio_get_value (gpio_pin_t pin)
{
	unsigned int mask = GPIO_PIN2BITMASK(pin);
	unsigned int block = GPIO_PIN2BLOCK(pin);
	unsigned int pio_in = (block == 0) ? REG_PIO_IN : REG_PIO_IN2;

	/*
	   Note: If pin is in function mode, then input reg contains the value
	         currently being output (not the physical state of the pin).
		 ie we can't detect conflicts for function mode pins as we can
		 do for normal GPIO pins.
	*/
	return (gpio_reg_read (pio_in) & mask) ? 1 : 0;
}

