text: #include <linux/interrupt.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/delay.h>
  #include <linux/slab.h>
  #include <linux/init.h>
  #include <linux/errno.h>
  #include <linux/platform_device.h>
  #include <linux/i2c.h>
  #include <linux/io.h>
  
  #include <asm/rt2880/rt_mmap.h>
  #include "i2c-ralink.h"
  
  static int i2c_id;
  
  static inline unsigned char 
  construct_addr(struct i2c_msg *msg) 
  {
  	unsigned char addr;
  
  	addr = (msg->addr << 1);
  
  	if (msg->flags & I2C_M_RD)
  		addr |= 1;
  	return addr;   
  }
  
  static void 
  ralink_i2c_reset(void)
  {
  	u32 val;
  
          /* reset i2c block */
          val = RT2880_REG(RT2880_RSTCTRL_REG);
  	val = val | RALINK_I2C_RST;
          RT2880_REG(RT2880_RSTCTRL_REG) = val;
  
  	val = val & ~RALINK_I2C_RST;
          RT2880_REG(RT2880_RSTCTRL_REG) = val;
  } 
  
  static void 
  ralink_i2c_enable(struct i2c_msg *msg)
  {
  	RT2880_REG(RT2880_I2C_DEVADDR_REG) = msg->addr;
          RT2880_REG(RT2880_I2C_CONFIG_REG) = I2C_CFG_DEFAULT;
          RT2880_REG(RT2880_I2C_CLKDIV_REG) = CLKDIV_VALUE;
  }
  
  static int 
  ralink_i2c_wait_tx_done(void)
  {
  	while(!(RT2880_REG(RT2880_I2C_STATUS_REG) & RALINK_I2C_SDOEMPTY));
  	return 0;
  }
  
  static int 
  ralink_i2c_wait_rx_done(void)
  {
  	while(!(RT2880_REG(RT2880_I2C_STATUS_REG) & RALINK_I2C_DATARDY));
  	return 0;
  }
  
  static int 
  ralink_i2c_wait_idle(void)
  {
  	while(RT2880_REG(RT2880_I2C_STATUS_REG) & RALINK_I2C_BUSY);
  	return 0;
  }
  
  static int 
  ralink_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) 
  {
  	int i=0, j=0, pos=0;
  	int nblock = pmsg->len / READ_BLOCK;
          int rem = pmsg->len % READ_BLOCK;
  
  	if (pmsg->flags & I2C_M_TEN) {
  		printk("10 bits addr not supported\n");
  		return -EINVAL;
  	}
  
  	if ((pmsg->flags&I2C_M_RD)) {
  		for(i=0; i< nblock; i++) {
  			ralink_i2c_wait_idle();
  			RT2880_REG(RT2880_I2C_BYTECNT_REG) = READ_BLOCK-1;
  			RT2880_REG(RT2880_I2C_STARTXFR_REG) = READ_CMD;
  			for(j=0; j< READ_BLOCK; j++) {
  				ralink_i2c_wait_rx_done();
  				pmsg->buf[pos++] = RT2880_REG(RT2880_I2C_DATAIN_REG);
  			}
  		}
  
  		ralink_i2c_wait_idle();
  		RT2880_REG(RT2880_I2C_BYTECNT_REG) = rem-1;
  		RT2880_REG(RT2880_I2C_STARTXFR_REG) = READ_CMD;
  		for(i=0; i< rem; i++) {
  			ralink_i2c_wait_rx_done();
  			pmsg->buf[pos++] = RT2880_REG(RT2880_I2C_DATAIN_REG);
  		}
  	} else {
  		ralink_i2c_wait_idle();
  		RT2880_REG(RT2880_I2C_BYTECNT_REG) = pmsg->len-1;
  
  		for(i=0; i< pmsg->len; i++) {
  			RT2880_REG(RT2880_I2C_DATAOUT_REG) = pmsg->buf[i];
  
  			if(i==0) {
  				RT2880_REG(RT2880_I2C_STARTXFR_REG) = WRITE_CMD;
  			}
  			ralink_i2c_wait_tx_done();
  		}
  	}
  
  	return 0;
  }
  
  /*
   * master_xfer() - main read/write entry
   */
  static int 
  ralink_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, 
  				int num)
  {
  	int im = 0;
  	int ret = 0;
  
  	ralink_i2c_wait_idle();
  	ralink_i2c_reset();
  	ralink_i2c_enable(msgs);
  
  	for (im = 0; ret == 0 && im != num; im++) {
  		ret = ralink_i2c_handle_msg(i2c_adap, &msgs[im]);
  	}
  
  	if(ret)
  		return ret;
  
  	return im;   
  }
  
  static u32 
  ralink_i2c_func(struct i2c_adapter *adap)
  {
  	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
  }
  
  static const struct i2c_algorithm ralink_i2c_algo = {
  	.master_xfer	= ralink_i2c_master_xfer,
  	.functionality	= ralink_i2c_func,
  };
  
  static int 
  ralink_i2c_remove(struct platform_device *pdev)
  {
  	struct i2c_adapter *padapter = platform_get_drvdata(pdev);
  	struct i2c_algo_ralink_data *adapter_data = 
  		(struct i2c_algo_ralink_data *)padapter->algo_data;
  
  	release_mem_region(RALINK_I2C_BASE, 256);
  	kfree(adapter_data);
  	kfree(padapter);
  
  	platform_set_drvdata(pdev, NULL);
  
  	return 0;
  }
  
  static int 
  ralink_i2c_probe(struct platform_device *pdev)
  {
  	struct resource *res;
  	int ret;
  	struct i2c_adapter *new_adapter;
  	struct i2c_algo_ralink_data *adapter_data;
  
  
  	new_adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
  	if (!new_adapter) {
  		ret = -ENOMEM;
  		goto out;
  	}
  
  	adapter_data = kzalloc(sizeof(struct i2c_algo_ralink_data), GFP_KERNEL);
  	if (!adapter_data) {
  		ret = -ENOMEM;
  		goto free_adapter;
  	}
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res) {
  		ret = -ENODEV;
  		goto free_both;
  	}
  
  	if (!request_mem_region(RALINK_I2C_BASE, resource_size(res), pdev->name)) {
  		ret = -EBUSY;
  		goto free_both;
  	}
  
  	adapter_data->id = i2c_id++;
  
  	memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
  	new_adapter->owner = THIS_MODULE;
  	new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
  	new_adapter->dev.parent = &pdev->dev;
  	new_adapter->nr = pdev->id;
  
  	new_adapter->timeout = HZ;
  	new_adapter->algo = &ralink_i2c_algo;
  
  	init_waitqueue_head(&adapter_data->waitq);
  	spin_lock_init(&adapter_data->lock);
  
  	platform_set_drvdata(pdev, new_adapter);
  	new_adapter->algo_data = adapter_data;
  
  	i2c_add_numbered_adapter(new_adapter);
  
  	return 0;
  
  
  free_both:
  	kfree(adapter_data);
  
  free_adapter:
  	kfree(new_adapter);
  
  out:
  	return ret;
  }
  
  
  static struct platform_driver ralink_i2c_driver = {
  	.probe		= ralink_i2c_probe,
  	.remove		= ralink_i2c_remove,
  	.driver		= {
  		.owner	= THIS_MODULE,
  		.name	= "Ralink-I2C",
  	},
  };
  
  static int __init 
  i2c_ralink_init (void)
  {
  	printk("Ralink I2C Init\n");
  	return platform_driver_register(&ralink_i2c_driver);
  }
  
  static void __exit 
  i2c_ralink_exit (void)
  {
  	printk("Ralink I2C Exit\n");
  	platform_driver_unregister(&ralink_i2c_driver);
  }
  
  module_init (i2c_ralink_init);
  module_exit (i2c_ralink_exit);
  
  MODULE_AUTHOR("Steven Liu <steven_liu@mediatek.com>");
  MODULE_DESCRIPTION("Ralink I2c host driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:Ralink-I2C");
  