/*
- * drivers/i2c/busses/i2c-bfin-twi.c
+ * Blackfin On-Chip Two Wire Interface Driver
*
- * Description: Driver for Blackfin Two Wire Interface
+ * Copyright 2005-2007 Analog Devices Inc.
*
- * Author: sonicz <sonic.zhang@analog.com>
+ * Enter bugs at http://blackfin.uclinux.org/
*
- * Copyright (c) 2005-2007 Analog Devices, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/io.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <asm/blackfin.h>
+#include <asm/portmux.h>
#include <asm/irq.h>
#define POLL_TIMEOUT (2 * HZ)
/* SMBus mode*/
-#define TWI_I2C_MODE_STANDARD 0x01
-#define TWI_I2C_MODE_STANDARDSUB 0x02
-#define TWI_I2C_MODE_COMBINED 0x04
+#define TWI_I2C_MODE_STANDARD 1
+#define TWI_I2C_MODE_STANDARDSUB 2
+#define TWI_I2C_MODE_COMBINED 3
+#define TWI_I2C_MODE_REPEAT 4
struct bfin_twi_iface {
- struct mutex twi_lock;
int irq;
spinlock_t lock;
char read_write;
struct timer_list timeout_timer;
struct i2c_adapter adap;
struct completion complete;
+ struct i2c_msg *pmsg;
+ int msg_num;
+ int cur_msg;
+ u16 saved_clkdiv;
+ u16 saved_control;
+ void __iomem *regs_base;
};
-static struct bfin_twi_iface twi_iface;
+
+#define DEFINE_TWI_REG(reg, off) \
+static inline u16 read_##reg(struct bfin_twi_iface *iface) \
+ { return bfin_read16(iface->regs_base + (off)); } \
+static inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \
+ { bfin_write16(iface->regs_base + (off), v); }
+
+DEFINE_TWI_REG(CLKDIV, 0x00)
+DEFINE_TWI_REG(CONTROL, 0x04)
+DEFINE_TWI_REG(SLAVE_CTL, 0x08)
+DEFINE_TWI_REG(SLAVE_STAT, 0x0C)
+DEFINE_TWI_REG(SLAVE_ADDR, 0x10)
+DEFINE_TWI_REG(MASTER_CTL, 0x14)
+DEFINE_TWI_REG(MASTER_STAT, 0x18)
+DEFINE_TWI_REG(MASTER_ADDR, 0x1C)
+DEFINE_TWI_REG(INT_STAT, 0x20)
+DEFINE_TWI_REG(INT_MASK, 0x24)
+DEFINE_TWI_REG(FIFO_CTL, 0x28)
+DEFINE_TWI_REG(FIFO_STAT, 0x2C)
+DEFINE_TWI_REG(XMT_DATA8, 0x80)
+DEFINE_TWI_REG(XMT_DATA16, 0x84)
+DEFINE_TWI_REG(RCV_DATA8, 0x88)
+DEFINE_TWI_REG(RCV_DATA16, 0x8C)
+
+static const u16 pin_req[2][3] = {
+ {P_TWI0_SCL, P_TWI0_SDA, 0},
+ {P_TWI1_SCL, P_TWI1_SDA, 0},
+};
static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
{
- unsigned short twi_int_status = bfin_read_TWI_INT_STAT();
- unsigned short mast_stat = bfin_read_TWI_MASTER_STAT();
+ unsigned short twi_int_status = read_INT_STAT(iface);
+ unsigned short mast_stat = read_MASTER_STAT(iface);
if (twi_int_status & XMTSERV) {
/* Transmit next data */
if (iface->writeNum > 0) {
- bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ write_XMT_DATA8(iface, *(iface->transPtr++));
iface->writeNum--;
}
/* start receive immediately after complete sending in
* combine mode.
*/
- else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
- | MDIR | RSTART);
- } else if (iface->manual_stop)
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
- | STOP);
+ else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | MDIR | RSTART);
+ else if (iface->manual_stop)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | STOP);
+ else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg + 1 < iface->msg_num) {
+ if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | RSTART | MDIR);
+ else
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+ }
SSYNC();
/* Clear status */
- bfin_write_TWI_INT_STAT(XMTSERV);
+ write_INT_STAT(iface, XMTSERV);
SSYNC();
}
if (twi_int_status & RCVSERV) {
if (iface->readNum > 0) {
/* Receive next data */
- *(iface->transPtr) = bfin_read_TWI_RCV_DATA8();
+ *(iface->transPtr) = read_RCV_DATA8(iface);
if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
/* Change combine mode into sub mode after
* read first data.
iface->transPtr++;
iface->readNum--;
} else if (iface->manual_stop) {
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
- | STOP);
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | STOP);
+ SSYNC();
+ } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg + 1 < iface->msg_num) {
+ if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | RSTART | MDIR);
+ else
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
SSYNC();
}
/* Clear interrupt source */
- bfin_write_TWI_INT_STAT(RCVSERV);
+ write_INT_STAT(iface, RCVSERV);
SSYNC();
}
if (twi_int_status & MERR) {
- bfin_write_TWI_INT_STAT(MERR);
- bfin_write_TWI_INT_MASK(0);
- bfin_write_TWI_MASTER_STAT(0x3e);
- bfin_write_TWI_MASTER_CTL(0);
+ write_INT_STAT(iface, MERR);
+ write_INT_MASK(iface, 0);
+ write_MASTER_STAT(iface, 0x3e);
+ write_MASTER_CTL(iface, 0);
SSYNC();
- iface->result = -1;
+ iface->result = -EIO;
/* if both err and complete int stats are set, return proper
* results.
*/
if (twi_int_status & MCOMP) {
- bfin_write_TWI_INT_STAT(MCOMP);
- bfin_write_TWI_INT_MASK(0);
- bfin_write_TWI_MASTER_CTL(0);
+ write_INT_STAT(iface, MCOMP);
+ write_INT_MASK(iface, 0);
+ write_MASTER_CTL(iface, 0);
SSYNC();
/* If it is a quick transfer, only address bug no data,
* not an err, return 1.
return;
}
if (twi_int_status & MCOMP) {
- bfin_write_TWI_INT_STAT(MCOMP);
+ write_INT_STAT(iface, MCOMP);
SSYNC();
if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
if (iface->readNum == 0) {
*/
iface->readNum = 1;
iface->manual_stop = 1;
- bfin_write_TWI_MASTER_CTL(
- bfin_read_TWI_MASTER_CTL()
- | (0xff << 6));
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | (0xff << 6));
} else {
/* set the readd number in other
* combine mode.
*/
- bfin_write_TWI_MASTER_CTL(
- (bfin_read_TWI_MASTER_CTL() &
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) &
+ (~(0xff << 6))) |
+ (iface->readNum << 6));
+ }
+ /* remove restart bit and enable master receive */
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) & ~RSTART);
+ SSYNC();
+ } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg+1 < iface->msg_num) {
+ iface->cur_msg++;
+ iface->transPtr = iface->pmsg[iface->cur_msg].buf;
+ iface->writeNum = iface->readNum =
+ iface->pmsg[iface->cur_msg].len;
+ /* Set Transmit device address */
+ write_MASTER_ADDR(iface,
+ iface->pmsg[iface->cur_msg].addr);
+ if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ write_XMT_DATA8(iface,
+ *(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
+ }
+ }
+
+ if (iface->pmsg[iface->cur_msg].len <= 255)
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) &
(~(0xff << 6))) |
- ( iface->readNum << 6));
+ (iface->pmsg[iface->cur_msg].len << 6));
+ else {
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) |
+ (0xff << 6)));
+ iface->manual_stop = 1;
}
/* remove restart bit and enable master receive */
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() &
- ~RSTART);
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
- MEN | MDIR);
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) & ~RSTART);
SSYNC();
} else {
iface->result = 1;
- bfin_write_TWI_INT_MASK(0);
- bfin_write_TWI_MASTER_CTL(0);
+ write_INT_MASK(iface, 0);
+ write_MASTER_CTL(iface, 0);
SSYNC();
complete(&iface->complete);
}
{
struct bfin_twi_iface *iface = adap->algo_data;
struct i2c_msg *pmsg;
- int i, ret;
int rc = 0;
- if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ if (!(read_CONTROL(iface) & TWI_ENA))
return -ENXIO;
- mutex_lock(&iface->twi_lock);
-
- while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
- mutex_unlock(&iface->twi_lock);
+ while (read_MASTER_STAT(iface) & BUSBUSY)
yield();
- mutex_lock(&iface->twi_lock);
+
+ iface->pmsg = msgs;
+ iface->msg_num = num;
+ iface->cur_msg = 0;
+
+ pmsg = &msgs[0];
+ if (pmsg->flags & I2C_M_TEN) {
+ dev_err(&adap->dev, "10 bits addr not supported!\n");
+ return -EINVAL;
}
- ret = 0;
- for (i = 0; rc >= 0 && i < num; i++) {
- pmsg = &msgs[i];
- if (pmsg->flags & I2C_M_TEN) {
- dev_err(&(adap->dev), "i2c-bfin-twi: 10 bits addr "
- "not supported !\n");
- rc = -EINVAL;
- break;
- }
+ iface->cur_mode = TWI_I2C_MODE_REPEAT;
+ iface->manual_stop = 0;
+ iface->transPtr = pmsg->buf;
+ iface->writeNum = iface->readNum = pmsg->len;
+ iface->result = 0;
+ iface->timeout_count = 10;
+ init_completion(&(iface->complete));
+ /* Set Transmit device address */
+ write_MASTER_ADDR(iface, pmsg->addr);
- iface->cur_mode = TWI_I2C_MODE_STANDARD;
- iface->manual_stop = 0;
- iface->transPtr = pmsg->buf;
- iface->writeNum = iface->readNum = pmsg->len;
- iface->result = 0;
- iface->timeout_count = 10;
- /* Set Transmit device address */
- bfin_write_TWI_MASTER_ADDR(pmsg->addr);
-
- /* FIFO Initiation. Data in FIFO should be
- * discarded before start a new operation.
- */
- bfin_write_TWI_FIFO_CTL(0x3);
- SSYNC();
- bfin_write_TWI_FIFO_CTL(0);
- SSYNC();
+ /* FIFO Initiation. Data in FIFO should be
+ * discarded before start a new operation.
+ */
+ write_FIFO_CTL(iface, 0x3);
+ SSYNC();
+ write_FIFO_CTL(iface, 0);
+ SSYNC();
- if (pmsg->flags & I2C_M_RD)
- iface->read_write = I2C_SMBUS_READ;
- else {
- iface->read_write = I2C_SMBUS_WRITE;
- /* Transmit first data */
- if (iface->writeNum > 0) {
- bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
- iface->writeNum--;
- SSYNC();
- }
+ if (pmsg->flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ write_XMT_DATA8(iface, *(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
}
+ }
- /* clear int stat */
- bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
-
- /* Interrupt mask . Enable XMT, RCV interrupt */
- bfin_write_TWI_INT_MASK(MCOMP | MERR |
- ((iface->read_write == I2C_SMBUS_READ)?
- RCVSERV : XMTSERV));
- SSYNC();
+ /* clear int stat */
+ write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
- if (pmsg->len > 0 && pmsg->len <= 255)
- bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
- else if (pmsg->len > 255) {
- bfin_write_TWI_MASTER_CTL(0xff << 6);
- iface->manual_stop = 1;
- } else
- break;
+ /* Interrupt mask . Enable XMT, RCV interrupt */
+ write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
+ SSYNC();
- iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
- add_timer(&iface->timeout_timer);
+ if (pmsg->len <= 255)
+ write_MASTER_CTL(iface, pmsg->len << 6);
+ else {
+ write_MASTER_CTL(iface, 0xff << 6);
+ iface->manual_stop = 1;
+ }
- /* Master enable */
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
- ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
- ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
- SSYNC();
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
- wait_for_completion(&iface->complete);
+ /* Master enable */
+ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
+ ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
+ SSYNC();
- rc = iface->result;
- if (rc == 1)
- ret++;
- else if (rc == -1)
- break;
- }
+ wait_for_completion(&iface->complete);
- /* Release mutex */
- mutex_unlock(&iface->twi_lock);
+ rc = iface->result;
- return ret;
+ if (rc == 1)
+ return num;
+ else
+ return rc;
}
/*
struct bfin_twi_iface *iface = adap->algo_data;
int rc = 0;
- if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ if (!(read_CONTROL(iface) & TWI_ENA))
return -ENXIO;
- mutex_lock(&iface->twi_lock);
-
- while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
- mutex_unlock(&iface->twi_lock);
+ while (read_MASTER_STAT(iface) & BUSBUSY)
yield();
- mutex_lock(&iface->twi_lock);
- }
iface->writeNum = 0;
iface->readNum = 0;
}
iface->transPtr = data->block;
break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = data->block[0];
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = data->block[0];
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = (u8 *)&data->block[1];
+ break;
default:
return -1;
}
iface->read_write = read_write;
iface->command = command;
iface->timeout_count = 10;
+ init_completion(&(iface->complete));
/* FIFO Initiation. Data in FIFO should be discarded before
* start a new operation.
*/
- bfin_write_TWI_FIFO_CTL(0x3);
+ write_FIFO_CTL(iface, 0x3);
SSYNC();
- bfin_write_TWI_FIFO_CTL(0);
+ write_FIFO_CTL(iface, 0);
/* clear int stat */
- bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+ write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
/* Set Transmit device address */
- bfin_write_TWI_MASTER_ADDR(addr);
+ write_MASTER_ADDR(iface, addr);
SSYNC();
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
switch (iface->cur_mode) {
case TWI_I2C_MODE_STANDARDSUB:
- bfin_write_TWI_XMT_DATA8(iface->command);
- bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ write_XMT_DATA8(iface, iface->command);
+ write_INT_MASK(iface, MCOMP | MERR |
((iface->read_write == I2C_SMBUS_READ) ?
RCVSERV : XMTSERV));
SSYNC();
if (iface->writeNum + 1 <= 255)
- bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6);
+ write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
else {
- bfin_write_TWI_MASTER_CTL(0xff << 6);
+ write_MASTER_CTL(iface, 0xff << 6);
iface->manual_stop = 1;
}
/* Master enable */
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
break;
case TWI_I2C_MODE_COMBINED:
- bfin_write_TWI_XMT_DATA8(iface->command);
- bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV);
+ write_XMT_DATA8(iface, iface->command);
+ write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
SSYNC();
if (iface->writeNum > 0)
- bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6);
+ write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
else
- bfin_write_TWI_MASTER_CTL(0x1 << 6);
+ write_MASTER_CTL(iface, 0x1 << 6);
/* Master enable */
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
break;
default:
- bfin_write_TWI_MASTER_CTL(0);
+ write_MASTER_CTL(iface, 0);
if (size != I2C_SMBUS_QUICK) {
/* Don't access xmit data register when this is a
* read operation.
*/
if (iface->read_write != I2C_SMBUS_READ) {
if (iface->writeNum > 0) {
- bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ write_XMT_DATA8(iface,
+ *(iface->transPtr++));
if (iface->writeNum <= 255)
- bfin_write_TWI_MASTER_CTL(iface->writeNum << 6);
+ write_MASTER_CTL(iface,
+ iface->writeNum << 6);
else {
- bfin_write_TWI_MASTER_CTL(0xff << 6);
+ write_MASTER_CTL(iface,
+ 0xff << 6);
iface->manual_stop = 1;
}
iface->writeNum--;
} else {
- bfin_write_TWI_XMT_DATA8(iface->command);
- bfin_write_TWI_MASTER_CTL(1 << 6);
+ write_XMT_DATA8(iface, iface->command);
+ write_MASTER_CTL(iface, 1 << 6);
}
} else {
if (iface->readNum > 0 && iface->readNum <= 255)
- bfin_write_TWI_MASTER_CTL(iface->readNum << 6);
+ write_MASTER_CTL(iface,
+ iface->readNum << 6);
else if (iface->readNum > 255) {
- bfin_write_TWI_MASTER_CTL(0xff << 6);
+ write_MASTER_CTL(iface, 0xff << 6);
iface->manual_stop = 1;
} else {
del_timer(&iface->timeout_timer);
}
}
}
- bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ write_INT_MASK(iface, MCOMP | MERR |
((iface->read_write == I2C_SMBUS_READ) ?
RCVSERV : XMTSERV));
SSYNC();
/* Master enable */
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
break;
rc = (iface->result >= 0) ? 0 : -1;
- /* Release mutex */
- mutex_unlock(&iface->twi_lock);
-
return rc;
}
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
- I2C_FUNC_I2C;
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
}
-
static struct i2c_algorithm bfin_twi_algorithm = {
.master_xfer = bfin_twi_master_xfer,
.smbus_xfer = bfin_twi_smbus_xfer,
.functionality = bfin_twi_functionality,
};
-
-static int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state)
+static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state)
{
-/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/
+ struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+
+ iface->saved_clkdiv = read_CLKDIV(iface);
+ iface->saved_control = read_CONTROL(iface);
+
+ free_irq(iface->irq, iface);
/* Disable TWI */
- bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() & ~TWI_ENA);
- SSYNC();
+ write_CONTROL(iface, iface->saved_control & ~TWI_ENA);
return 0;
}
-static int i2c_bfin_twi_resume(struct platform_device *dev)
+static int i2c_bfin_twi_resume(struct platform_device *pdev)
{
-/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/
+ struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
- /* Enable TWI */
- bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
- SSYNC();
+ int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
+ IRQF_DISABLED, pdev->name, iface);
+ if (rc) {
+ dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
+ return -ENODEV;
+ }
+
+ /* Resume TWI interface clock as specified */
+ write_CLKDIV(iface, iface->saved_clkdiv);
+
+ /* Resume TWI */
+ write_CONTROL(iface, iface->saved_control);
return 0;
}
-static int i2c_bfin_twi_probe(struct platform_device *dev)
+static int i2c_bfin_twi_probe(struct platform_device *pdev)
{
- struct bfin_twi_iface *iface = &twi_iface;
+ struct bfin_twi_iface *iface;
struct i2c_adapter *p_adap;
+ struct resource *res;
int rc;
+ unsigned int clkhilow;
+
+ iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL);
+ if (!iface) {
+ dev_err(&pdev->dev, "Cannot allocate memory\n");
+ rc = -ENOMEM;
+ goto out_error_nomem;
+ }
- mutex_init(&(iface->twi_lock));
spin_lock_init(&(iface->lock));
- init_completion(&(iface->complete));
- iface->irq = IRQ_TWI;
+
+ /* Find and map our resources */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+ rc = -ENOENT;
+ goto out_error_get_res;
+ }
+
+ iface->regs_base = ioremap(res->start, resource_size(res));
+ if (iface->regs_base == NULL) {
+ dev_err(&pdev->dev, "Cannot map IO\n");
+ rc = -ENXIO;
+ goto out_error_ioremap;
+ }
+
+ iface->irq = platform_get_irq(pdev, 0);
+ if (iface->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ specified\n");
+ rc = -ENOENT;
+ goto out_error_no_irq;
+ }
init_timer(&(iface->timeout_timer));
iface->timeout_timer.function = bfin_twi_timeout;
iface->timeout_timer.data = (unsigned long)iface;
p_adap = &iface->adap;
- p_adap->id = I2C_HW_BLACKFIN;
- strlcpy(p_adap->name, dev->name, sizeof(p_adap->name));
+ p_adap->nr = pdev->id;
+ strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
p_adap->algo = &bfin_twi_algorithm;
p_adap->algo_data = iface;
- p_adap->class = I2C_CLASS_ALL;
- p_adap->dev.parent = &dev->dev;
+ p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ p_adap->dev.parent = &pdev->dev;
+
+ rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
+ if (rc) {
+ dev_err(&pdev->dev, "Can't setup pin mux!\n");
+ goto out_error_pin_mux;
+ }
rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- IRQF_DISABLED, dev->name, iface);
+ IRQF_DISABLED, pdev->name, iface);
if (rc) {
- dev_err(&(p_adap->dev), "i2c-bfin-twi: can't get IRQ %d !\n",
- iface->irq);
- return -ENODEV;
+ dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
+ rc = -ENODEV;
+ goto out_error_req_irq;
}
/* Set TWI internal clock as 10MHz */
- bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
+ write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
+
+ /*
+ * We will not end up with a CLKDIV=0 because no one will specify
+ * 20kHz SCL or less in Kconfig now. (5 * 1024 / 20 = 0x100)
+ */
+ clkhilow = 5 * 1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ;
/* Set Twi interface clock as specified */
- bfin_write_TWI_CLKDIV((( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
- << 8) | (( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
- & 0xFF));
+ write_CLKDIV(iface, (clkhilow << 8) | clkhilow);
/* Enable TWI */
- bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
+ write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
SSYNC();
- rc = i2c_add_adapter(p_adap);
- if (rc < 0)
- free_irq(iface->irq, iface);
- else
- platform_set_drvdata(dev, iface);
+ rc = i2c_add_numbered_adapter(p_adap);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Can't add i2c adapter!\n");
+ goto out_error_add_adapter;
+ }
+
+ platform_set_drvdata(pdev, iface);
+
+ dev_info(&pdev->dev, "Blackfin BF5xx on-chip I2C TWI Contoller, "
+ "regs_base@%p\n", iface->regs_base);
+
+ return 0;
+out_error_add_adapter:
+ free_irq(iface->irq, iface);
+out_error_req_irq:
+out_error_no_irq:
+ peripheral_free_list(pin_req[pdev->id]);
+out_error_pin_mux:
+ iounmap(iface->regs_base);
+out_error_ioremap:
+out_error_get_res:
+ kfree(iface);
+out_error_nomem:
return rc;
}
i2c_del_adapter(&(iface->adap));
free_irq(iface->irq, iface);
+ peripheral_free_list(pin_req[pdev->id]);
+ iounmap(iface->regs_base);
+ kfree(iface);
return 0;
}
static int __init i2c_bfin_twi_init(void)
{
- pr_info("I2C: Blackfin I2C TWI driver\n");
-
return platform_driver_register(&i2c_bfin_twi_driver);
}
platform_driver_unregister(&i2c_bfin_twi_driver);
}
-MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for Blackfin TWI");
-MODULE_LICENSE("GPL");
-
module_init(i2c_bfin_twi_init);
module_exit(i2c_bfin_twi_exit);
+
+MODULE_AUTHOR("Bryan Wu, Sonic Zhang");
+MODULE_DESCRIPTION("Blackfin BF5xx on-chip I2C TWI Contoller Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c-bfin-twi");