[PATCH -mm] Blackfin: blackfin i2c driver
From: Wu, Bryan
Date: Tue Mar 06 2007 - 01:56:52 EST
Hi folks,
[PATCH] Blackfin: blackfin i2c driver
The i2c linux driver for blackfin architecture which supports both GPIO
i2c operation and blackfin on-chip TWI controller i2c operation.
Signed-off-by: Bryan Wu <bryan.wu@xxxxxxxxxx>
---
drivers/i2c/busses/Kconfig | 47 ++++
drivers/i2c/busses/i2c-bfin-gpio.c | 98 ++++++++++
drivers/i2c/busses/i2c-bfin-twi.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 675 insertions(+)
Index: linux-2.6/drivers/i2c/busses/Kconfig
===================================================================
--- linux-2.6.orig/drivers/i2c/busses/Kconfig 2007-03-05 11:20:04.000000000 +0800
+++ linux-2.6/drivers/i2c/busses/Kconfig 2007-03-06 14:46:46.000000000 +0800
@@ -5,6 +5,53 @@
menu "I2C Hardware Bus support"
depends on I2C
+config I2C_BFIN_GPIO
+ tristate "Generic Blackfin and HHBF533/561 development board I2C support"
+ depends on I2C && EXPERIMENTAL
+ select I2C_ALGOBIT
+ help
+ --
+
+menu "BFIN I2C SDA/SCL Selection"
+ depends on I2C_BFIN_GPIO
+config BFIN_SDA
+ int "SDA is GPIO Number"
+ range 0 15 if (BF533 || BF532 || BF531)
+ range 0 47 if (BF534 || BF536 || BF537)
+ range 0 47 if BF561
+ default 2 if (BF533 || BF532 || BF531)
+
+config BFIN_SCL
+ int "SCL is GPIO Number"
+ range 0 15 if (BF533 || BF532 || BF531)
+ range 0 47 if (BF534 || BF536 || BF537)
+ range 0 47 if BF561
+ default 3
+endmenu
+
+config I2C_BFIN_GPIO_CYCLE_DELAY
+ int "Cycle Delay in usec"
+ depends on I2C_BFIN_GPIO
+ range 1 100
+ default 40
+
+config I2C_BFIN_TWI
+ tristate "Blackfin TWI I2C support"
+ depends on I2C && (BF534 || BF536 || BF537)
+ help
+ This the TWI I2C device driver for Blackfin 534/536/537.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-bfin-twi.
+
+config TWICLK_KHZ
+ int "TWI clock (kHZ)"
+ depends on I2C_BFIN_TWI
+ default 50
+ help
+ The unit of the TWI clock is kilo HZ. Please divide the clock
+ by 1024 if you count it in HZ. The value should be less than 400.
+
config I2C_ALI1535
tristate "ALI 1535"
depends on I2C && PCI
Index: linux-2.6/drivers/i2c/busses/i2c-bfin-gpio.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/i2c/busses/i2c-bfin-gpio.c 2007-03-06 14:47:31.000000000 +0800
@@ -0,0 +1,98 @@
+/****************************************************************
+ * Description: *
+ * *
+ * Maintainer: Meihui Fan <mhfan@xxxxxxxx> *
+ * *
+ * CopyRight (c) 2004 HHTech *
+ * www.hhcn.com, www.hhcn.org *
+ * All rights reserved. *
+ * *
+ * This file is free software; *
+ * you are free to modify and/or redistribute it *
+ * under the terms of the GNU General Public Licence (GPL). *
+ * *
+ ****************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/blackfin.h>
+#include <asm/gpio.h>
+
+#define I2C_HW_B_HHBF 0x13
+
+static void hhbf_setsda(void *data, int state)
+{
+ if (state) {
+ gpio_direction_input(CONFIG_BFIN_SDA);
+
+ } else {
+ gpio_direction_output(CONFIG_BFIN_SDA);
+ gpio_set_value(CONFIG_BFIN_SDA, 0);
+ }
+}
+
+static void hhbf_setscl(void *data, int state)
+{
+ gpio_set_value(CONFIG_BFIN_SCL, state);
+}
+
+static int hhbf_getsda(void *data)
+{
+ return (gpio_get_value(CONFIG_BFIN_SDA) != 0);
+}
+
+
+static struct i2c_algo_bit_data bit_hhbf_data = {
+ .setsda = hhbf_setsda,
+ .setscl = hhbf_setscl,
+ .getsda = hhbf_getsda,
+ .udelay = CONFIG_I2C_BFIN_GPIO_CYCLE_DELAY,
+ .timeout = HZ
+};
+
+static struct i2c_adapter hhbf_ops = {
+ .owner = THIS_MODULE,
+ .id = I2C_HW_B_HHBF,
+ .algo_data = &bit_hhbf_data,
+ .name = "HHBF I2C driver",
+};
+
+static int __init i2c_hhbf_init(void)
+{
+
+ if(gpio_request(CONFIG_BFIN_SCL, NULL)) {
+ printk(KERN_ERR "%s: gpio_request GPIO %d failed \n",__FUNCTION__, CONFIG_BFIN_SCL);
+ return -1;
+ }
+
+ if(gpio_request(CONFIG_BFIN_SDA, NULL)) {
+ printk(KERN_ERR "%s: gpio_request GPIO %d failed \n",__FUNCTION__, CONFIG_BFIN_SDA);
+ return -1;
+ }
+
+
+ gpio_direction_output(CONFIG_BFIN_SCL);
+ gpio_direction_input(CONFIG_BFIN_SDA);
+ gpio_set_value(CONFIG_BFIN_SCL, 1);
+
+ return i2c_bit_add_bus(&hhbf_ops);
+}
+
+static void __exit i2c_hhbf_exit(void)
+{
+ gpio_free(CONFIG_BFIN_SCL);
+ gpio_free(CONFIG_BFIN_SDA);
+ i2c_bit_del_bus(&hhbf_ops);
+}
+
+MODULE_AUTHOR("Meihui Fan <mhfan@xxxxxxxx>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for Blackfin and HHBF Boards");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_hhbf_init);
+module_exit(i2c_hhbf_exit);
Index: linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c 2007-03-06 14:46:46.000000000 +0800
@@ -0,0 +1,530 @@
+/****************************************************************
+ * Description: Driver for Blackfin Two Wire Interface *
+ * Maintainer: sonicz <sonic.zhang@xxxxxxxxxx> *
+ * *
+ * Copyright (c) 2005-2007 Analog Device *
+ * *
+ * This file is free software; *
+ * you are free to modify and/or redistribute it *
+ * under the terms of the GNU General Public Licence (GPL). *
+ ****************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+
+#include <asm/blackfin.h>
+#include <asm/irq.h>
+
+#define I2C_BFIN_TWI 0x00
+#define POLL_TIMEOUT (2 * HZ)
+#ifndef CONFIG_TWICLK_KHZ
+#define CONFIG_TWICLK_KHZ 400
+#endif
+
+/* SMBus mode*/
+#define TWI_I2C_MODE_STANDARD 0x01
+#define TWI_I2C_MODE_STANDARDSUB 0x02
+#define TWI_I2C_MODE_COMBINED 0x04
+
+struct bfin_twi_iface
+{
+ struct semaphore twi_lock;
+ int irq;
+ spinlock_t lock;
+ char read_write;
+ u8 command;
+ u8 *transPtr;
+ int readNum;
+ int writeNum;
+ int cur_mode;
+ int manual_stop;
+ int result;
+ int timeout_count;
+ struct timer_list timeout_timer;
+ struct i2c_adapter adap;
+ struct completion complete;
+};
+
+static struct bfin_twi_iface twi_iface;
+
+static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
+{
+ unsigned short twi_int_stat = bfin_read_TWI_INT_STAT();
+ unsigned short mast_stat = bfin_read_TWI_MASTER_STAT();
+
+ if (XMTSERV & twi_int_stat) {
+ /* Transmit next data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ }
+ /* start receive immediately after complete sending in combine mode. */
+ else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MDIR | RSTART);
+ } else if (iface->manual_stop)
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | STOP);
+ SSYNC();
+ /* Clear status */
+ bfin_write_TWI_INT_STAT(XMTSERV);
+ SSYNC();
+ }
+ if (RCVSERV & twi_int_stat) {
+ if (iface->readNum > 0) {
+ /* Receive next data */
+ *(iface->transPtr) = bfin_read_TWI_RCV_DATA8();
+ if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ /* Change combine mode into sub mode after read first data. */
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ /* Get read number from first byte in block combine mode. */
+ if (iface->readNum == 1 && iface->manual_stop)
+ iface->readNum = *iface->transPtr+1;
+ }
+ iface->transPtr++;
+ iface->readNum--;
+ } else if (iface->manual_stop) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | STOP);
+ SSYNC();
+ }
+ /* Clear interrupt source */
+ bfin_write_TWI_INT_STAT(RCVSERV);
+ SSYNC();
+ }
+ if (MERR & twi_int_stat) {
+ bfin_write_TWI_INT_STAT(MERR);
+ bfin_write_TWI_INT_MASK(0);
+ bfin_write_TWI_MASTER_STAT(0x3e);
+ bfin_write_TWI_MASTER_CTL(0);
+ SSYNC();
+ iface->result = -1;
+ /* if both err and complete int stats are set, return proper results. */
+ if (MCOMP & twi_int_stat) {
+ bfin_write_TWI_INT_STAT(MCOMP);
+ bfin_write_TWI_INT_MASK(0);
+ bfin_write_TWI_MASTER_CTL(0);
+ SSYNC();
+ /* If it is a quick transfer, only address bug no data, not an err, return 1. */
+ if (iface->writeNum == 0 && (mast_stat & BUFRDERR))
+ iface->result = 1;
+ /* If address not acknowledged return -1, else return 0. */
+ else if (!(mast_stat & ANAK))
+ iface->result = 0;
+ }
+ complete(&iface->complete);
+ return;
+ }
+ if (MCOMP & twi_int_stat) {
+ bfin_write_TWI_INT_STAT(MCOMP);
+ SSYNC();
+ if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ if (iface->readNum == 0) {
+ /* set the read number to 1 and ask for manual stop in block combine mode */
+ iface->readNum = 1;
+ iface->manual_stop = 1;
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | (0xff << 6));
+ } else {
+ /* set the readd number in other combine mode. */
+ bfin_write_TWI_MASTER_CTL((bfin_read_TWI_MASTER_CTL() & (~(0xff << 6))) | ( iface->readNum << 6));
+ }
+ /* remove restart bit and enable master receive */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() & ~RSTART);
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | MDIR);
+ SSYNC();
+ } else {
+ iface->result = 1;
+ bfin_write_TWI_INT_MASK(0);
+ bfin_write_TWI_MASTER_CTL(0);
+ SSYNC();
+ complete(&iface->complete);
+ }
+ }
+}
+
+/* Interrupt handler */
+static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
+{
+ struct bfin_twi_iface *iface = (struct bfin_twi_iface *)dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iface->lock, flags);
+ del_timer(&iface->timeout_timer);
+ bfin_twi_handle_interrupt(iface);
+ spin_unlock_irqrestore(&iface->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void bfin_twi_timeout(unsigned long data)
+{
+ struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iface->lock, flags);
+ bfin_twi_handle_interrupt(iface);
+ if (iface->result == 0) {
+ iface->timeout_count--;
+ if (iface->timeout_count > 0) {
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+ } else {
+ iface->result = -1;
+ complete(&iface->complete);
+ }
+ }
+ spin_unlock_irqrestore(&iface->lock, flags);
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int bfin_twi_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct bfin_twi_iface* iface = (struct bfin_twi_iface*)adap->algo_data;
+ struct i2c_msg *pmsg;
+ int i, ret;
+ int rc = 0;
+
+ if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ return -ENXIO;
+
+ down(&iface->twi_lock);
+
+ while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
+ up(&iface->twi_lock);
+ schedule();
+ down(&iface->twi_lock);
+ }
+
+ ret = 0;
+ for (i = 0; rc >= 0 && i < num;) {
+ pmsg = &msgs[i++];
+ if (pmsg->flags & I2C_M_TEN) {
+ printk(KERN_ERR "i2c-bfin-twi: 10 bits addr not supported !\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ iface->manual_stop = 0;
+ iface->transPtr = pmsg->buf;
+ iface->writeNum = iface->readNum = pmsg->len;
+ iface->result = 0;
+ iface->timeout_count = 10;
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(pmsg->addr);
+
+ /* FIFO Initiation. Data in FIFO should be discarded before start a new operation.*/
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+ SSYNC();
+
+ if (pmsg->flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
+ }
+ }
+
+ /* clear int stat */
+ bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+
+ /* Interrupt mask . Enable XMT, RCV interrupt */
+ bfin_write_TWI_INT_MASK(MCOMP | MERR | ((iface->read_write == I2C_SMBUS_READ)? RCVSERV : XMTSERV));
+ SSYNC();
+
+ if (pmsg->len > 0 && pmsg->len <= 255)
+ bfin_write_TWI_MASTER_CTL(( pmsg->len << 6 ));
+ else if (pmsg->len > 255) {
+ bfin_write_TWI_MASTER_CTL(( 0xff << 6 ));
+ iface->manual_stop = 1;
+ } else
+ break;
+
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | ((CONFIG_TWICLK_KHZ>100) ? FAST : 0));
+ SSYNC();
+
+ wait_for_completion(&iface->complete);
+
+ rc = iface->result;
+ if (rc == 1)
+ ret++;
+ else if (rc == -1)
+ break;
+ }
+
+ /* Release sem */
+ up(&iface->twi_lock);
+
+ return ret;
+}
+
+/*
+ * SMBus type transfer entrypoint
+ */
+
+int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data * data)
+{
+ struct bfin_twi_iface* iface = (struct bfin_twi_iface*)adap->algo_data;
+ int rc = 0;
+
+ if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ return -ENXIO;
+
+ down(&iface->twi_lock);
+
+ while(bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
+ up(&iface->twi_lock);
+ schedule();
+ down(&iface->twi_lock);
+ }
+
+ iface->writeNum = 0;
+ iface->readNum = 0;
+
+ /* Prepare datas & select mode */
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ iface->transPtr = NULL;
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ break;
+ case I2C_SMBUS_BYTE:
+ if (data == NULL)
+ iface->transPtr = NULL;
+ else {
+ if (read_write == I2C_SMBUS_READ)
+ iface->readNum = 1;
+ else
+ iface->writeNum = 1;
+ iface->transPtr = &data->byte;
+ }
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 1;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = 1;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = &data->byte;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = (u8 *)&data->word;
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ iface->writeNum = 2;
+ iface->readNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ iface->transPtr = (u8 *)&data->word;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 0;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = data->block[0]+1;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = data->block;
+ break;
+ default:
+ return -1;
+ }
+
+ iface->result = 0;
+ iface->manual_stop = 0;
+ iface->read_write = read_write;
+ iface->command = command;
+ iface->timeout_count = 10;
+
+ /* FIFO Initiation. Data in FIFO should be discarded before start a new operation.*/
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+
+ /* clear int stat */
+ bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(addr);
+ SSYNC();
+
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+
+ switch (iface->cur_mode) {
+ case TWI_I2C_MODE_STANDARDSUB:
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_INT_MASK(MCOMP | MERR | ((iface->read_write == I2C_SMBUS_READ) ? RCVSERV : XMTSERV));
+ SSYNC();
+
+ if (iface->writeNum + 1 <= 255)
+ bfin_write_TWI_MASTER_CTL(((iface->writeNum+1) << 6));
+ else {
+ bfin_write_TWI_MASTER_CTL(( 0xff << 6 ));
+ iface->manual_stop = 1;
+ }
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | ((CONFIG_TWICLK_KHZ>100) ? FAST : 0));
+ break;
+ case TWI_I2C_MODE_COMBINED:
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV);
+ SSYNC();
+
+ if (iface->writeNum > 0)
+ bfin_write_TWI_MASTER_CTL(((iface->writeNum+1) << 6));
+ else
+ bfin_write_TWI_MASTER_CTL(( 0x1 << 6 ));
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | ((CONFIG_TWICLK_KHZ>100) ? FAST : 0));
+ break;
+ default:
+ bfin_write_TWI_MASTER_CTL(0);
+ if (size != I2C_SMBUS_QUICK) {
+ /* Don't access xmit data register when this is a read operation. */
+ if (iface->read_write != I2C_SMBUS_READ) {
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ if (iface->writeNum <= 255)
+ bfin_write_TWI_MASTER_CTL(( iface->writeNum << 6 ));
+ else {
+ bfin_write_TWI_MASTER_CTL(( 0xff << 6 ));
+ iface->manual_stop = 1;
+ }
+ iface->writeNum--;
+ } else {
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_MASTER_CTL(( 1 << 6 ));
+ }
+ } else {
+ if (iface->readNum > 0 && iface->readNum <= 255)
+ bfin_write_TWI_MASTER_CTL(( iface->readNum << 6 ));
+ else if (iface->readNum > 255) {
+ bfin_write_TWI_MASTER_CTL(( 0xff << 6 ));
+ iface->manual_stop = 1;
+ } else {
+ del_timer(&iface->timeout_timer);
+ break;
+ }
+ }
+ }
+ bfin_write_TWI_INT_MASK(MCOMP | MERR | ((iface->read_write == I2C_SMBUS_READ) ? RCVSERV : XMTSERV));
+ SSYNC();
+
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | ((CONFIG_TWICLK_KHZ > 100) ? FAST : 0));
+ break;
+ }
+ SSYNC();
+
+ wait_for_completion(&iface->complete);
+
+ rc = (iface->result >= 0) ? 0 : -1;
+
+ /* Release sem */
+ up(&iface->twi_lock);
+
+ return rc;
+}
+
+/*
+ * Return what the adapter supports
+ */
+static u32 bfin_twi_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_I2C;
+}
+
+
+static struct i2c_algorithm bfin_twi_algorithm = {
+ .master_xfer = bfin_twi_master_xfer,
+ .smbus_xfer = bfin_twi_smbus_xfer,
+ .functionality = bfin_twi_functionality,
+};
+
+static int __init i2c_bfin_twi_init(void)
+{
+ struct i2c_adapter *p_adap;
+ int rc;
+
+ init_MUTEX(&(twi_iface.twi_lock));
+ spin_lock_init(&twi_iface.lock);
+ init_completion(&twi_iface.complete);
+ twi_iface.irq = IRQ_TWI;
+
+ init_timer(&twi_iface.timeout_timer);
+ twi_iface.timeout_timer.function = bfin_twi_timeout;
+ twi_iface.timeout_timer.data = (unsigned long)&twi_iface;
+
+ p_adap = &twi_iface.adap;
+ p_adap->id = I2C_BFIN_TWI;
+ strcpy(p_adap->name, "i2c-bfin-twi");
+ p_adap->algo = &bfin_twi_algorithm;
+ p_adap->algo_data = &twi_iface;
+ p_adap->client_register = NULL;
+ p_adap->client_unregister = NULL;
+ p_adap->class = I2C_CLASS_ALL;
+
+ rc = request_irq(twi_iface.irq, bfin_twi_interrupt_entry, SA_INTERRUPT, "i2c-bfin-twi", &twi_iface);
+ if (rc) {
+ printk(KERN_ERR "i2c-bfin-twi: can't get IRQ %d !\n", twi_iface.irq);
+ return -ENODEV;
+ }
+
+ /* Set TWI internal clock as 10MHz */
+ bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
+
+ /* Set Twi interface clock as specified */
+ if (CONFIG_TWICLK_KHZ > 400)
+ bfin_write_TWI_CLKDIV((( 5*1024 / 400 ) << 8) | (( 5*1024 / 400 ) & 0xFF));
+ else
+ bfin_write_TWI_CLKDIV((( 5*1024 / CONFIG_TWICLK_KHZ ) << 8) | (( 5*1024 / CONFIG_TWICLK_KHZ ) & 0xFF));
+
+ /* Enable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
+ SSYNC();
+
+ return i2c_add_adapter(p_adap);
+}
+
+static void __exit i2c_bfin_twi_exit(void)
+{
+ i2c_del_adapter(&twi_iface.adap);
+ free_irq(twi_iface.irq, &twi_iface);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@xxxxxxxxxx>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for Blackfin TWI");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_bfin_twi_init);
+module_exit(i2c_bfin_twi_exit);
_
Thanks
-Bryan Wu
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/