i2c-bfin-twi.c revision 991dee591a99d035796a8c194eb1796cc020e142
1d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 2d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * drivers/i2c/busses/i2c-bfin-twi.c 3d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 4d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Description: Driver for Blackfin Two Wire Interface 5d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 6d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Author: sonicz <sonic.zhang@analog.com> 7d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 8d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Copyright (c) 2005-2007 Analog Devices, Inc. 9d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 10d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * This program is free software; you can redistribute it and/or modify 11d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * it under the terms of the GNU General Public License as published by 12d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * the Free Software Foundation; either version 2 of the License, or 13d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * (at your option) any later version. 14d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 15d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * This program is distributed in the hope that it will be useful, 16d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * but WITHOUT ANY WARRANTY; without even the implied warranty of 17d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * GNU General Public License for more details. 19d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 20d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * You should have received a copy of the GNU General Public License 21d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * along with this program; if not, write to the Free Software 22d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 24d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 25d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/module.h> 26d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/kernel.h> 27d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/init.h> 28d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/i2c.h> 29d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/mm.h> 30d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/timer.h> 31d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/spinlock.h> 32d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/completion.h> 33d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/interrupt.h> 34d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/platform_device.h> 35d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 36d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <asm/blackfin.h> 37d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <asm/irq.h> 38d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 39d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#define POLL_TIMEOUT (2 * HZ) 40d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 41d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* SMBus mode*/ 42d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#define TWI_I2C_MODE_STANDARD 0x01 43d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#define TWI_I2C_MODE_STANDARDSUB 0x02 44d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#define TWI_I2C_MODE_COMBINED 0x04 45d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 46d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustruct bfin_twi_iface { 47d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int irq; 48d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spinlock_t lock; 49d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu char read_write; 50d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu u8 command; 51d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu u8 *transPtr; 52d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int readNum; 53d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int writeNum; 54d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int cur_mode; 55d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int manual_stop; 56d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int result; 57d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int timeout_count; 58d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct timer_list timeout_timer; 59d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_adapter adap; 60d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct completion complete; 61d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}; 62d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 63d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct bfin_twi_iface twi_iface; 64d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 65d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) 66d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 67d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned short twi_int_status = bfin_read_TWI_INT_STAT(); 68d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned short mast_stat = bfin_read_TWI_MASTER_STAT(); 69d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 70d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & XMTSERV) { 71d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Transmit next data */ 72d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) { 73d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_XMT_DATA8(*(iface->transPtr++)); 74d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum--; 75d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 76d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* start receive immediately after complete sending in 77d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * combine mode. 78d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 79d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { 80d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() 81d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu | MDIR | RSTART); 82d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else if (iface->manual_stop) 83d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() 84d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu | STOP); 85d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 86d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Clear status */ 87d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(XMTSERV); 88d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 89d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 90d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & RCVSERV) { 91d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum > 0) { 92d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Receive next data */ 93d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu *(iface->transPtr) = bfin_read_TWI_RCV_DATA8(); 94d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { 95d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Change combine mode into sub mode after 96d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * read first data. 97d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 98d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 99d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Get read number from first byte in block 100d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * combine mode. 101d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 102d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum == 1 && iface->manual_stop) 103d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = *iface->transPtr + 1; 104d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 105d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr++; 106d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum--; 107d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else if (iface->manual_stop) { 108d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() 109d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu | STOP); 110d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 111d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 112d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Clear interrupt source */ 113d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(RCVSERV); 114d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 115d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 116d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & MERR) { 117d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(MERR); 118d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(0); 119d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_STAT(0x3e); 120d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0); 121d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 122d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = -1; 123d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* if both err and complete int stats are set, return proper 124d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * results. 125d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 126d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & MCOMP) { 127d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(MCOMP); 128d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(0); 129d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0); 130d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 131d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* If it is a quick transfer, only address bug no data, 132d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * not an err, return 1. 133d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 134d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum == 0 && (mast_stat & BUFRDERR)) 135d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 1; 136d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* If address not acknowledged return -1, 137d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * else return 0. 138d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 139d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else if (!(mast_stat & ANAK)) 140d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 0; 141d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 142d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu complete(&iface->complete); 143d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return; 144d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 145d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & MCOMP) { 146d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(MCOMP); 147d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 148d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { 149d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum == 0) { 150d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* set the read number to 1 and ask for manual 151d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * stop in block combine mode 152d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 153d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 1; 154d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 155d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL( 156d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_read_TWI_MASTER_CTL() 157d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu | (0xff << 6)); 158d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 159d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* set the readd number in other 160d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * combine mode. 161d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 162d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL( 163d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu (bfin_read_TWI_MASTER_CTL() & 164d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu (~(0xff << 6))) | 165d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ( iface->readNum << 6)); 166d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 167d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* remove restart bit and enable master receive */ 168d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() & 169d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ~RSTART); 170d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | 171d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu MEN | MDIR); 172d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 173d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 174d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 1; 175d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(0); 176d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0); 177d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 178d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu complete(&iface->complete); 179d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 180d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 181d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 182d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 183d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* Interrupt handler */ 184d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id) 185d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 186d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = dev_id; 187d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned long flags; 188d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 189d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_lock_irqsave(&iface->lock, flags); 190d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu del_timer(&iface->timeout_timer); 191d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_twi_handle_interrupt(iface); 192d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_unlock_irqrestore(&iface->lock, flags); 193d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return IRQ_HANDLED; 194d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 195d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 196d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic void bfin_twi_timeout(unsigned long data) 197d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 198d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data; 199d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned long flags; 200d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 201d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_lock_irqsave(&iface->lock, flags); 202d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_twi_handle_interrupt(iface); 203d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->result == 0) { 204d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_count--; 205d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->timeout_count > 0) { 206d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; 207d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu add_timer(&iface->timeout_timer); 208d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 209d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = -1; 210d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu complete(&iface->complete); 211d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 212d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 213d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_unlock_irqrestore(&iface->lock, flags); 214d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 215d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 216d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 217d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Generic i2c master transfer entrypoint 218d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 219d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int bfin_twi_master_xfer(struct i2c_adapter *adap, 220d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_msg *msgs, int num) 221d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 222d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = adap->algo_data; 223d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_msg *pmsg; 224d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int i, ret; 225d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int rc = 0; 226d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 227d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (!(bfin_read_TWI_CONTROL() & TWI_ENA)) 228d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -ENXIO; 229d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 230d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) { 231d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu yield(); 232d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 233d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 234d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ret = 0; 235d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu for (i = 0; rc >= 0 && i < num; i++) { 236d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu pmsg = &msgs[i]; 237d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (pmsg->flags & I2C_M_TEN) { 238d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu dev_err(&(adap->dev), "i2c-bfin-twi: 10 bits addr " 239d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu "not supported !\n"); 240d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu rc = -EINVAL; 241d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 242d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 243d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 244d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARD; 245d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 0; 246d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = pmsg->buf; 247d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = iface->readNum = pmsg->len; 248d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 0; 249d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_count = 10; 250d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set Transmit device address */ 251d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_ADDR(pmsg->addr); 252d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 253d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* FIFO Initiation. Data in FIFO should be 254d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * discarded before start a new operation. 255d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 256d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_FIFO_CTL(0x3); 257d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 258d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_FIFO_CTL(0); 259d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 260d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 261d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (pmsg->flags & I2C_M_RD) 262d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->read_write = I2C_SMBUS_READ; 263d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 264d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->read_write = I2C_SMBUS_WRITE; 265d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Transmit first data */ 266d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) { 267d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_XMT_DATA8(*(iface->transPtr++)); 268d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum--; 269d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 270d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 271d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 272d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 273d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* clear int stat */ 274d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV); 275d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 276d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Interrupt mask . Enable XMT, RCV interrupt */ 277d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(MCOMP | MERR | 278d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ)? 279d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu RCVSERV : XMTSERV)); 280d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 281d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 282d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (pmsg->len > 0 && pmsg->len <= 255) 283d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(pmsg->len << 6); 284d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else if (pmsg->len > 255) { 285d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0xff << 6); 286d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 287d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else 288d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 289d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 290d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; 291d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu add_timer(&iface->timeout_timer); 292d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 293d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 294d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | 295d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | 296d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); 297d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 298d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 299d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu wait_for_completion(&iface->complete); 300d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 301d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu rc = iface->result; 302d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (rc == 1) 303d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ret++; 304d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else if (rc == -1) 305d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 306d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 307d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 308d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return ret; 309d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 310d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 311d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 312d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * SMBus type transfer entrypoint 313d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 314d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 315d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wuint bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, 316d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned short flags, char read_write, 317d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu u8 command, int size, union i2c_smbus_data *data) 318d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 319d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = adap->algo_data; 320d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int rc = 0; 321d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 322d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (!(bfin_read_TWI_CONTROL() & TWI_ENA)) 323d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -ENXIO; 324d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 325d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) { 326d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu yield(); 327d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 328d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 329d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 0; 330d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 0; 331d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 332d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Prepare datas & select mode */ 333d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu switch (size) { 334d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_QUICK: 335d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = NULL; 336d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARD; 337d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 338d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_BYTE: 339d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (data == NULL) 340d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = NULL; 341d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 342d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) 343d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 1; 344d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else 345d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 1; 346d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = &data->byte; 347d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 348d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARD; 349d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 350d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_BYTE_DATA: 351d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) { 352d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 1; 353d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 354d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 355d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 1; 356d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 357d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 358d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = &data->byte; 359d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 360d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_WORD_DATA: 361d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) { 362d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 2; 363d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 364d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 365d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 2; 366d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 367d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 368d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = (u8 *)&data->word; 369d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 370d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_PROC_CALL: 371d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 2; 372d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 2; 373d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 374d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = (u8 *)&data->word; 375d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 376d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_BLOCK_DATA: 377d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) { 378d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 0; 379d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 380d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 381d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = data->block[0] + 1; 382d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 383d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 384d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = data->block; 385d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 386d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu default: 387d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -1; 388d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 389d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 390d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 0; 391d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 0; 392d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->read_write = read_write; 393d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->command = command; 394d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_count = 10; 395d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 396d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* FIFO Initiation. Data in FIFO should be discarded before 397d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * start a new operation. 398d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 399d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_FIFO_CTL(0x3); 400d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 401d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_FIFO_CTL(0); 402d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 403d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* clear int stat */ 404d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV); 405d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 406d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set Transmit device address */ 407d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_ADDR(addr); 408d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 409d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 410d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; 411d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu add_timer(&iface->timeout_timer); 412d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 413d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu switch (iface->cur_mode) { 414d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case TWI_I2C_MODE_STANDARDSUB: 415d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_XMT_DATA8(iface->command); 416d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(MCOMP | MERR | 417d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? 418d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu RCVSERV : XMTSERV)); 419d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 420d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 421d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum + 1 <= 255) 422d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6); 423d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 424d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0xff << 6); 425d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 426d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 427d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 428d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | 429d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); 430d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 431d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case TWI_I2C_MODE_COMBINED: 432d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_XMT_DATA8(iface->command); 433d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV); 434d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 435d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 436d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) 437d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6); 438d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else 439d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0x1 << 6); 440d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 441d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | 442d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); 443d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 444d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu default: 445d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0); 446d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (size != I2C_SMBUS_QUICK) { 447d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Don't access xmit data register when this is a 448d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * read operation. 449d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 450d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->read_write != I2C_SMBUS_READ) { 451d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) { 452d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_XMT_DATA8(*(iface->transPtr++)); 453d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum <= 255) 454d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(iface->writeNum << 6); 455d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 456d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0xff << 6); 457d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 458d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 459d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum--; 460d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 461d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_XMT_DATA8(iface->command); 462d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(1 << 6); 463d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 464d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 465d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum > 0 && iface->readNum <= 255) 466d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(iface->readNum << 6); 467d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else if (iface->readNum > 255) { 468d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(0xff << 6); 469d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 470d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 471d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu del_timer(&iface->timeout_timer); 472d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 473d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 474d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 475d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 476d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_INT_MASK(MCOMP | MERR | 477d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? 478d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu RCVSERV : XMTSERV)); 479d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 480d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 481d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 482d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | 483d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | 484d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); 485d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 486d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 487d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 488d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 489d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu wait_for_completion(&iface->complete); 490d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 491d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu rc = (iface->result >= 0) ? 0 : -1; 492d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 493d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return rc; 494d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 495d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 496d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 497d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Return what the adapter supports 498d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 499d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic u32 bfin_twi_functionality(struct i2c_adapter *adap) 500d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 501d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 502d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 503d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | 504d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu I2C_FUNC_I2C; 505d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 506d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 507d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 508d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct i2c_algorithm bfin_twi_algorithm = { 509d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .master_xfer = bfin_twi_master_xfer, 510d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .smbus_xfer = bfin_twi_smbus_xfer, 511d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .functionality = bfin_twi_functionality, 512d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}; 513d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 514d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 515d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state) 516d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 517d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/ 518d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 519d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Disable TWI */ 520d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() & ~TWI_ENA); 521d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 522d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 523d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return 0; 524d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 525d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 526d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int i2c_bfin_twi_resume(struct platform_device *dev) 527d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 528d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/ 529d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 530d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Enable TWI */ 531d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA); 532d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 533d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 534d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return 0; 535d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 536d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 537d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int i2c_bfin_twi_probe(struct platform_device *dev) 538d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 539d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = &twi_iface; 540d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_adapter *p_adap; 541d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int rc; 542d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 543d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_lock_init(&(iface->lock)); 544d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu init_completion(&(iface->complete)); 545d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->irq = IRQ_TWI; 546d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 547d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu init_timer(&(iface->timeout_timer)); 548d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_timer.function = bfin_twi_timeout; 549d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->timeout_timer.data = (unsigned long)iface; 550d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 551d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap = &iface->adap; 552d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->id = I2C_HW_BLACKFIN; 553991dee591a99d035796a8c194eb1796cc020e142Kalle Pokki p_adap->nr = dev->id; 554d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu strlcpy(p_adap->name, dev->name, sizeof(p_adap->name)); 555d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->algo = &bfin_twi_algorithm; 556d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->algo_data = iface; 557d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->class = I2C_CLASS_ALL; 558d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->dev.parent = &dev->dev; 559d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 560d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu rc = request_irq(iface->irq, bfin_twi_interrupt_entry, 561d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu IRQF_DISABLED, dev->name, iface); 562d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (rc) { 563d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu dev_err(&(p_adap->dev), "i2c-bfin-twi: can't get IRQ %d !\n", 564d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->irq); 565d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -ENODEV; 566d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 567d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 568d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set TWI internal clock as 10MHz */ 569d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F); 570d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 571d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set Twi interface clock as specified */ 572d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_CLKDIV((( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ ) 573d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu << 8) | (( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ ) 574d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu & 0xFF)); 575d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 576d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Enable TWI */ 577d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA); 578d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 579d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 580991dee591a99d035796a8c194eb1796cc020e142Kalle Pokki rc = i2c_add_numbered_adapter(p_adap); 581d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (rc < 0) 582d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu free_irq(iface->irq, iface); 583d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else 584d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu platform_set_drvdata(dev, iface); 585d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 586d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return rc; 587d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 588d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 589d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int i2c_bfin_twi_remove(struct platform_device *pdev) 590d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 591d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = platform_get_drvdata(pdev); 592d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 593d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu platform_set_drvdata(pdev, NULL); 594d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 595d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu i2c_del_adapter(&(iface->adap)); 596d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu free_irq(iface->irq, iface); 597d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 598d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return 0; 599d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 600d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 601d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct platform_driver i2c_bfin_twi_driver = { 602d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .probe = i2c_bfin_twi_probe, 603d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .remove = i2c_bfin_twi_remove, 604d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .suspend = i2c_bfin_twi_suspend, 605d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .resume = i2c_bfin_twi_resume, 606d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .driver = { 607d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .name = "i2c-bfin-twi", 608d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .owner = THIS_MODULE, 609d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu }, 610d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}; 611d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 612d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int __init i2c_bfin_twi_init(void) 613d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 614d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu pr_info("I2C: Blackfin I2C TWI driver\n"); 615d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 616d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return platform_driver_register(&i2c_bfin_twi_driver); 617d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 618d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 619d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic void __exit i2c_bfin_twi_exit(void) 620d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 621d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu platform_driver_unregister(&i2c_bfin_twi_driver); 622d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 623d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 624d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan WuMODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); 625d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan WuMODULE_DESCRIPTION("I2C-Bus adapter routines for Blackfin TWI"); 626d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan WuMODULE_LICENSE("GPL"); 627d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 628d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wumodule_init(i2c_bfin_twi_init); 629d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wumodule_exit(i2c_bfin_twi_exit); 630