i2c-bfin-twi.c revision be2f80f0a3a333c0c00b2c8a7c2d74fcd66b75a2
1d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 2bd584996b092a019a3ac32fcde7c3851935add96Mike Frysinger * Blackfin On-Chip Two Wire Interface Driver 3d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 4bd584996b092a019a3ac32fcde7c3851935add96Mike Frysinger * Copyright 2005-2007 Analog Devices Inc. 5d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 6bd584996b092a019a3ac32fcde7c3851935add96Mike Frysinger * Enter bugs at http://blackfin.uclinux.org/ 7d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * 8bd584996b092a019a3ac32fcde7c3851935add96Mike Frysinger * Licensed under the GPL-2 or later. 9d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 10d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 11d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/module.h> 12d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/kernel.h> 13d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/init.h> 14d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/i2c.h> 155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 166df263cf2ee1c6dd9709488ecd3c7b3447511ecfMike Frysinger#include <linux/io.h> 17d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/mm.h> 18d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/timer.h> 19d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/spinlock.h> 20d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/completion.h> 21d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/interrupt.h> 22d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <linux/platform_device.h> 23d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 24d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <asm/blackfin.h> 2574d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu#include <asm/portmux.h> 26d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <asm/irq.h> 27d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 28d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* SMBus mode*/ 294dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_STANDARD 1 304dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_STANDARDSUB 2 314dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_COMBINED 3 324dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_REPEAT 4 33d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 34d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustruct bfin_twi_iface { 35d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int irq; 36d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spinlock_t lock; 37d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu char read_write; 38d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu u8 command; 39d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu u8 *transPtr; 40d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int readNum; 41d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int writeNum; 42d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int cur_mode; 43d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int manual_stop; 44d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int result; 45d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_adapter adap; 46d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct completion complete; 474dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang struct i2c_msg *pmsg; 484dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang int msg_num; 494dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang int cur_msg; 50958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich u16 saved_clkdiv; 51958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich u16 saved_control; 52aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu void __iomem *regs_base; 53d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}; 54d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 55aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 56aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu#define DEFINE_TWI_REG(reg, off) \ 57aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wustatic inline u16 read_##reg(struct bfin_twi_iface *iface) \ 58aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu { return bfin_read16(iface->regs_base + (off)); } \ 59aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wustatic inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \ 60aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu { bfin_write16(iface->regs_base + (off), v); } 61aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 62aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(CLKDIV, 0x00) 63aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(CONTROL, 0x04) 64aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(SLAVE_CTL, 0x08) 65aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(SLAVE_STAT, 0x0C) 66aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(SLAVE_ADDR, 0x10) 67aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(MASTER_CTL, 0x14) 68aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(MASTER_STAT, 0x18) 69aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(MASTER_ADDR, 0x1C) 70aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(INT_STAT, 0x20) 71aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(INT_MASK, 0x24) 72aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(FIFO_CTL, 0x28) 73aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(FIFO_STAT, 0x2C) 74aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(XMT_DATA8, 0x80) 75aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(XMT_DATA16, 0x84) 76aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(RCV_DATA8, 0x88) 77aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(RCV_DATA16, 0x8C) 78d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 7974d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wustatic const u16 pin_req[2][3] = { 8074d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu {P_TWI0_SCL, P_TWI0_SDA, 0}, 8174d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu {P_TWI1_SCL, P_TWI1_SDA, 0}, 8274d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu}; 8374d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu 845481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhangstatic void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, 855481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang unsigned short twi_int_status) 86d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 87aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu unsigned short mast_stat = read_MASTER_STAT(iface); 88d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 89d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & XMTSERV) { 90d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Transmit next data */ 91d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) { 925481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang SSYNC(); 93aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, *(iface->transPtr++)); 94d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum--; 95d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 96d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* start receive immediately after complete sending in 97d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * combine mode. 98d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 994dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) 100aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 101aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu read_MASTER_CTL(iface) | MDIR | RSTART); 1024dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else if (iface->manual_stop) 103aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 104aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu read_MASTER_CTL(iface) | STOP); 1054dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && 10694327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew iface->cur_msg + 1 < iface->msg_num) { 10794327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) 10894327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew write_MASTER_CTL(iface, 10994327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew read_MASTER_CTL(iface) | RSTART | MDIR); 11094327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew else 11194327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew write_MASTER_CTL(iface, 11294327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew (read_MASTER_CTL(iface) | RSTART) & ~MDIR); 11394327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew } 114d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 115d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & RCVSERV) { 116d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum > 0) { 117d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Receive next data */ 118aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu *(iface->transPtr) = read_RCV_DATA8(iface); 119d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { 120d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Change combine mode into sub mode after 121d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * read first data. 122d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 123d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 124d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Get read number from first byte in block 125d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * combine mode. 126d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 127d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum == 1 && iface->manual_stop) 128d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = *iface->transPtr + 1; 129d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 130d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr++; 131d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum--; 132d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else if (iface->manual_stop) { 133aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 134aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu read_MASTER_CTL(iface) | STOP); 1354dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && 13694327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew iface->cur_msg + 1 < iface->msg_num) { 13794327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) 13894327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew write_MASTER_CTL(iface, 13994327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew read_MASTER_CTL(iface) | RSTART | MDIR); 14094327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew else 14194327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew write_MASTER_CTL(iface, 14294327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew (read_MASTER_CTL(iface) | RSTART) & ~MDIR); 143d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 144d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 145d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & MERR) { 146aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_MASK(iface, 0); 147aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_STAT(iface, 0x3e); 148aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0); 1494dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->result = -EIO; 1505cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich 1515cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich if (mast_stat & LOSTARB) 1525cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich dev_dbg(&iface->adap.dev, "Lost Arbitration\n"); 1535cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich if (mast_stat & ANAK) 1545cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n"); 1555cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich if (mast_stat & DNAK) 1565cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n"); 1575cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich if (mast_stat & BUFRDERR) 1585cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich dev_dbg(&iface->adap.dev, "Buffer Read Error\n"); 1595cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich if (mast_stat & BUFWRERR) 1605cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich dev_dbg(&iface->adap.dev, "Buffer Write Error\n"); 1615cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich 162d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* if both err and complete int stats are set, return proper 163d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * results. 164d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 165d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & MCOMP) { 166dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang /* If it is a quick transfer, only address without data, 167d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * not an err, return 1. 168dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * If address is acknowledged return 1. 169d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 170dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang if ((iface->writeNum == 0 && (mast_stat & BUFRDERR)) 171dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang || !(mast_stat & ANAK)) 172d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 1; 173d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 174d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu complete(&iface->complete); 175d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return; 176d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 177d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (twi_int_status & MCOMP) { 178d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { 179d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum == 0) { 180d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* set the read number to 1 and ask for manual 181d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * stop in block combine mode 182d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 183d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 1; 184d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 185aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 186aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu read_MASTER_CTL(iface) | (0xff << 6)); 187d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 188d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* set the readd number in other 189d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * combine mode. 190d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 191aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 192aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu (read_MASTER_CTL(iface) & 193d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu (~(0xff << 6))) | 194aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu (iface->readNum << 6)); 195d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 196d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* remove restart bit and enable master receive */ 197aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 198aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu read_MASTER_CTL(iface) & ~RSTART); 1994dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && 2004dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->cur_msg+1 < iface->msg_num) { 2014dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->cur_msg++; 2024dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->transPtr = iface->pmsg[iface->cur_msg].buf; 2034dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->writeNum = iface->readNum = 2044dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->pmsg[iface->cur_msg].len; 2054dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* Set Transmit device address */ 206aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_ADDR(iface, 2074dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->pmsg[iface->cur_msg].addr); 2084dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD) 2094dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->read_write = I2C_SMBUS_READ; 2104dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else { 2114dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->read_write = I2C_SMBUS_WRITE; 2124dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* Transmit first data */ 2134dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (iface->writeNum > 0) { 214aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, 2154dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang *(iface->transPtr++)); 2164dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->writeNum--; 2174dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } 2184dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } 2194dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang 2204dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (iface->pmsg[iface->cur_msg].len <= 255) 22157a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang write_MASTER_CTL(iface, 22257a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang (read_MASTER_CTL(iface) & 22357a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang (~(0xff << 6))) | 22457a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang (iface->pmsg[iface->cur_msg].len << 6)); 2254dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else { 22657a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang write_MASTER_CTL(iface, 22757a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang (read_MASTER_CTL(iface) | 22857a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang (0xff << 6))); 2294dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->manual_stop = 1; 2304dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } 2314dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* remove restart bit and enable master receive */ 232aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 233aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu read_MASTER_CTL(iface) & ~RSTART); 234d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 235d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 1; 236aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_MASK(iface, 0); 237aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0); 238d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 239d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 240dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang complete(&iface->complete); 241d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 242d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 243d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* Interrupt handler */ 244d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id) 245d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 246d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = dev_id; 247d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned long flags; 2485481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang unsigned short twi_int_status; 249d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 250d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_lock_irqsave(&iface->lock, flags); 2515481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang while (1) { 2525481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang twi_int_status = read_INT_STAT(iface); 2535481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang if (!twi_int_status) 2545481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang break; 2555481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang /* Clear interrupt status */ 2565481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang write_INT_STAT(iface, twi_int_status); 2575481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang bfin_twi_handle_interrupt(iface, twi_int_status); 2585481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang SSYNC(); 2595481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang } 260d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_unlock_irqrestore(&iface->lock, flags); 261d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return IRQ_HANDLED; 262d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 263d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 264d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 265dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * One i2c master transfer 266d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 267dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangstatic int bfin_twi_do_master_xfer(struct i2c_adapter *adap, 268d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_msg *msgs, int num) 269d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 270d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = adap->algo_data; 271d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_msg *pmsg; 272d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int rc = 0; 273d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 274aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (!(read_CONTROL(iface) & TWI_ENA)) 275d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -ENXIO; 276d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 277aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu while (read_MASTER_STAT(iface) & BUSBUSY) 278d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu yield(); 279d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 2804dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->pmsg = msgs; 2814dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->msg_num = num; 2824dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->cur_msg = 0; 283d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 2844dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang pmsg = &msgs[0]; 2854dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (pmsg->flags & I2C_M_TEN) { 2864dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang dev_err(&adap->dev, "10 bits addr not supported!\n"); 2874dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang return -EINVAL; 2884dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } 289d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 2904dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->cur_mode = TWI_I2C_MODE_REPEAT; 2914dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->manual_stop = 0; 2924dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->transPtr = pmsg->buf; 2934dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->writeNum = iface->readNum = pmsg->len; 2944dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->result = 0; 295afc13b765ea71d316ce4974d3dc5a96cc73a0e95Hans Schillstrom init_completion(&(iface->complete)); 2964dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* Set Transmit device address */ 297aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_ADDR(iface, pmsg->addr); 2984dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang 2994dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* FIFO Initiation. Data in FIFO should be 3004dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang * discarded before start a new operation. 3014dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang */ 302aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_FIFO_CTL(iface, 0x3); 3034dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang SSYNC(); 304aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_FIFO_CTL(iface, 0); 3054dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang SSYNC(); 3064dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang 3074dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (pmsg->flags & I2C_M_RD) 3084dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->read_write = I2C_SMBUS_READ; 3094dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else { 3104dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->read_write = I2C_SMBUS_WRITE; 3114dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* Transmit first data */ 3124dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (iface->writeNum > 0) { 313aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, *(iface->transPtr++)); 3144dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->writeNum--; 3154dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang SSYNC(); 316d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 3174dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } 318d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 3194dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* clear int stat */ 320aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV); 321d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 3224dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* Interrupt mask . Enable XMT, RCV interrupt */ 323aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV); 3244dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang SSYNC(); 325d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 3264dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang if (pmsg->len <= 255) 327aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, pmsg->len << 6); 3284dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else { 329aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0xff << 6); 3304dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang iface->manual_stop = 1; 3314dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang } 332d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 3334dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang /* Master enable */ 334aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | 3354dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | 3364dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); 3374dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang SSYNC(); 3384dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang 339dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang while (!iface->result) { 340dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang if (!wait_for_completion_timeout(&iface->complete, 341dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang adap->timeout)) { 342dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang iface->result = -1; 343dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang dev_err(&adap->dev, "master transfer timeout\n"); 344dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang } 345dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang } 346d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 347dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang if (iface->result == 1) 348dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang rc = iface->cur_msg + 1; 3494dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang else 350dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang rc = iface->result; 351dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang 352dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang return rc; 353d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 354d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 355d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 356dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * Generic i2c master transfer entrypoint 357d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 358dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangstatic int bfin_twi_master_xfer(struct i2c_adapter *adap, 359dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang struct i2c_msg *msgs, int num) 360dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang{ 361be2f80f0a3a333c0c00b2c8a7c2d74fcd66b75a2Sonic Zhang return bfin_twi_do_master_xfer(adap, msgs, num); 362dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang} 363dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang 364dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang/* 365dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * One I2C SMBus transfer 366dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang */ 367dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangint bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, 368d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu unsigned short flags, char read_write, 369d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu u8 command, int size, union i2c_smbus_data *data) 370d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 371d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = adap->algo_data; 372d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int rc = 0; 373d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 374aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (!(read_CONTROL(iface) & TWI_ENA)) 375d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -ENXIO; 376d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 377aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu while (read_MASTER_STAT(iface) & BUSBUSY) 378d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu yield(); 379d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 380d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 0; 381d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 0; 382d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 383d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Prepare datas & select mode */ 384d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu switch (size) { 385d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_QUICK: 386d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = NULL; 387d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARD; 388d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 389d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_BYTE: 390d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (data == NULL) 391d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = NULL; 392d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 393d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) 394d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 1; 395d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else 396d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 1; 397d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = &data->byte; 398d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 399d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARD; 400d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 401d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_BYTE_DATA: 402d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) { 403d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 1; 404d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 405d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 406d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 1; 407d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 408d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 409d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = &data->byte; 410d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 411d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_WORD_DATA: 412d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) { 413d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 2; 414d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 415d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 416d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 2; 417d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 418d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 419d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = (u8 *)&data->word; 420d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 421d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_PROC_CALL: 422d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = 2; 423d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 2; 424d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 425d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = (u8 *)&data->word; 426d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 427d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case I2C_SMBUS_BLOCK_DATA: 428d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (read_write == I2C_SMBUS_READ) { 429d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->readNum = 0; 430d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_COMBINED; 431d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 432d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum = data->block[0] + 1; 433d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 434d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 435d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->transPtr = data->block; 436d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 437e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich case I2C_SMBUS_I2C_BLOCK_DATA: 438e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich if (read_write == I2C_SMBUS_READ) { 439e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich iface->readNum = data->block[0]; 440e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich iface->cur_mode = TWI_I2C_MODE_COMBINED; 441e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich } else { 442e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich iface->writeNum = data->block[0]; 443e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; 444e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich } 445e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich iface->transPtr = (u8 *)&data->block[1]; 446e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich break; 447d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu default: 448d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return -1; 449d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 450d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 451d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->result = 0; 452d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 0; 453d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->read_write = read_write; 454d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->command = command; 455afc13b765ea71d316ce4974d3dc5a96cc73a0e95Hans Schillstrom init_completion(&(iface->complete)); 456d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 457d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* FIFO Initiation. Data in FIFO should be discarded before 458d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * start a new operation. 459d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 460aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_FIFO_CTL(iface, 0x3); 461d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 462aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_FIFO_CTL(iface, 0); 463d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 464d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* clear int stat */ 465aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV); 466d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 467d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set Transmit device address */ 468aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_ADDR(iface, addr); 469d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 470d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 471d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu switch (iface->cur_mode) { 472d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case TWI_I2C_MODE_STANDARDSUB: 473aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, iface->command); 474aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_MASK(iface, MCOMP | MERR | 475d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? 476d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu RCVSERV : XMTSERV)); 477d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 478d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 479d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum + 1 <= 255) 480aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, (iface->writeNum + 1) << 6); 481d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 482aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0xff << 6); 483d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 484d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 485d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 486aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | 487d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); 488d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 489d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu case TWI_I2C_MODE_COMBINED: 490aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, iface->command); 491aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV); 492d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 493d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 494d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) 495aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, (iface->writeNum + 1) << 6); 496d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else 497aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0x1 << 6); 498d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 499aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | 500d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); 501d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 502d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu default: 503aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0); 504d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (size != I2C_SMBUS_QUICK) { 505d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Don't access xmit data register when this is a 506d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * read operation. 507d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 508d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->read_write != I2C_SMBUS_READ) { 509d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum > 0) { 510aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, 511aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu *(iface->transPtr++)); 512d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->writeNum <= 255) 513aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 514aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu iface->writeNum << 6); 515d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else { 516aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 517aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 0xff << 6); 518d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 519d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 520d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->writeNum--; 521d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 522aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_XMT_DATA8(iface, iface->command); 523aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 1 << 6); 524d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 525d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } else { 526d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (iface->readNum > 0 && iface->readNum <= 255) 527aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 528aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu iface->readNum << 6); 529d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu else if (iface->readNum > 255) { 530aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, 0xff << 6); 531d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu iface->manual_stop = 1; 532dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang } else 533d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 534d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 535d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 536aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_INT_MASK(iface, MCOMP | MERR | 537d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? 538d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu RCVSERV : XMTSERV)); 539d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 540d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 541d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Master enable */ 542aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | 543d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | 544d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); 545d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu break; 546d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 547d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 548d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 549dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang while (!iface->result) { 550dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang if (!wait_for_completion_timeout(&iface->complete, 551dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang adap->timeout)) { 552dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang iface->result = -1; 553dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang dev_err(&adap->dev, "smbus transfer timeout\n"); 554dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang } 555dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang } 556d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 557d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu rc = (iface->result >= 0) ? 0 : -1; 558d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 559d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return rc; 560d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 561d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 562d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* 563dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * Generic I2C SMBus transfer entrypoint 564dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang */ 565dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangint bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, 566dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang unsigned short flags, char read_write, 567dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang u8 command, int size, union i2c_smbus_data *data) 568dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang{ 569be2f80f0a3a333c0c00b2c8a7c2d74fcd66b75a2Sonic Zhang return bfin_twi_do_smbus_xfer(adap, addr, flags, 570dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang read_write, command, size, data); 571dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang} 572dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang 573dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang/* 574d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Return what the adapter supports 575d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */ 576d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic u32 bfin_twi_functionality(struct i2c_adapter *adap) 577d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 578d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 579d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 580d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | 581e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK; 582d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 583d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 584d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct i2c_algorithm bfin_twi_algorithm = { 585d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .master_xfer = bfin_twi_master_xfer, 586d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .smbus_xfer = bfin_twi_smbus_xfer, 587d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .functionality = bfin_twi_functionality, 588d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}; 589d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 590958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerichstatic int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state) 591d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 592958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich struct bfin_twi_iface *iface = platform_get_drvdata(pdev); 593958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich 594958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich iface->saved_clkdiv = read_CLKDIV(iface); 595958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich iface->saved_control = read_CONTROL(iface); 596958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich 597958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich free_irq(iface->irq, iface); 598d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 599d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Disable TWI */ 600958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich write_CONTROL(iface, iface->saved_control & ~TWI_ENA); 601d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 602d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return 0; 603d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 604d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 605958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerichstatic int i2c_bfin_twi_resume(struct platform_device *pdev) 606d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 607958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich struct bfin_twi_iface *iface = platform_get_drvdata(pdev); 608d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 609958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich int rc = request_irq(iface->irq, bfin_twi_interrupt_entry, 610958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich IRQF_DISABLED, pdev->name, iface); 611958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich if (rc) { 612958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); 613958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich return -ENODEV; 614958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich } 615958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich 616958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich /* Resume TWI interface clock as specified */ 617958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich write_CLKDIV(iface, iface->saved_clkdiv); 618958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich 619958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich /* Resume TWI */ 620958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich write_CONTROL(iface, iface->saved_control); 621d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 622d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return 0; 623d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 624d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 625aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wustatic int i2c_bfin_twi_probe(struct platform_device *pdev) 626d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 627aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu struct bfin_twi_iface *iface; 628d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct i2c_adapter *p_adap; 629aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu struct resource *res; 630d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu int rc; 6319528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich unsigned int clkhilow; 632d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 633aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL); 634aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (!iface) { 635aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu dev_err(&pdev->dev, "Cannot allocate memory\n"); 636aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu rc = -ENOMEM; 637aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu goto out_error_nomem; 638aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu } 639aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 640d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu spin_lock_init(&(iface->lock)); 641aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 642aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu /* Find and map our resources */ 643aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 644aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (res == NULL) { 645aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); 646aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu rc = -ENOENT; 647aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu goto out_error_get_res; 648aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu } 649aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 650c6ffddea36dd576b70dfbd10eb5d2b287b786dcaLinus Walleij iface->regs_base = ioremap(res->start, resource_size(res)); 651aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (iface->regs_base == NULL) { 652aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu dev_err(&pdev->dev, "Cannot map IO\n"); 653aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu rc = -ENXIO; 654aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu goto out_error_ioremap; 655aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu } 656aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 657aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu iface->irq = platform_get_irq(pdev, 0); 658aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (iface->irq < 0) { 659aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu dev_err(&pdev->dev, "No IRQ specified\n"); 660aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu rc = -ENOENT; 661aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu goto out_error_no_irq; 662aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu } 663d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 664d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap = &iface->adap; 665aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu p_adap->nr = pdev->id; 666aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name)); 667d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->algo = &bfin_twi_algorithm; 668d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu p_adap->algo_data = iface; 669e1995f65be0786ca201f466f049dad1e2e4c3421Jean Delvare p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 670aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu p_adap->dev.parent = &pdev->dev; 671dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang p_adap->timeout = 5 * HZ; 672dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang p_adap->retries = 3; 673d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 67474d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi"); 67574d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu if (rc) { 67674d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu dev_err(&pdev->dev, "Can't setup pin mux!\n"); 67774d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu goto out_error_pin_mux; 67874d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu } 67974d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu 680d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu rc = request_irq(iface->irq, bfin_twi_interrupt_entry, 681aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu IRQF_DISABLED, pdev->name, iface); 682d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu if (rc) { 683aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); 684aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu rc = -ENODEV; 685aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu goto out_error_req_irq; 686d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu } 687d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 688d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set TWI internal clock as 10MHz */ 689ac07fb4dc1908d300f50fa711982c9d750eb59f7Sonic Zhang write_CONTROL(iface, ((get_sclk() / 1000 / 1000 + 5) / 10) & 0x7F); 690d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 6919528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich /* 6929528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich * We will not end up with a CLKDIV=0 because no one will specify 693ac07fb4dc1908d300f50fa711982c9d750eb59f7Sonic Zhang * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250) 6949528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich */ 695ac07fb4dc1908d300f50fa711982c9d750eb59f7Sonic Zhang clkhilow = ((10 * 1000 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ) + 1) / 2; 6969528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich 697d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Set Twi interface clock as specified */ 6989528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich write_CLKDIV(iface, (clkhilow << 8) | clkhilow); 699d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 700d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu /* Enable TWI */ 701aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); 702d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu SSYNC(); 703d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 704991dee591a99d035796a8c194eb1796cc020e142Kalle Pokki rc = i2c_add_numbered_adapter(p_adap); 705aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu if (rc < 0) { 706aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu dev_err(&pdev->dev, "Can't add i2c adapter!\n"); 707aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu goto out_error_add_adapter; 708aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu } 709aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 710aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu platform_set_drvdata(pdev, iface); 711d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 712fa6ad222713a65980528348e7f75abc768b78297Bryan Wu dev_info(&pdev->dev, "Blackfin BF5xx on-chip I2C TWI Contoller, " 713fa6ad222713a65980528348e7f75abc768b78297Bryan Wu "regs_base@%p\n", iface->regs_base); 714aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 715aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu return 0; 716aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu 717aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_add_adapter: 718aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu free_irq(iface->irq, iface); 719aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_req_irq: 720aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_no_irq: 72174d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu peripheral_free_list(pin_req[pdev->id]); 72274d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wuout_error_pin_mux: 723aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu iounmap(iface->regs_base); 724aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_ioremap: 725aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_get_res: 726aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu kfree(iface); 727aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_nomem: 728d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return rc; 729d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 730d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 731d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int i2c_bfin_twi_remove(struct platform_device *pdev) 732d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 733d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu struct bfin_twi_iface *iface = platform_get_drvdata(pdev); 734d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 735d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu platform_set_drvdata(pdev, NULL); 736d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 737d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu i2c_del_adapter(&(iface->adap)); 738d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu free_irq(iface->irq, iface); 73974d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu peripheral_free_list(pin_req[pdev->id]); 740aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu iounmap(iface->regs_base); 741aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu kfree(iface); 742d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 743d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return 0; 744d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 745d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 746d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct platform_driver i2c_bfin_twi_driver = { 747d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .probe = i2c_bfin_twi_probe, 748d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .remove = i2c_bfin_twi_remove, 749d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .suspend = i2c_bfin_twi_suspend, 750d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .resume = i2c_bfin_twi_resume, 751d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .driver = { 752d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .name = "i2c-bfin-twi", 753d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu .owner = THIS_MODULE, 754d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu }, 755d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}; 756d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 757d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int __init i2c_bfin_twi_init(void) 758d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 759d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu return platform_driver_register(&i2c_bfin_twi_driver); 760d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 761d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 762d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic void __exit i2c_bfin_twi_exit(void) 763d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{ 764d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu platform_driver_unregister(&i2c_bfin_twi_driver); 765d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu} 766d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu 767d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wumodule_init(i2c_bfin_twi_init); 768d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wumodule_exit(i2c_bfin_twi_exit); 769fa6ad222713a65980528348e7f75abc768b78297Bryan Wu 770fa6ad222713a65980528348e7f75abc768b78297Bryan WuMODULE_AUTHOR("Bryan Wu, Sonic Zhang"); 771fa6ad222713a65980528348e7f75abc768b78297Bryan WuMODULE_DESCRIPTION("Blackfin BF5xx on-chip I2C TWI Contoller Driver"); 772fa6ad222713a65980528348e7f75abc768b78297Bryan WuMODULE_LICENSE("GPL"); 773add8eda7f2be781af0224241e870715cf0cfd75aKay SieversMODULE_ALIAS("platform:i2c-bfin-twi"); 774