/*
 * La Fonera i2c adapter on SW1 GPIO
 *
 * Copyright (C) 2007 Denis Bodor <lefinnois@lefinnois.net>
 * Based on i2c-parport-light.c by Jean Delvare
 * Based on i2c_gpio by olg & Yoshinori Sato
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Fonera GPIO Description
 *
 * 0    TP3 
 * 1    pin 5 of SW1
 * 2    WLAN LED
 * 3    pin 1 of SW1
 * 4    pin 2 of SW1
 * 5    RESET (!)
 * 6    RESET button
 * 7    pin 6 of SW1
 */

#include <linux/autoconf.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* for copy_from_user */
#include "ar531xlnx.h"

#define DRIVER_VERSION "0.0.5"

#define AR5315_DSLBASE          0xB1000000
#define AR5315_GPIO_DI          (AR5315_DSLBASE + 0x0088) /* intput register */
#define AR5315_GPIO_DO          (AR5315_DSLBASE + 0x0090) /* output register */
#define AR5315_GPIO_CR          (AR5315_DSLBASE + 0x0098) /* control register */
#define AR5315_GPIO_INT         (AR5315_DSLBASE + 0x00a0)

/* GPIO Control Register bit field definitions */
#define GPIO_CR_M(x)                (1 << (x)) /* mask for i/o */
#define GPIO_CR_O(x)                (1 << (x)) /* output mask */
#define GPIO_CR_I(x)                (0 << (x)) /* input mask */

#define NAME "gpio_i2c"

#define GPIO_SW1_1  3
#define GPIO_SW1_2  4
#define GPIO_SW1_5  1
#define GPIO_SW1_6  7

static int i2c_sda_o = GPIO_SW1_1;
static int i2c_sda_i = GPIO_SW1_2;
static int i2c_scl_o = GPIO_SW1_5;
static int i2c_scl_i = GPIO_SW1_6;
static int inverted = 0;

#define GPIO_CLOCK (1 << i2c_scl_i)
#define GPIO_DATA  (1 << i2c_sda_i)
#define GPIO_CLOCK_OUT (1 << i2c_scl_o)
#define GPIO_DATA_OUT (1 << i2c_sda_o)

/* GPIO low level functions */

/* set a GPIO bit */
static void bit_gpio_set(u32 mask) {
  u32 reg = 0;
  reg = sysRegRead(AR5315_GPIO_DO);
  if(inverted) {
    reg = reg & ~mask; /* clear bit */
  } else {
    reg = reg | mask; /* set bit */
  }
  sysRegWrite(AR5315_GPIO_DO,reg);
}

/* clear a GPIO bit */
static void bit_gpio_clear(u32 mask) {
  u32 reg = 0;
  reg = sysRegRead(AR5315_GPIO_DO);
  if(inverted) {
    reg = reg | mask; /* set bit */
  } else {
    reg = reg & ~mask; /* clear bit */
  }
  sysRegWrite(AR5315_GPIO_DO,reg);
}

/* get a GPIO bit */
static int bit_gpio_get(u32 mask) {
  return (sysRegRead(AR5315_GPIO_DI) & mask);
}

/* ----- I2C algorithm call-back functions and structures ----------------- */
static void bit_gpio_setscl(void *data, int state) {
        if (state) {
                bit_gpio_set(GPIO_CLOCK_OUT);
        } else {
                bit_gpio_clear(GPIO_CLOCK_OUT);
        }
}

static void bit_gpio_setsda(void *data, int state) {
        if (state) {
                bit_gpio_set(GPIO_DATA_OUT);
        } else {
                bit_gpio_clear(GPIO_DATA_OUT);
        }
}

static int bit_gpio_getscl(void *data) {
        return bit_gpio_get(GPIO_CLOCK);
}

static int bit_gpio_getsda(void *data) {
        return bit_gpio_get(GPIO_DATA);
}

/* Encapsulate the functions above in the correct structure
   Note that getscl will be set to NULL by the attaching code for adapters
   that cannot read SCL back */
static struct i2c_algo_bit_data i2c_gpio_algo_data = {
	.setsda		= bit_gpio_setsda,
	.setscl		= bit_gpio_setscl,
	.getsda		= bit_gpio_getsda,
	.getscl		= bit_gpio_getscl,
  600,   /* udelay, half-clock-cycle time in microsecs, i.e. clock is (500 / udelay) KHz */ 
  600,   /* mdelay, in millisecs, unused                                                 */
  100,   /* timeout, in jiffies                                                          */
         /* delays are high, Fonera's GPIO are very slow                                 */
}; 

/* ----- I2c structure ---------------------------------------------------- */
static struct i2c_adapter i2c_gpio_adapter = {
	.owner		= THIS_MODULE,
	.algo_data	= &i2c_gpio_algo_data,
	.name		= "GPIO adapter",
};

/* ----- Module loading, unloading and information ------------------------ */
static __init int i2c_gpio_init(void)
{
    u32 regCR=0;

    regCR = sysRegRead(AR5315_GPIO_CR);
    regCR = regCR | GPIO_CLOCK_OUT;
    regCR = regCR | GPIO_DATA_OUT;
    regCR = regCR & ~GPIO_CLOCK;
    regCR = regCR & ~GPIO_DATA;
    sysRegWrite(AR5315_GPIO_CR,regCR);

    /* Reset hardware to a sane state (SCL and SDA high) */
    bit_gpio_setsda(NULL,1);
    bit_gpio_setscl(NULL,1);

    if (i2c_bit_add_bus(&i2c_gpio_adapter) < 0) {
      printk(KERN_ERR NAME ": adapter %s registration failed\n",
          i2c_gpio_adapter.name);
      return -ENODEV;
    }

		printk (KERN_NOTICE NAME ": module loaded\n");

    if(inverted) {
      printk (KERN_NOTICE NAME ": inverted outputs\n");
    } else {
      printk (KERN_NOTICE NAME ": no inverted outputs\n");
    }
		return 0;
}

static void i2c_gpio_exit(void)
{
  i2c_bit_del_bus(&i2c_gpio_adapter);
	printk(KERN_INFO NAME ": unloaded\n");
}

module_init(i2c_gpio_init);
module_exit(i2c_gpio_exit);

MODULE_AUTHOR("Denis Bodor <lefinnois@lefinnois.net>");
MODULE_DESCRIPTION("AR2315 GPIO i2c bus v" DRIVER_VERSION);
MODULE_LICENSE("GPL");

module_param(inverted, int, 0);
MODULE_PARM_DESC(inverted, "inverted outputs");

