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>
23540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich#include <linux/delay.h>
24d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
25d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <asm/blackfin.h>
2674d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu#include <asm/portmux.h>
27d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu#include <asm/irq.h>
28d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
29d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* SMBus mode*/
304dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_STANDARD		1
314dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_STANDARDSUB	2
324dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_COMBINED		3
334dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang#define TWI_I2C_MODE_REPEAT		4
34d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
35d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustruct bfin_twi_iface {
36d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int			irq;
37d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	spinlock_t		lock;
38d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	char			read_write;
39d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	u8			command;
40d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	u8			*transPtr;
41d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int			readNum;
42d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int			writeNum;
43d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int			cur_mode;
44d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int			manual_stop;
45d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int			result;
46d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct i2c_adapter	adap;
47d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct completion	complete;
484dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	struct i2c_msg 		*pmsg;
494dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	int			msg_num;
504dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	int			cur_msg;
51958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	u16			saved_clkdiv;
52958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	u16			saved_control;
53aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	void __iomem		*regs_base;
54d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu};
55d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
56aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
57aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu#define DEFINE_TWI_REG(reg, off) \
58aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wustatic inline u16 read_##reg(struct bfin_twi_iface *iface) \
59aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	{ return bfin_read16(iface->regs_base + (off)); } \
60aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wustatic inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \
61aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	{ bfin_write16(iface->regs_base + (off), v); }
62aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
63aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(CLKDIV, 0x00)
64aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(CONTROL, 0x04)
65aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(SLAVE_CTL, 0x08)
66aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(SLAVE_STAT, 0x0C)
67aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(SLAVE_ADDR, 0x10)
68aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(MASTER_CTL, 0x14)
69aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(MASTER_STAT, 0x18)
70aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(MASTER_ADDR, 0x1C)
71aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(INT_STAT, 0x20)
72aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(INT_MASK, 0x24)
73aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(FIFO_CTL, 0x28)
74aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(FIFO_STAT, 0x2C)
75aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(XMT_DATA8, 0x80)
76aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(XMT_DATA16, 0x84)
77aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(RCV_DATA8, 0x88)
78aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan WuDEFINE_TWI_REG(RCV_DATA16, 0x8C)
79d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
8074d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wustatic const u16 pin_req[2][3] = {
8174d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	{P_TWI0_SCL, P_TWI0_SDA, 0},
8274d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	{P_TWI1_SCL, P_TWI1_SDA, 0},
8374d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu};
8474d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu
855481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhangstatic void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
865481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang					unsigned short twi_int_status)
87d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
88aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	unsigned short mast_stat = read_MASTER_STAT(iface);
89d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
90d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	if (twi_int_status & XMTSERV) {
91d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		/* Transmit next data */
92d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (iface->writeNum > 0) {
935481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang			SSYNC();
94aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_XMT_DATA8(iface, *(iface->transPtr++));
95d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->writeNum--;
96d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
97d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		/* start receive immediately after complete sending in
98d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		 * combine mode.
99d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		 */
1004dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
101aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface,
102aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				read_MASTER_CTL(iface) | MDIR | RSTART);
1034dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		else if (iface->manual_stop)
104aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface,
105aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				read_MASTER_CTL(iface) | STOP);
1064dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
10794327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew		         iface->cur_msg + 1 < iface->msg_num) {
10894327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew			if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
10994327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew				write_MASTER_CTL(iface,
11094327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew					read_MASTER_CTL(iface) | RSTART | MDIR);
11194327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew			else
11294327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew				write_MASTER_CTL(iface,
11394327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew					(read_MASTER_CTL(iface) | RSTART) & ~MDIR);
11494327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew		}
115d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
116d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	if (twi_int_status & RCVSERV) {
117d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (iface->readNum > 0) {
118d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			/* Receive next data */
119aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			*(iface->transPtr) = read_RCV_DATA8(iface);
120d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
121d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				/* Change combine mode into sub mode after
122d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 * read first data.
123d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 */
124d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
125d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				/* Get read number from first byte in block
126d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 * combine mode.
127d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 */
128d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				if (iface->readNum == 1 && iface->manual_stop)
129d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					iface->readNum = *iface->transPtr + 1;
130d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			}
131d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->transPtr++;
132d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->readNum--;
133d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		} else if (iface->manual_stop) {
134aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface,
135aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				read_MASTER_CTL(iface) | STOP);
1364dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
13794327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew		           iface->cur_msg + 1 < iface->msg_num) {
13894327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew			if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
13994327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew				write_MASTER_CTL(iface,
14094327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew					read_MASTER_CTL(iface) | RSTART | MDIR);
14194327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew			else
14294327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew				write_MASTER_CTL(iface,
14394327d009e3aa20214e9dfa486a1fd14445fe736Frank Shew					(read_MASTER_CTL(iface) | RSTART) & ~MDIR);
144d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
145d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
146d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	if (twi_int_status & MERR) {
147aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_INT_MASK(iface, 0);
148aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_STAT(iface, 0x3e);
149aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, 0);
1504dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		iface->result = -EIO;
1515cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich
1525cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich		if (mast_stat & LOSTARB)
1535cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich			dev_dbg(&iface->adap.dev, "Lost Arbitration\n");
1545cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich		if (mast_stat & ANAK)
1555cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich			dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n");
1565cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich		if (mast_stat & DNAK)
1575cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich			dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n");
1585cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich		if (mast_stat & BUFRDERR)
1595cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich			dev_dbg(&iface->adap.dev, "Buffer Read Error\n");
1605cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich		if (mast_stat & BUFWRERR)
1615cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich			dev_dbg(&iface->adap.dev, "Buffer Write Error\n");
1625cfafc18f38aa6701f581bee875fb0b19f3b3b4bMichael Hennerich
163540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich		/* Faulty slave devices, may drive SDA low after a transfer
164540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich		 * finishes. To release the bus this code generates up to 9
165540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich		 * extra clocks until SDA is released.
166540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich		 */
167540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich
168540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich		if (read_MASTER_STAT(iface) & SDASEN) {
169540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			int cnt = 9;
170540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			do {
171540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich				write_MASTER_CTL(iface, SCLOVR);
172540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich				udelay(6);
173540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich				write_MASTER_CTL(iface, 0);
174540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich				udelay(6);
175540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			} while ((read_MASTER_STAT(iface) & SDASEN) && cnt--);
176540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich
177540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			write_MASTER_CTL(iface, SDAOVR | SCLOVR);
178540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			udelay(6);
179540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			write_MASTER_CTL(iface, SDAOVR);
180540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			udelay(6);
181540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich			write_MASTER_CTL(iface, 0);
182540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich		}
183540ac5553e8169413a90a8e68b3a10f801640eb7Michael Hennerich
184f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang		/* If it is a quick transfer, only address without data,
185f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang		 * not an err, return 1.
186d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		 */
187f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang		if (iface->cur_mode == TWI_I2C_MODE_STANDARD &&
188f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang			iface->transPtr == NULL &&
189f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang			(twi_int_status & MCOMP) && (mast_stat & DNAK))
190f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang			iface->result = 1;
191f0ac131a21ed13e8baaa9df6f0420f2c4b45e807Sonic Zhang
192d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		complete(&iface->complete);
193d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		return;
194d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
195d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	if (twi_int_status & MCOMP) {
1964a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang		if ((read_MASTER_CTL(iface) & MEN) == 0 &&
1974a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang			(iface->cur_mode == TWI_I2C_MODE_REPEAT ||
1984a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang			iface->cur_mode == TWI_I2C_MODE_COMBINED)) {
1994a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang			iface->result = -1;
2004a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang			write_INT_MASK(iface, 0);
2014a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang			write_MASTER_CTL(iface, 0);
2024a65163e3b2190445c1d94daa21d09c5af604d97Sonic Zhang		} else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
203d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			if (iface->readNum == 0) {
204d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				/* set the read number to 1 and ask for manual
205d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 * stop in block combine mode
206d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 */
207d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				iface->readNum = 1;
208d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				iface->manual_stop = 1;
209aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				write_MASTER_CTL(iface,
210aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					read_MASTER_CTL(iface) | (0xff << 6));
211d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			} else {
212d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				/* set the readd number in other
213d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 * combine mode.
214d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				 */
215aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				write_MASTER_CTL(iface,
216aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					(read_MASTER_CTL(iface) &
217d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					(~(0xff << 6))) |
218aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					(iface->readNum << 6));
219d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			}
220d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			/* remove restart bit and enable master receive */
221aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface,
222aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				read_MASTER_CTL(iface) & ~RSTART);
2234dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
2244dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				iface->cur_msg+1 < iface->msg_num) {
2254dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			iface->cur_msg++;
2264dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			iface->transPtr = iface->pmsg[iface->cur_msg].buf;
2274dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			iface->writeNum = iface->readNum =
2284dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				iface->pmsg[iface->cur_msg].len;
2294dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			/* Set Transmit device address */
230aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_ADDR(iface,
2314dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				iface->pmsg[iface->cur_msg].addr);
2324dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)
2334dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				iface->read_write = I2C_SMBUS_READ;
2344dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			else {
2354dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				iface->read_write = I2C_SMBUS_WRITE;
2364dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				/* Transmit first data */
2374dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				if (iface->writeNum > 0) {
238aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					write_XMT_DATA8(iface,
2394dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang						*(iface->transPtr++));
2404dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang					iface->writeNum--;
2414dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				}
2424dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			}
2434dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang
2444dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			if (iface->pmsg[iface->cur_msg].len <= 255)
24557a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang					write_MASTER_CTL(iface,
24657a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang					(read_MASTER_CTL(iface) &
24757a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang					(~(0xff << 6))) |
24857a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang				(iface->pmsg[iface->cur_msg].len << 6));
2494dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			else {
25057a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang				write_MASTER_CTL(iface,
25157a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang					(read_MASTER_CTL(iface) |
25257a8f32eafa6f36ea3a128e8b13f353c5a3ca9b2Sonic Zhang					(0xff << 6)));
2534dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang				iface->manual_stop = 1;
2544dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			}
2554dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			/* remove restart bit and enable master receive */
256aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface,
257aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu				read_MASTER_CTL(iface) & ~RSTART);
258d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		} else {
259d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->result = 1;
260aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_INT_MASK(iface, 0);
261aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface, 0);
262d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
263d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
264dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	complete(&iface->complete);
265d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
266d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
267d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/* Interrupt handler */
268d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
269d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
270d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct bfin_twi_iface *iface = dev_id;
271d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	unsigned long flags;
2725481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang	unsigned short twi_int_status;
273d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
274d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	spin_lock_irqsave(&iface->lock, flags);
2755481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang	while (1) {
2765481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang		twi_int_status = read_INT_STAT(iface);
2775481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang		if (!twi_int_status)
2785481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang			break;
2795481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang		/* Clear interrupt status */
2805481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang		write_INT_STAT(iface, twi_int_status);
2815481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang		bfin_twi_handle_interrupt(iface, twi_int_status);
2825481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang		SSYNC();
2835481d0753e7a78bff7550a8165b7924baa74e9cfSonic Zhang	}
284d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	spin_unlock_irqrestore(&iface->lock, flags);
285d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return IRQ_HANDLED;
286d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
287d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
288d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/*
289dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * One i2c master transfer
290d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */
291dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangstatic int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
292d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				struct i2c_msg *msgs, int num)
293d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
294d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct bfin_twi_iface *iface = adap->algo_data;
295d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct i2c_msg *pmsg;
296d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int rc = 0;
297d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
298aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (!(read_CONTROL(iface) & TWI_ENA))
299d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		return -ENXIO;
300d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
301aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	while (read_MASTER_STAT(iface) & BUSBUSY)
302d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		yield();
303d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3044dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->pmsg = msgs;
3054dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->msg_num = num;
3064dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->cur_msg = 0;
307d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3084dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	pmsg = &msgs[0];
3094dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	if (pmsg->flags & I2C_M_TEN) {
3104dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		dev_err(&adap->dev, "10 bits addr not supported!\n");
3114dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		return -EINVAL;
3124dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	}
313d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3144dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->cur_mode = TWI_I2C_MODE_REPEAT;
3154dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->manual_stop = 0;
3164dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->transPtr = pmsg->buf;
3174dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->writeNum = iface->readNum = pmsg->len;
3184dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	iface->result = 0;
319afc13b765ea71d316ce4974d3dc5a96cc73a0e95Hans Schillstrom	init_completion(&(iface->complete));
3204dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	/* Set Transmit device address */
321aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_MASTER_ADDR(iface, pmsg->addr);
3224dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang
3234dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	/* FIFO Initiation. Data in FIFO should be
3244dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	 *  discarded before start a new operation.
3254dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	 */
326aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_FIFO_CTL(iface, 0x3);
3274dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	SSYNC();
328aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_FIFO_CTL(iface, 0);
3294dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	SSYNC();
3304dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang
3314dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	if (pmsg->flags & I2C_M_RD)
3324dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		iface->read_write = I2C_SMBUS_READ;
3334dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	else {
3344dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		iface->read_write = I2C_SMBUS_WRITE;
3354dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		/* Transmit first data */
3364dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		if (iface->writeNum > 0) {
337aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_XMT_DATA8(iface, *(iface->transPtr++));
3384dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			iface->writeNum--;
3394dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang			SSYNC();
340d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
3414dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	}
342d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3434dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	/* clear int stat */
344aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
345d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3464dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	/* Interrupt mask . Enable XMT, RCV interrupt */
347aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
3484dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	SSYNC();
349d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3504dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	if (pmsg->len <= 255)
351aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, pmsg->len << 6);
3524dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	else {
353aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, 0xff << 6);
3544dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		iface->manual_stop = 1;
3554dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	}
356d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
3574dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	/* Master enable */
358aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
3594dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
3604dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang		((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
3614dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	SSYNC();
3624dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang
363dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	while (!iface->result) {
364dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang		if (!wait_for_completion_timeout(&iface->complete,
365dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			adap->timeout)) {
366dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			iface->result = -1;
367dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			dev_err(&adap->dev, "master transfer timeout\n");
368dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang		}
369dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	}
370d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
371dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	if (iface->result == 1)
372dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang		rc = iface->cur_msg + 1;
3734dd39bb12f5b9f0d9a98f29071dc1c51e9306954Sonic Zhang	else
374dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang		rc = iface->result;
375dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang
376dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	return rc;
377d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
378d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
379d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/*
380dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * Generic i2c master transfer entrypoint
381d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */
382dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangstatic int bfin_twi_master_xfer(struct i2c_adapter *adap,
383dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang				struct i2c_msg *msgs, int num)
384dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang{
385be2f80f0a3a333c0c00b2c8a7c2d74fcd66b75a2Sonic Zhang	return bfin_twi_do_master_xfer(adap, msgs, num);
386dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang}
387dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang
388dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang/*
389dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * One I2C SMBus transfer
390dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang */
391dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangint bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
392d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			unsigned short flags, char read_write,
393d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			u8 command, int size, union i2c_smbus_data *data)
394d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
395d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct bfin_twi_iface *iface = adap->algo_data;
396d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int rc = 0;
397d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
398aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (!(read_CONTROL(iface) & TWI_ENA))
399d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		return -ENXIO;
400d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
401aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	while (read_MASTER_STAT(iface) & BUSBUSY)
402d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		yield();
403d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
404d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	iface->writeNum = 0;
405d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	iface->readNum = 0;
406d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
407d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* Prepare datas & select mode */
408d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	switch (size) {
409d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case I2C_SMBUS_QUICK:
410d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->transPtr = NULL;
411d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->cur_mode = TWI_I2C_MODE_STANDARD;
412d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
413d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case I2C_SMBUS_BYTE:
414d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (data == NULL)
415d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->transPtr = NULL;
416d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		else {
417d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			if (read_write == I2C_SMBUS_READ)
418d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				iface->readNum = 1;
419d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			else
420d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				iface->writeNum = 1;
421d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->transPtr = &data->byte;
422d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
423d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->cur_mode = TWI_I2C_MODE_STANDARD;
424d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
425d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case I2C_SMBUS_BYTE_DATA:
426d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (read_write == I2C_SMBUS_READ) {
427d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->readNum = 1;
428d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->cur_mode = TWI_I2C_MODE_COMBINED;
429d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		} else {
430d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->writeNum = 1;
431d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
432d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
433d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->transPtr = &data->byte;
434d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
435d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case I2C_SMBUS_WORD_DATA:
436d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (read_write == I2C_SMBUS_READ) {
437d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->readNum = 2;
438d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->cur_mode = TWI_I2C_MODE_COMBINED;
439d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		} else {
440d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->writeNum = 2;
441d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
442d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
443d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->transPtr = (u8 *)&data->word;
444d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
445d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case I2C_SMBUS_PROC_CALL:
446d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->writeNum = 2;
447d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->readNum = 2;
448d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->cur_mode = TWI_I2C_MODE_COMBINED;
449d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->transPtr = (u8 *)&data->word;
450d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
451d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case I2C_SMBUS_BLOCK_DATA:
452d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (read_write == I2C_SMBUS_READ) {
453d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->readNum = 0;
454d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->cur_mode = TWI_I2C_MODE_COMBINED;
455d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		} else {
456d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->writeNum = data->block[0] + 1;
457d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
458d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
459d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		iface->transPtr = data->block;
460d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
461e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich	case I2C_SMBUS_I2C_BLOCK_DATA:
462e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich		if (read_write == I2C_SMBUS_READ) {
463e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich			iface->readNum = data->block[0];
464e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich			iface->cur_mode = TWI_I2C_MODE_COMBINED;
465e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich		} else {
466e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich			iface->writeNum = data->block[0];
467e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich			iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
468e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich		}
469e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich		iface->transPtr = (u8 *)&data->block[1];
470e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich		break;
471d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	default:
472d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		return -1;
473d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
474d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
475d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	iface->result = 0;
476d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	iface->manual_stop = 0;
477d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	iface->read_write = read_write;
478d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	iface->command = command;
479afc13b765ea71d316ce4974d3dc5a96cc73a0e95Hans Schillstrom	init_completion(&(iface->complete));
480d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
481d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* FIFO Initiation. Data in FIFO should be discarded before
482d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	 * start a new operation.
483d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	 */
484aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_FIFO_CTL(iface, 0x3);
485d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	SSYNC();
486aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_FIFO_CTL(iface, 0);
487d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
488d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* clear int stat */
489aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
490d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
491d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* Set Transmit device address */
492aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_MASTER_ADDR(iface, addr);
493d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	SSYNC();
494d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
495d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	switch (iface->cur_mode) {
496d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case TWI_I2C_MODE_STANDARDSUB:
497aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_XMT_DATA8(iface, iface->command);
498aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_INT_MASK(iface, MCOMP | MERR |
499d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			((iface->read_write == I2C_SMBUS_READ) ?
500d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			RCVSERV : XMTSERV));
501d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		SSYNC();
502d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
503d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (iface->writeNum + 1 <= 255)
504aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
505d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		else {
506aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface, 0xff << 6);
507d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			iface->manual_stop = 1;
508d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
509d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		/* Master enable */
510aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
511d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
512d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
513d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	case TWI_I2C_MODE_COMBINED:
514aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_XMT_DATA8(iface, iface->command);
515aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
516d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		SSYNC();
517d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
518d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (iface->writeNum > 0)
519aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
520d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		else
521aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu			write_MASTER_CTL(iface, 0x1 << 6);
522d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		/* Master enable */
523aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
524d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
525d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
526d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	default:
527aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, 0);
528d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		if (size != I2C_SMBUS_QUICK) {
529d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			/* Don't access xmit data register when this is a
530d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			 * read operation.
531d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			 */
532d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			if (iface->read_write != I2C_SMBUS_READ) {
533d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				if (iface->writeNum > 0) {
534aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					write_XMT_DATA8(iface,
535aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu						*(iface->transPtr++));
536d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					if (iface->writeNum <= 255)
537aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu						write_MASTER_CTL(iface,
538aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu							iface->writeNum << 6);
539d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					else {
540aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu						write_MASTER_CTL(iface,
541aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu							0xff << 6);
542d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu						iface->manual_stop = 1;
543d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					}
544d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					iface->writeNum--;
545d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				} else {
546aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					write_XMT_DATA8(iface, iface->command);
547aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					write_MASTER_CTL(iface, 1 << 6);
548d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				}
549d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			} else {
550d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				if (iface->readNum > 0 && iface->readNum <= 255)
551aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					write_MASTER_CTL(iface,
552aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu						iface->readNum << 6);
553d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu				else if (iface->readNum > 255) {
554aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu					write_MASTER_CTL(iface, 0xff << 6);
555d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					iface->manual_stop = 1;
556dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang				} else
557d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu					break;
558d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			}
559d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		}
560aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_INT_MASK(iface, MCOMP | MERR |
561d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			((iface->read_write == I2C_SMBUS_READ) ?
562d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			RCVSERV : XMTSERV));
563d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		SSYNC();
564d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
565d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		/* Master enable */
566aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
567d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
568d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu			((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
569d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		break;
570d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
571d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	SSYNC();
572d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
573dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	while (!iface->result) {
574dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang		if (!wait_for_completion_timeout(&iface->complete,
575dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			adap->timeout)) {
576dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			iface->result = -1;
577dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			dev_err(&adap->dev, "smbus transfer timeout\n");
578dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang		}
579dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	}
580d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
581d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	rc = (iface->result >= 0) ? 0 : -1;
582d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
583d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return rc;
584d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
585d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
586d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu/*
587dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang * Generic I2C SMBus transfer entrypoint
588dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang */
589dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhangint bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
590dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			unsigned short flags, char read_write,
591dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			u8 command, int size, union i2c_smbus_data *data)
592dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang{
593be2f80f0a3a333c0c00b2c8a7c2d74fcd66b75a2Sonic Zhang	return bfin_twi_do_smbus_xfer(adap, addr, flags,
594dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang			read_write, command, size, data);
595dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang}
596dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang
597dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang/*
598d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu * Return what the adapter supports
599d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu */
600d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic u32 bfin_twi_functionality(struct i2c_adapter *adap)
601d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
602d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
603d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
604d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	       I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
605e0cd2dd5dd2b7c6512e46ce0b4f119cd7b0c74a4Michael Hennerich	       I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
606d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
607d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
608d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct i2c_algorithm bfin_twi_algorithm = {
609d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.master_xfer   = bfin_twi_master_xfer,
610d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.smbus_xfer    = bfin_twi_smbus_xfer,
611d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.functionality = bfin_twi_functionality,
612d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu};
613d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
614958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerichstatic int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state)
615d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
616958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
617958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich
618958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	iface->saved_clkdiv = read_CLKDIV(iface);
619958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	iface->saved_control = read_CONTROL(iface);
620958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich
621958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	free_irq(iface->irq, iface);
622d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
623d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* Disable TWI */
624958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	write_CONTROL(iface, iface->saved_control & ~TWI_ENA);
625d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
626d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return 0;
627d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
628d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
629958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerichstatic int i2c_bfin_twi_resume(struct platform_device *pdev)
630d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
631958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
632d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
633958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
6344311051c358ad0e66b68934e7a33cf10ba533466Yong Zhang		0, pdev->name, iface);
635958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	if (rc) {
636958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich		dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
637958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich		return -ENODEV;
638958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	}
639958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich
640958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	/* Resume TWI interface clock as specified */
641958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	write_CLKDIV(iface, iface->saved_clkdiv);
642958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich
643958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	/* Resume TWI */
644958585f58f675a3c2855c7d91b6fdd2875552d0bMichael Hennerich	write_CONTROL(iface, iface->saved_control);
645d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
646d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return 0;
647d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
648d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
649aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wustatic int i2c_bfin_twi_probe(struct platform_device *pdev)
650d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
651aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	struct bfin_twi_iface *iface;
652d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct i2c_adapter *p_adap;
653aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	struct resource *res;
654d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	int rc;
6559528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich	unsigned int clkhilow;
656d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
657aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL);
658aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (!iface) {
659aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		dev_err(&pdev->dev, "Cannot allocate memory\n");
660aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		rc = -ENOMEM;
661aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		goto out_error_nomem;
662aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	}
663aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
664d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	spin_lock_init(&(iface->lock));
665aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
666aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	/* Find and map our resources */
667aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
668aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (res == NULL) {
669aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
670aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		rc = -ENOENT;
671aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		goto out_error_get_res;
672aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	}
673aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
674c6ffddea36dd576b70dfbd10eb5d2b287b786dcaLinus Walleij	iface->regs_base = ioremap(res->start, resource_size(res));
675aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (iface->regs_base == NULL) {
676aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		dev_err(&pdev->dev, "Cannot map IO\n");
677aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		rc = -ENXIO;
678aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		goto out_error_ioremap;
679aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	}
680aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
681aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	iface->irq = platform_get_irq(pdev, 0);
682aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (iface->irq < 0) {
683aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		dev_err(&pdev->dev, "No IRQ specified\n");
684aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		rc = -ENOENT;
685aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		goto out_error_no_irq;
686aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	}
687d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
688d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	p_adap = &iface->adap;
689aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	p_adap->nr = pdev->id;
690aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
691d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	p_adap->algo = &bfin_twi_algorithm;
692d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	p_adap->algo_data = iface;
693e1995f65be0786ca201f466f049dad1e2e4c3421Jean Delvare	p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
694aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	p_adap->dev.parent = &pdev->dev;
695dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	p_adap->timeout = 5 * HZ;
696dd7319a5289deb4e17beb8851d343e7930b32c3bSonic Zhang	p_adap->retries = 3;
697d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
69874d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
69974d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	if (rc) {
70074d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu		dev_err(&pdev->dev, "Can't setup pin mux!\n");
70174d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu		goto out_error_pin_mux;
70274d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	}
70374d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu
704d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
7054311051c358ad0e66b68934e7a33cf10ba533466Yong Zhang		0, pdev->name, iface);
706d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	if (rc) {
707aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
708aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		rc = -ENODEV;
709aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		goto out_error_req_irq;
710d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	}
711d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
712d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* Set TWI internal clock as 10MHz */
713ac07fb4dc1908d300f50fa711982c9d750eb59f7Sonic Zhang	write_CONTROL(iface, ((get_sclk() / 1000 / 1000 + 5) / 10) & 0x7F);
714d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
7159528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich	/*
7169528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich	 * We will not end up with a CLKDIV=0 because no one will specify
717ac07fb4dc1908d300f50fa711982c9d750eb59f7Sonic Zhang	 * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250)
7189528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich	 */
719ac07fb4dc1908d300f50fa711982c9d750eb59f7Sonic Zhang	clkhilow = ((10 * 1000 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ) + 1) / 2;
7209528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich
721d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* Set Twi interface clock as specified */
7229528d1c7a541b481a0e80301dc8d545848104023Michael Hennerich	write_CLKDIV(iface, (clkhilow << 8) | clkhilow);
723d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
724d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	/* Enable TWI */
725aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
726d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	SSYNC();
727d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
728991dee591a99d035796a8c194eb1796cc020e142Kalle Pokki	rc = i2c_add_numbered_adapter(p_adap);
729aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	if (rc < 0) {
730aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		dev_err(&pdev->dev, "Can't add i2c adapter!\n");
731aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu		goto out_error_add_adapter;
732aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	}
733aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
734aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	platform_set_drvdata(pdev, iface);
735d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
736fa6ad222713a65980528348e7f75abc768b78297Bryan Wu	dev_info(&pdev->dev, "Blackfin BF5xx on-chip I2C TWI Contoller, "
737fa6ad222713a65980528348e7f75abc768b78297Bryan Wu		"regs_base@%p\n", iface->regs_base);
738aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
739aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	return 0;
740aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu
741aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_add_adapter:
742aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	free_irq(iface->irq, iface);
743aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_req_irq:
744aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_no_irq:
74574d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	peripheral_free_list(pin_req[pdev->id]);
74674d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wuout_error_pin_mux:
747aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	iounmap(iface->regs_base);
748aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_ioremap:
749aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_get_res:
750aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	kfree(iface);
751aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wuout_error_nomem:
752d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return rc;
753d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
754d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
755d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int i2c_bfin_twi_remove(struct platform_device *pdev)
756d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
757d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
758d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
759d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	platform_set_drvdata(pdev, NULL);
760d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
761d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	i2c_del_adapter(&(iface->adap));
762d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	free_irq(iface->irq, iface);
76374d362e0b3afb7a324855ab9675eb6cda78fda8cBryan Wu	peripheral_free_list(pin_req[pdev->id]);
764aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	iounmap(iface->regs_base);
765aa3d02091747727f7ff2e1f2455ad8363a9e6946Bryan Wu	kfree(iface);
766d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
767d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return 0;
768d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
769d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
770d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic struct platform_driver i2c_bfin_twi_driver = {
771d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.probe		= i2c_bfin_twi_probe,
772d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.remove		= i2c_bfin_twi_remove,
773d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.suspend	= i2c_bfin_twi_suspend,
774d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.resume		= i2c_bfin_twi_resume,
775d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	.driver		= {
776d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		.name	= "i2c-bfin-twi",
777d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu		.owner	= THIS_MODULE,
778d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	},
779d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu};
780d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
781d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic int __init i2c_bfin_twi_init(void)
782d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
783d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	return platform_driver_register(&i2c_bfin_twi_driver);
784d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
785d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
786d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wustatic void __exit i2c_bfin_twi_exit(void)
787d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu{
788d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu	platform_driver_unregister(&i2c_bfin_twi_driver);
789d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu}
790d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wu
79174f56c4ad4e4627862bddb0b8c3ab394e3c004f7Michael Hennerichsubsys_initcall(i2c_bfin_twi_init);
792d24ecfcc3953f9c3b833508cd839be614a3f3c64Bryan Wumodule_exit(i2c_bfin_twi_exit);
793fa6ad222713a65980528348e7f75abc768b78297Bryan Wu
794fa6ad222713a65980528348e7f75abc768b78297Bryan WuMODULE_AUTHOR("Bryan Wu, Sonic Zhang");
795fa6ad222713a65980528348e7f75abc768b78297Bryan WuMODULE_DESCRIPTION("Blackfin BF5xx on-chip I2C TWI Contoller Driver");
796fa6ad222713a65980528348e7f75abc768b78297Bryan WuMODULE_LICENSE("GPL");
797add8eda7f2be781af0224241e870715cf0cfd75aKay SieversMODULE_ALIAS("platform:i2c-bfin-twi");
798