#include <linux/module.h>
#include <linux/types.h>
#include <asm-generic/io.h>

#define GPIO_BASE 0x500;

// pin 0 (GPIO 21) is controlled by an specific bit of a different set of ports:
#define PIN0_FUNCTION  GPIO_BASE + 0x2;
#define PIN0_DIRECTION GPIO_BASE + 0x6;
#define PIN0_STATUS    GPIO_BASE + 0xE;
#define PIN0_BIT       5;

  // pins 1 to 7 (GPIO 33 to 39) correspond to the respective bit on these ports
#define PINX_FUNCTION  GPIO_BASE + 0x30;
#define PINX_DIRECTION GPIO_BASE + 0x34;
#define PINX_STATUS    GPIO_BASE + 0x38;


static int jwnf98_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
	unsigned long dir_add;
	unsigned bit_off;
	u8 byte;
	if (off)
	{
		dir_add = PINX_DIRECTION;
		bit_off = off;
	}
	else
	{
		dir_add = PIN0_DIRECTION;
		bit_off = PIN0_BIT;
	}
	byte = inb(dir_add);
	byte |= (1 << bit_off);
	outb(byte, dir_add);
}

static int jwnf98_gpio_direction_output(struct gpio_chip *gc, unsigned off, int val)
{
	unsigned long dir_add;
	unsigned long val_add;
	unsigned bit_off;
	u8 byte;
	if (off)
	{
		dir_add = PINX_DIRECTION;
		val_add = PINX_STATUS;
		bit_off = off;
	}
	else
	{
		dir_add = PIN0_DIRECTION;
		val_add = PIN0_STATUS;
		bit_off = PIN0_BIT;
	}
	byte = inb(dir_add);
	byte &= ~(1 << bit_off);
	outb(byte, dir_add);
	if (val)
	{
		byte = inb(val_add);
		byte |= (1 << bit_off);
		outb(byte, val_add);
	}
	else
	{
		byte = inb(val_add);
		byte |= (1 << bit_off);
		outb(byte, val_add);
	}
}

static int jwnf98_gpio_get(struct gpio_chip *gc, unsigned off)
{
/* 
  Nothing here, waiting for support team answer.
  Maybe read from the same address than output?
*/
}

static void jwnf98_gpio_set(struct gpio_chip *gc, unsigned off, int val)
{
	unsigned long add;
	unsigned bit;
	u8 byte;
	if (off)
	{
		add = PINX_STATUS;
		bit = off;
	}
	else
	{
		add = PIN0_STATUS;
		bit = PIN0_BIT;
	}
	byte = inb(add);
	if (val)
		byte |= (1 << bit);
	else
		byte &= ~(1 << bit);
	outb(byte, add);
}

static struct gpio_chip gpio_pins = {
	.label = "jwnf98",
	.owner = THIS_MODULE,
	.direction_input  = jwnf98_gpio_direction_input,
	.get = jwnf98_gpio_get,
	.direction_output = jwnf98_gpio_direction_output,
	.set = jwnf98_gpio_set,
	.dbg_show = jwnf98_gpio_dbg_show,
	.can_sleep = 0,
};

static int __init jwnf98_gpio_init(void)
{
	/* 
	Do preliminar work here:
		- Request ports?
		- Create the chip here instead of let it be static? How?
		- Something else?
	*/
}

static void __exit jwnf98_gpio_exit(void)
{
	/* 
	Do cleanup work here:
		- Release ports?
		- Delete the chip if it was created on init function 
		  instead of being static? How?
		- Something else?
	*/
}

module_init(jwnf98_gpio_init);
module_exit(jwnf98_gpio_exit);

MODULE_DESCRIPTION("Jetway NF98 GPIO driver");
MODULE_LICENSE("GPL");
