LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
@ 2007-03-21 10:08 Wu, Bryan
2007-03-21 17:56 ` Jean Delvare
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Wu, Bryan @ 2007-03-21 10:08 UTC (permalink / raw)
To: khali, David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
Hi folks,
As GPIO based blackfin driver will be replaced by I2C-GPIO generic
driver, we just update this latest version of blackfin on-chip TWI I2C
driver according to LKML review.
[PATCH] Blackfin: on-chip Two Wire Interface I2C driver
The i2c linux driver for blackfin architecture which supports blackfin
on-chip TWI controller i2c operation.
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
Reviewed-by: Jean Delvare <khali@linux-fr.org>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
drivers/i2c/busses/Kconfig | 16
drivers/i2c/busses/Makefile | 1
drivers/i2c/busses/i2c-bfin-twi.c | 653 ++++++++++++++++++++++++++++++++++++++
3 files changed, 670 insertions(+)
Index: linux-2.6/drivers/i2c/busses/Kconfig
===================================================================
--- linux-2.6.orig/drivers/i2c/busses/Kconfig
+++ linux-2.6/drivers/i2c/busses/Kconfig
@@ -91,6 +91,22 @@
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
+config I2C_BLACKFIN_TWI
+ tristate "Blackfin TWI I2C support"
+ depends on I2C && (BF534 || BF536 || BF537)
+ help
+ This is the TWI I2C device driver for Blackfin 534/536/537.
+ This driver can also be built as a module. If so, the module
+ will be called i2c-bfin-twi.
+
+config I2C_BLACKFIN_TWI_CLK_KHZ
+ int "Blackfin TWI I2C clock (kHz)"
+ depends on I2C_BLACKFIN_TWI
+ range 10 400
+ default 50
+ help
+ The unit of the TWI clock is kilo HZ.
+
config I2C_ELEKTOR
tristate "Elektor ISA card"
depends on I2C && ISA && BROKEN_ON_SMP
Index: linux-2.6/drivers/i2c/busses/Makefile
===================================================================
--- linux-2.6.orig/drivers/i2c/busses/Makefile
+++ linux-2.6/drivers/i2c/busses/Makefile
@@ -10,6 +10,7 @@
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
+obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
Index: linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
@@ -0,0 +1,653 @@
+/*
+ * drivers/i2c/busses/i2c-bfin-twi.c
+ *
+ * Description: Driver for Blackfin Two Wire Interface
+ *
+ * Author: sonicz <sonic.zhang@analog.com>
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/blackfin.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
+
+struct bfin_twi_iface {
+ struct mutex twi_lock;
+ int irq;
+ spinlock_t lock;
+ char read_write;
+ u8 command;
+ u8 *transPtr;
+ int readNum;
+ int writeNum;
+ int cur_mode;
+ int manual_stop;
+ int result;
+ int timeout_count;
+ struct timer_list timeout_timer;
+ struct i2c_adapter adap;
+ struct completion complete;
+};
+
+static struct bfin_twi_iface twi_iface;
+
+static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
+{
+ unsigned short twi_int_status = bfin_read_TWI_INT_STAT();
+ unsigned short mast_stat = bfin_read_TWI_MASTER_STAT();
+
+ if (twi_int_status & XMTSERV) {
+ /* Transmit next data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ }
+ /* start receive immediately after complete sending in
+ * combine mode.
+ */
+ else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | MDIR | RSTART);
+ } else if (iface->manual_stop)
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | STOP);
+ SSYNC();
+ /* Clear status */
+ bfin_write_TWI_INT_STAT(XMTSERV);
+ SSYNC();
+ }
+ if (twi_int_status & RCVSERV) {
+ if (iface->readNum > 0) {
+ /* Receive next data */
+ *(iface->transPtr) = bfin_read_TWI_RCV_DATA8();
+ if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ /* Change combine mode into sub mode after
+ * read first data.
+ */
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ /* Get read number from first byte in block
+ * combine mode.
+ */
+ if (iface->readNum == 1 && iface->manual_stop)
+ iface->readNum = *iface->transPtr + 1;
+ }
+ iface->transPtr++;
+ iface->readNum--;
+ } else if (iface->manual_stop) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | STOP);
+ SSYNC();
+ }
+ /* Clear interrupt source */
+ bfin_write_TWI_INT_STAT(RCVSERV);
+ SSYNC();
+ }
+ if (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);
+ SSYNC();
+ iface->result = -1;
+ /* 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);
+ SSYNC();
+ /* If it is a quick transfer, only address bug no data,
+ * not an err, return 1.
+ */
+ if (iface->writeNum == 0 && (mast_stat & BUFRDERR))
+ iface->result = 1;
+ /* If address not acknowledged return -1,
+ * else return 0.
+ */
+ else if (!(mast_stat & ANAK))
+ iface->result = 0;
+ }
+ complete(&iface->complete);
+ return;
+ }
+ if (twi_int_status & MCOMP) {
+ bfin_write_TWI_INT_STAT(MCOMP);
+ SSYNC();
+ if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ if (iface->readNum == 0) {
+ /* set the read number to 1 and ask for manual
+ * stop in block combine mode
+ */
+ iface->readNum = 1;
+ iface->manual_stop = 1;
+ bfin_write_TWI_MASTER_CTL(
+ bfin_read_TWI_MASTER_CTL()
+ | (0xff << 6));
+ } else {
+ /* set the readd number in other
+ * combine mode.
+ */
+ bfin_write_TWI_MASTER_CTL(
+ (bfin_read_TWI_MASTER_CTL() &
+ (~(0xff << 6))) |
+ ( iface->readNum << 6));
+ }
+ /* remove restart bit and enable master receive */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() &
+ ~RSTART);
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
+ MEN | MDIR);
+ SSYNC();
+ } else {
+ iface->result = 1;
+ bfin_write_TWI_INT_MASK(0);
+ bfin_write_TWI_MASTER_CTL(0);
+ SSYNC();
+ complete(&iface->complete);
+ }
+ }
+}
+
+/* Interrupt handler */
+static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
+{
+ struct bfin_twi_iface *iface = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iface->lock, flags);
+ del_timer(&iface->timeout_timer);
+ bfin_twi_handle_interrupt(iface);
+ spin_unlock_irqrestore(&iface->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void bfin_twi_timeout(unsigned long data)
+{
+ struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iface->lock, flags);
+ bfin_twi_handle_interrupt(iface);
+ if (iface->result == 0) {
+ iface->timeout_count--;
+ if (iface->timeout_count > 0) {
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+ } else {
+ iface->result = -1;
+ complete(&iface->complete);
+ }
+ }
+ spin_unlock_irqrestore(&iface->lock, flags);
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int bfin_twi_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct bfin_twi_iface *iface = adap->algo_data;
+ struct i2c_msg *pmsg;
+ int i, ret;
+ int rc = 0;
+
+ if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ return -ENXIO;
+
+ mutex_lock(&iface->twi_lock);
+
+ while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
+ mutex_unlock(&iface->twi_lock);
+ yield();
+ mutex_lock(&iface->twi_lock);
+ }
+
+ 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_STANDARD;
+ iface->manual_stop = 0;
+ iface->transPtr = pmsg->buf;
+ iface->writeNum = iface->readNum = pmsg->len;
+ iface->result = 0;
+ iface->timeout_count = 10;
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(pmsg->addr);
+
+ /* FIFO Initiation. Data in FIFO should be
+ * discarded before start a new operation.
+ */
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+ SSYNC();
+
+ if (pmsg->flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
+ }
+ }
+
+ /* clear int stat */
+ bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+
+ /* Interrupt mask . Enable XMT, RCV interrupt */
+ bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ ((iface->read_write == I2C_SMBUS_READ)?
+ RCVSERV : XMTSERV));
+ SSYNC();
+
+ if (pmsg->len > 0 && pmsg->len <= 255)
+ bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
+ else if (pmsg->len > 255) {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ } else
+ break;
+
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
+ SSYNC();
+
+ wait_for_completion(&iface->complete);
+
+ rc = iface->result;
+ if (rc == 1)
+ ret++;
+ else if (rc == -1)
+ break;
+ }
+
+ /* Release mutex */
+ mutex_unlock(&iface->twi_lock);
+
+ return ret;
+}
+
+/*
+ * SMBus type transfer entrypoint
+ */
+
+int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ struct bfin_twi_iface *iface = adap->algo_data;
+ int rc = 0;
+
+ if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ return -ENXIO;
+
+ mutex_lock(&iface->twi_lock);
+
+ while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
+ mutex_unlock(&iface->twi_lock);
+ yield();
+ mutex_lock(&iface->twi_lock);
+ }
+
+ iface->writeNum = 0;
+ iface->readNum = 0;
+
+ /* Prepare datas & select mode */
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ iface->transPtr = NULL;
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ break;
+ case I2C_SMBUS_BYTE:
+ if (data == NULL)
+ iface->transPtr = NULL;
+ else {
+ if (read_write == I2C_SMBUS_READ)
+ iface->readNum = 1;
+ else
+ iface->writeNum = 1;
+ iface->transPtr = &data->byte;
+ }
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 1;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = 1;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = &data->byte;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = (u8 *)&data->word;
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ iface->writeNum = 2;
+ iface->readNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ iface->transPtr = (u8 *)&data->word;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 0;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = data->block[0] + 1;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = data->block;
+ break;
+ default:
+ return -1;
+ }
+
+ iface->result = 0;
+ iface->manual_stop = 0;
+ iface->read_write = read_write;
+ iface->command = command;
+ iface->timeout_count = 10;
+
+ /* FIFO Initiation. Data in FIFO should be discarded before
+ * start a new operation.
+ */
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+
+ /* clear int stat */
+ bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(addr);
+ SSYNC();
+
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+
+ switch (iface->cur_mode) {
+ case TWI_I2C_MODE_STANDARDSUB:
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ ((iface->read_write == I2C_SMBUS_READ) ?
+ RCVSERV : XMTSERV));
+ SSYNC();
+
+ if (iface->writeNum + 1 <= 255)
+ bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6);
+ else {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ }
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((CONFIG_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);
+ SSYNC();
+
+ if (iface->writeNum > 0)
+ bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6);
+ else
+ bfin_write_TWI_MASTER_CTL(0x1 << 6);
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
+ break;
+ default:
+ bfin_write_TWI_MASTER_CTL(0);
+ if (size != I2C_SMBUS_QUICK) {
+ /* Don't access xmit data register when this is a
+ * read operation.
+ */
+ if (iface->read_write != I2C_SMBUS_READ) {
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ if (iface->writeNum <= 255)
+ bfin_write_TWI_MASTER_CTL(iface->writeNum << 6);
+ else {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ }
+ iface->writeNum--;
+ } else {
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_MASTER_CTL(1 << 6);
+ }
+ } else {
+ if (iface->readNum > 0 && iface->readNum <= 255)
+ bfin_write_TWI_MASTER_CTL(iface->readNum << 6);
+ else if (iface->readNum > 255) {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ } else {
+ del_timer(&iface->timeout_timer);
+ break;
+ }
+ }
+ }
+ bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ ((iface->read_write == I2C_SMBUS_READ) ?
+ RCVSERV : XMTSERV));
+ SSYNC();
+
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
+ break;
+ }
+ SSYNC();
+
+ wait_for_completion(&iface->complete);
+
+ rc = (iface->result >= 0) ? 0 : -1;
+
+ /* Release mutex */
+ mutex_unlock(&iface->twi_lock);
+
+ return rc;
+}
+
+/*
+ * Return what the adapter supports
+ */
+static u32 bfin_twi_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_I2C;
+}
+
+
+static struct i2c_algorithm bfin_twi_algorithm = {
+ .master_xfer = bfin_twi_master_xfer,
+ .smbus_xfer = bfin_twi_smbus_xfer,
+ .functionality = bfin_twi_functionality,
+};
+
+
+static int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state)
+{
+/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/
+
+ /* Disable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() & ~TWI_ENA);
+ SSYNC();
+
+ return 0;
+}
+
+static int i2c_bfin_twi_resume(struct platform_device *dev)
+{
+/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/
+
+ /* Enable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
+ SSYNC();
+
+ return 0;
+}
+
+static int i2c_bfin_twi_probe(struct platform_device *dev)
+{
+ struct bfin_twi_iface *iface = &twi_iface;
+ struct i2c_adapter *p_adap;
+ int rc;
+
+ mutex_init(&(iface->twi_lock));
+ spin_lock_init(&(iface->lock));
+ init_completion(&(iface->complete));
+ iface->irq = IRQ_TWI;
+
+ 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_DRIVERID_BLACKFINTWI;
+ strlcpy(p_adap->name, dev->name, I2C_NAME_SIZE);
+ p_adap->algo = &bfin_twi_algorithm;
+ p_adap->algo_data = iface;
+ p_adap->class = I2C_CLASS_ALL;
+ p_adap->dev.parent = &(dev->dev);
+
+ rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
+ SA_INTERRUPT, dev->name, iface);
+ if (rc) {
+ printk(KERN_ERR"i2c-bfin-twi: can't get IRQ %d !\n",
+ iface->irq);
+ return -ENODEV;
+ }
+
+ /* Set TWI internal clock as 10MHz */
+ bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
+
+ /* Set Twi interface clock as specified */
+ if (CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 400)
+ bfin_write_TWI_CLKDIV((( 5*1024 / 400 ) << 8) |
+ (( 5*1024 / 400 ) & 0xFF));
+ else
+ bfin_write_TWI_CLKDIV((( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
+ << 8) | (( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
+ & 0xFF));
+
+ /* Enable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
+ SSYNC();
+
+ rc = i2c_add_adapter(p_adap);
+ if (rc < 0)
+ free_irq(iface->irq, iface);
+ else
+ platform_set_drvdata(dev, iface);
+
+ return rc;
+}
+
+static int i2c_bfin_twi_remove(struct platform_device *pdev)
+{
+ struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ i2c_del_adapter(&(iface->adap));
+ free_irq(iface->irq, iface);
+
+ return 0;
+}
+
+static struct platform_driver i2c_bfin_twi_driver = {
+ .probe = i2c_bfin_twi_probe,
+ .remove = i2c_bfin_twi_remove,
+ .suspend = i2c_bfin_twi_suspend,
+ .resume = i2c_bfin_twi_resume,
+ .driver = {
+ .name = "i2c-bfin-twi",
+ },
+};
+
+static int __init i2c_bfin_twi_init(void)
+{
+ int ret;
+
+ pr_info("I2C: Blackfin I2C TWI driver\n");
+
+ ret = platform_driver_register(&i2c_bfin_twi_driver);
+ if (ret) {
+ pr_debug("Fail to register i2c_bfin_twi driver\n");
+ }
+ return ret;
+}
+
+static void __exit i2c_bfin_twi_exit(void)
+{
+ 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);
_
Thanks
-Bryan Wu
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-21 10:08 [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver Wu, Bryan
@ 2007-03-21 17:56 ` Jean Delvare
2007-03-21 18:17 ` Bob Copeland
2007-03-21 19:22 ` Jean Delvare
2 siblings, 0 replies; 14+ messages in thread
From: Jean Delvare @ 2007-03-21 17:56 UTC (permalink / raw)
To: Bryan Wu; +Cc: David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
Hi Bryan,
What's the point of setting the Reply-To header to your own address,
other than forcing me to add your name manually each time I reply?
On Wed, 21 Mar 2007 18:08:08 +0800, Wu, Bryan wrote:
> As GPIO based blackfin driver will be replaced by I2C-GPIO generic
> driver, we just update this latest version of blackfin on-chip TWI I2C
> driver according to LKML review.
Good idea.
> [PATCH] Blackfin: on-chip Two Wire Interface I2C driver
>
> The i2c linux driver for blackfin architecture which supports blackfin
> on-chip TWI controller i2c operation.
>
> Signed-off-by: Bryan Wu <bryan.wu@analog.com>
> Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
> Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
> Reviewed-by: Jean Delvare <khali@linux-fr.org>
> Cc: David Brownell <david-b@pacbell.net>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
>
> drivers/i2c/busses/Kconfig | 16
> drivers/i2c/busses/Makefile | 1
> drivers/i2c/busses/i2c-bfin-twi.c | 653 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 670 insertions(+)
This is missing a MAINTAINERS entry. Please provide a patch adding it,
and I'm push this patch on my i2c stack (for Linux 2.6.22.)
> +static int __init i2c_bfin_twi_init(void)
> +{
> + int ret;
> +
> + pr_info("I2C: Blackfin I2C TWI driver\n");
> +
> + ret = platform_driver_register(&i2c_bfin_twi_driver);
> + if (ret) {
> + pr_debug("Fail to register i2c_bfin_twi driver\n");
> + }
> + return ret;
> +}
That pr_debug isn't exactly useful. If the registration failed, the
user will notice soon enough, as the driver will not be loaded at all
and modprobe will return the error code (which is more valuable for
debugging than the message itself.)
--
Jean Delvare
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-21 10:08 [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver Wu, Bryan
2007-03-21 17:56 ` Jean Delvare
@ 2007-03-21 18:17 ` Bob Copeland
2007-03-21 19:14 ` Mike Frysinger
2007-03-21 19:22 ` Jean Delvare
2 siblings, 1 reply; 14+ messages in thread
From: Bob Copeland @ 2007-03-21 18:17 UTC (permalink / raw)
To: bryan.wu
Cc: khali, David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
> +config I2C_BLACKFIN_TWI
> + tristate "Blackfin TWI I2C support"
> + depends on I2C && (BF534 || BF536 || BF537)
> + help
> + This is the TWI I2C device driver for Blackfin 534/536/537.
> + This driver can also be built as a module. If so, the module
> + will be called i2c-bfin-twi.
Pardon my ignorance, but is there any reason to call it "Blackfin TWI"
as opposed to just "Blackfin" since TWI and I2C mean the same thing?
It lends itself to some redundancies such as:
> + pr_info("I2C: Blackfin I2C TWI driver\n");
-Bob
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-21 18:17 ` Bob Copeland
@ 2007-03-21 19:14 ` Mike Frysinger
2007-03-22 7:03 ` Jean Delvare
0 siblings, 1 reply; 14+ messages in thread
From: Mike Frysinger @ 2007-03-21 19:14 UTC (permalink / raw)
To: Bob Copeland
Cc: bryan.wu, khali, David Brownell, Andrew Morton, Alexey Dobriyan,
linux-kernel
On 3/21/07, Bob Copeland <me@bobcopeland.com> wrote:
> > +config I2C_BLACKFIN_TWI
> > + tristate "Blackfin TWI I2C support"
> > + depends on I2C && (BF534 || BF536 || BF537)
> > + help
> > + This is the TWI I2C device driver for Blackfin 534/536/537.
> > + This driver can also be built as a module. If so, the module
> > + will be called i2c-bfin-twi.
>
> Pardon my ignorance, but is there any reason to call it "Blackfin TWI"
> as opposed to just "Blackfin" since TWI and I2C mean the same thing?
> It lends itself to some redundancies such as:
>
> > + pr_info("I2C: Blackfin I2C TWI driver\n");
it reflects the hardware manual and all public Analog Devices
documentation ... the peripheral is always referred to as a "Two Wire
Interface that is fully compatible with the I2C standard" since using
the "I2C" term is not free
-mike
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-21 10:08 [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver Wu, Bryan
2007-03-21 17:56 ` Jean Delvare
2007-03-21 18:17 ` Bob Copeland
@ 2007-03-21 19:22 ` Jean Delvare
2007-03-22 5:39 ` Mike Frysinger
2 siblings, 1 reply; 14+ messages in thread
From: Jean Delvare @ 2007-03-21 19:22 UTC (permalink / raw)
To: Bryan Wu; +Cc: David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
Bryan,
On Wed, 21 Mar 2007 18:08:08 +0800, Wu, Bryan wrote:
> As GPIO based blackfin driver will be replaced by I2C-GPIO generic
> driver, we just update this latest version of blackfin on-chip TWI I2C
> driver according to LKML review.
>
> [PATCH] Blackfin: on-chip Two Wire Interface I2C driver
>
> The i2c linux driver for blackfin architecture which supports blackfin
> on-chip TWI controller i2c operation.
>
> Signed-off-by: Bryan Wu <bryan.wu@analog.com>
> Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
> Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
> Reviewed-by: Jean Delvare <khali@linux-fr.org>
> Cc: David Brownell <david-b@pacbell.net>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
>
> drivers/i2c/busses/Kconfig | 16
> drivers/i2c/busses/Makefile | 1
> drivers/i2c/busses/i2c-bfin-twi.c | 653 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 670 insertions(+)
I overlooked a number of problems in the probe function, sorry.
> Index: linux-2.6/drivers/i2c/busses/Makefile
> ===================================================================
> --- linux-2.6.orig/drivers/i2c/busses/Makefile
> +++ linux-2.6/drivers/i2c/busses/Makefile
> @@ -10,6 +10,7 @@
> obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
> obj-$(CONFIG_I2C_AT91) += i2c-at91.o
> obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
> +obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
Please align with a tab, not spaces.
> obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
> obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
> obj-$(CONFIG_I2C_I801) += i2c-i801.o
> Index: linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
> (...)
> +static int i2c_bfin_twi_probe(struct platform_device *dev)
> +{
> + struct bfin_twi_iface *iface = &twi_iface;
> + struct i2c_adapter *p_adap;
> + int rc;
> +
> + mutex_init(&(iface->twi_lock));
> + spin_lock_init(&(iface->lock));
> + init_completion(&(iface->complete));
> + iface->irq = IRQ_TWI;
> +
> + 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_DRIVERID_BLACKFINTWI;
This isn't defined anywhere, and isn't valid anyway. I2C_DRIVERID_* are
for I2C chip drivers, not bus drivers.
> + strlcpy(p_adap->name, dev->name, I2C_NAME_SIZE);
I2C_NAME_SIZE changed recently, it no longer applies to
i2c_adapter.name. Please use sizeof(p_adap->name) instead.
> + p_adap->algo = &bfin_twi_algorithm;
> + p_adap->algo_data = iface;
> + p_adap->class = I2C_CLASS_ALL;
This pretty much voids the point of these probing classes. You should
only select the classes matching devices which may actually be probed
for on this bus. If different boards have different needs, get the
right classes from the platform data.
> + p_adap->dev.parent = &(dev->dev);
Note that parentheses are not needed.
> +
> + rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
> + SA_INTERRUPT, dev->name, iface);
> + if (rc) {
> + printk(KERN_ERR"i2c-bfin-twi: can't get IRQ %d !\n",
> + iface->irq);
Usually we leave a space between KERN_* and the beginning of the string
- I'm even surprised that it compiles without it. OTOH there is no
space before exclamation marks in English.
Oh, and it should be dev_err() anyway.
> + return -ENODEV;
> + }
> +
> + /* Set TWI internal clock as 10MHz */
> + bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
> +
> + /* Set Twi interface clock as specified */
> + if (CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 400)
> + bfin_write_TWI_CLKDIV((( 5*1024 / 400 ) << 8) |
> + (( 5*1024 / 400 ) & 0xFF));
This can never happen, as you have a range defined in Kconfig.
> + else
> + bfin_write_TWI_CLKDIV((( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
> + << 8) | (( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
> + & 0xFF));
> +
> + /* Enable TWI */
> + bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
> + SSYNC();
> +
> + rc = i2c_add_adapter(p_adap);
> + if (rc < 0)
> + free_irq(iface->irq, iface);
> + else
> + platform_set_drvdata(dev, iface);
> +
> + return rc;
> +}
> +static struct platform_driver i2c_bfin_twi_driver = {
> + .probe = i2c_bfin_twi_probe,
> + .remove = i2c_bfin_twi_remove,
> + .suspend = i2c_bfin_twi_suspend,
> + .resume = i2c_bfin_twi_resume,
> + .driver = {
> + .name = "i2c-bfin-twi",
> + },
> +};
Missing .owner = THIS_MODULE.
Thanks,
--
Jean Delvare
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-21 19:22 ` Jean Delvare
@ 2007-03-22 5:39 ` Mike Frysinger
2007-03-22 7:48 ` Jean Delvare
0 siblings, 1 reply; 14+ messages in thread
From: Mike Frysinger @ 2007-03-22 5:39 UTC (permalink / raw)
To: Jean Delvare
Cc: Bryan Wu, David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
On 3/21/07, Jean Delvare <khali@linux-fr.org> wrote:
> > + p_adap->class = I2C_CLASS_ALL;
>
> This pretty much voids the point of these probing classes. You should
> only select the classes matching devices which may actually be probed
> for on this bus. If different boards have different needs, get the
> right classes from the platform data.
i asked about the class issue previously specifically for this bus
driver and was told that they werent really fully defined ... the
on-chip I2C interface on the Blackfin chip can handle any I2C device
which is why i added this line
any examples of how to go about doing it via boards ? i dont see any
other I2C bus driver doing it that way ...
-mike
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-21 19:14 ` Mike Frysinger
@ 2007-03-22 7:03 ` Jean Delvare
2007-03-22 7:13 ` Mike Frysinger
0 siblings, 1 reply; 14+ messages in thread
From: Jean Delvare @ 2007-03-22 7:03 UTC (permalink / raw)
To: Mike Frysinger
Cc: Bob Copeland, bryan.wu, David Brownell, Andrew Morton,
Alexey Dobriyan, linux-kernel
On Wed, 21 Mar 2007 15:14:29 -0400, Mike Frysinger wrote:
> On 3/21/07, Bob Copeland <me@bobcopeland.com> wrote:
> > > +config I2C_BLACKFIN_TWI
> > > + tristate "Blackfin TWI I2C support"
> > > + depends on I2C && (BF534 || BF536 || BF537)
> > > + help
> > > + This is the TWI I2C device driver for Blackfin 534/536/537.
> > > + This driver can also be built as a module. If so, the module
> > > + will be called i2c-bfin-twi.
> >
> > Pardon my ignorance, but is there any reason to call it "Blackfin TWI"
> > as opposed to just "Blackfin" since TWI and I2C mean the same thing?
> > It lends itself to some redundancies such as:
> >
> > > + pr_info("I2C: Blackfin I2C TWI driver\n");
>
> it reflects the hardware manual and all public Analog Devices
> documentation ... the peripheral is always referred to as a "Two Wire
> Interface that is fully compatible with the I2C standard" since using
> the "I2C" term is not free
Damn, I hate it when legal idiocy takes the lead on technical clarity :(
--
Jean Delvare
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-22 7:03 ` Jean Delvare
@ 2007-03-22 7:13 ` Mike Frysinger
0 siblings, 0 replies; 14+ messages in thread
From: Mike Frysinger @ 2007-03-22 7:13 UTC (permalink / raw)
To: Jean Delvare
Cc: Bob Copeland, bryan.wu, David Brownell, Andrew Morton,
Alexey Dobriyan, linux-kernel
On 3/22/07, Jean Delvare <khali@linux-fr.org> wrote:
> On Wed, 21 Mar 2007 15:14:29 -0400, Mike Frysinger wrote:
> > On 3/21/07, Bob Copeland <me@bobcopeland.com> wrote:
> > > > +config I2C_BLACKFIN_TWI
> > > > + tristate "Blackfin TWI I2C support"
> > > > + depends on I2C && (BF534 || BF536 || BF537)
> > > > + help
> > > > + This is the TWI I2C device driver for Blackfin 534/536/537.
> > > > + This driver can also be built as a module. If so, the module
> > > > + will be called i2c-bfin-twi.
> > >
> > > Pardon my ignorance, but is there any reason to call it "Blackfin TWI"
> > > as opposed to just "Blackfin" since TWI and I2C mean the same thing?
> > > It lends itself to some redundancies such as:
> > >
> > > > + pr_info("I2C: Blackfin I2C TWI driver\n");
> >
> > it reflects the hardware manual and all public Analog Devices
> > documentation ... the peripheral is always referred to as a "Two Wire
> > Interface that is fully compatible with the I2C standard" since using
> > the "I2C" term is not free
>
> Damn, I hate it when legal idiocy takes the lead on technical clarity :(
i'm with you 100% but it isnt my call to make ;(
-mike
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-22 5:39 ` Mike Frysinger
@ 2007-03-22 7:48 ` Jean Delvare
2007-03-22 8:12 ` Mike Frysinger
0 siblings, 1 reply; 14+ messages in thread
From: Jean Delvare @ 2007-03-22 7:48 UTC (permalink / raw)
To: Mike Frysinger
Cc: Bryan Wu, David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
Hi Mike,
On Thu, 22 Mar 2007 01:39:28 -0400, Mike Frysinger wrote:
> On 3/21/07, Jean Delvare <khali@linux-fr.org> wrote:
> > > + p_adap->class = I2C_CLASS_ALL;
> >
> > This pretty much voids the point of these probing classes. You should
> > only select the classes matching devices which may actually be probed
> > for on this bus. If different boards have different needs, get the
> > right classes from the platform data.
>
> i asked about the class issue previously specifically for this bus
> driver and was told that they werent really fully defined ... the
> on-chip I2C interface on the Blackfin chip can handle any I2C device
> which is why i added this line
Grep for I2C_CLASS_ and you'll see that the classes are defined and
used consistently in the kernel tree. Maybe just not by the embedded
folks.
Also note that what matters is not the type of devices that can be
connected on the bus, but the type of devices that can be probed for.
This was essentially the same so far (general probing was the only way
to instantiate an i2c device), but the new i2c-core code that will go
into 2.6.22 will make it different. These classes will probably be less
used in the future.
> any examples of how to go about doing it via boards ? i dont see any
> other I2C bus driver doing it that way ...
I don't think many drivers from the embedded world do it properly. But
they don't appear to define any class at all, which means that in
practice they keep most regular i2c drivers out of their bus. Pretty
much the contrary of what you're doing with I2C_CLASS_ALL.
It's really up to you, but if you set the class to I2C_CLASS_ALL, don't
later come and complain that other drivers keep probing your I2C bus
and possibly attach to your I2C devices. You asked for it.
--
Jean Delvare
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-22 7:48 ` Jean Delvare
@ 2007-03-22 8:12 ` Mike Frysinger
2007-03-22 9:24 ` Jean Delvare
0 siblings, 1 reply; 14+ messages in thread
From: Mike Frysinger @ 2007-03-22 8:12 UTC (permalink / raw)
To: Jean Delvare
Cc: Bryan Wu, David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
On 3/22/07, Jean Delvare <khali@linux-fr.org> wrote:
> On Thu, 22 Mar 2007 01:39:28 -0400, Mike Frysinger wrote:
> > On 3/21/07, Jean Delvare <khali@linux-fr.org> wrote:
> > > > + p_adap->class = I2C_CLASS_ALL;
> > >
> > > This pretty much voids the point of these probing classes. You should
> > > only select the classes matching devices which may actually be probed
> > > for on this bus. If different boards have different needs, get the
> > > right classes from the platform data.
> >
> > i asked about the class issue previously specifically for this bus
> > driver and was told that they werent really fully defined ... the
> > on-chip I2C interface on the Blackfin chip can handle any I2C device
> > which is why i added this line
>
> Grep for I2C_CLASS_ and you'll see that the classes are defined and
> used consistently in the kernel tree. Maybe just not by the embedded
> folks.
>
> Also note that what matters is not the type of devices that can be
> connected on the bus, but the type of devices that can be probed for.
> This was essentially the same so far (general probing was the only way
> to instantiate an i2c device), but the new i2c-core code that will go
> into 2.6.22 will make it different. These classes will probably be less
> used in the future.
was referring to this:
http://lists.lm-sensors.org/pipermail/i2c/2007-February/000770.html
> > any examples of how to go about doing it via boards ? i dont see any
> > other I2C bus driver doing it that way ...
>
> I don't think many drivers from the embedded world do it properly.
which is why i asked if you had any hints ... if no one else is doing
it, it means no standard exists, which means there's nothing for me to
copy :)
it isnt obvious to me how to leverage the normal platform_device and
resource structures to achieve moving the class out of the driver and
into a boards file ... i'm perfectly happy doing this knowing what the
correct direction to take things ... but i guess this gets us back to
if no one is doing this yet and no one has any proposals, it's on our
shoulders to get it resolved satisfactorily
i might also argue (for fun) that some of the bus drivers arent doing
it correctly ... they should implement support for the specific I2C
implementation and then a different file for the specific
[mother]board would contain the specific details
> But
> they don't appear to define any class at all, which means that in
> practice they keep most regular i2c drivers out of their bus. Pretty
> much the contrary of what you're doing with I2C_CLASS_ALL.
this is certainly true, but i'd rather have the default behavior be
"allow any I2C device on the bus" than "only allow I2C devices that
dont check the bus type" ...
> It's really up to you, but if you set the class to I2C_CLASS_ALL, don't
> later come and complain that other drivers keep probing your I2C bus
> and possibly attach to your I2C devices. You asked for it.
i dont see why we would complain when it's obviously our fault ;)
it also wouldnt be a real issue as it's an embedded board, so it'd
only have the drivers enabled for devices that are actually hooked up
...
-mike
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-22 8:12 ` Mike Frysinger
@ 2007-03-22 9:24 ` Jean Delvare
2007-03-23 5:46 ` [PATCH -mm try#2] " Wu, Bryan
0 siblings, 1 reply; 14+ messages in thread
From: Jean Delvare @ 2007-03-22 9:24 UTC (permalink / raw)
To: Mike Frysinger
Cc: Bryan Wu, David Brownell, Andrew Morton, Alexey Dobriyan, linux-kernel
On Thu, 22 Mar 2007 04:12:37 -0400, Mike Frysinger wrote:
> it isnt obvious to me how to leverage the normal platform_device and
> resource structures to achieve moving the class out of the driver and
> into a boards file ... i'm perfectly happy doing this knowing what the
> correct direction to take things ... but i guess this gets us back to
> if no one is doing this yet and no one has any proposals, it's on our
> shoulders to get it resolved satisfactorily
I'd say: leave things as they are, if a problem arises, we'll see how
to fix it. By then, the new i2c driver model will be in mainline, which
will gives us a nicer way to address the problem.
> i might also argue (for fun) that some of the bus drivers arent doing
> it correctly ... they should implement support for the specific I2C
> implementation and then a different file for the specific
> [mother]board would contain the specific details
This sounds easy when you're in the embedded world. Now come and try to
support the literally thousands of different PC motherboard models out
there that way!
--
Jean Delvare
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH -mm try#2] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-22 9:24 ` Jean Delvare
@ 2007-03-23 5:46 ` Wu, Bryan
2007-03-23 7:27 ` Jean Delvare
0 siblings, 1 reply; 14+ messages in thread
From: Wu, Bryan @ 2007-03-23 5:46 UTC (permalink / raw)
To: Jean Delvare, Mike Frysinger, Bryan Wu, David Brownell,
Andrew Morton, Alexey Dobriyan, linux-kernel
Hi folks,
Changlogs:
a) Fixed issues according to Jean's review.
b) Add MAINTAINS infomation
c) add I2C_HW_B_BLACKFIN to i2c-id.h
[PATCH] Blackfin: on-chip Two Wire Interface I2C driver
The i2c linux driver for blackfin architecture which supports blackfin
on-chip TWI controller i2c operation.
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
Reviewed-by: Jean Delvare <khali@linux-fr.org>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
MAINTAINERS | 7
drivers/i2c/busses/Kconfig | 16
drivers/i2c/busses/Makefile | 1
drivers/i2c/busses/i2c-bfin-twi.c | 644 ++++++++++++++++++++++++++++++++++++++
include/linux/i2c-id.h | 1
5 files changed, 669 insertions(+)
Index: linux-2.6/drivers/i2c/busses/Kconfig
===================================================================
--- linux-2.6.orig/drivers/i2c/busses/Kconfig
+++ linux-2.6/drivers/i2c/busses/Kconfig
@@ -91,6 +91,22 @@
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
+config I2C_BLACKFIN_TWI
+ tristate "Blackfin TWI I2C support"
+ depends on I2C && (BF534 || BF536 || BF537)
+ help
+ This is the TWI I2C device driver for Blackfin 534/536/537.
+ This driver can also be built as a module. If so, the module
+ will be called i2c-bfin-twi.
+
+config I2C_BLACKFIN_TWI_CLK_KHZ
+ int "Blackfin TWI I2C clock (kHz)"
+ depends on I2C_BLACKFIN_TWI
+ range 10 400
+ default 50
+ help
+ The unit of the TWI clock is kilo HZ.
+
config I2C_ELEKTOR
tristate "Elektor ISA card"
depends on I2C && ISA && BROKEN_ON_SMP
Index: linux-2.6/drivers/i2c/busses/Makefile
===================================================================
--- linux-2.6.orig/drivers/i2c/busses/Makefile
+++ linux-2.6/drivers/i2c/busses/Makefile
@@ -10,6 +10,7 @@
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
+obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
Index: linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/i2c/busses/i2c-bfin-twi.c
@@ -0,0 +1,644 @@
+/*
+ * drivers/i2c/busses/i2c-bfin-twi.c
+ *
+ * Description: Driver for Blackfin Two Wire Interface
+ *
+ * Author: sonicz <sonic.zhang@analog.com>
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/blackfin.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
+
+struct bfin_twi_iface {
+ struct mutex twi_lock;
+ int irq;
+ spinlock_t lock;
+ char read_write;
+ u8 command;
+ u8 *transPtr;
+ int readNum;
+ int writeNum;
+ int cur_mode;
+ int manual_stop;
+ int result;
+ int timeout_count;
+ struct timer_list timeout_timer;
+ struct i2c_adapter adap;
+ struct completion complete;
+};
+
+static struct bfin_twi_iface twi_iface;
+
+static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
+{
+ unsigned short twi_int_status = bfin_read_TWI_INT_STAT();
+ unsigned short mast_stat = bfin_read_TWI_MASTER_STAT();
+
+ if (twi_int_status & XMTSERV) {
+ /* Transmit next data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ }
+ /* start receive immediately after complete sending in
+ * combine mode.
+ */
+ else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | MDIR | RSTART);
+ } else if (iface->manual_stop)
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | STOP);
+ SSYNC();
+ /* Clear status */
+ bfin_write_TWI_INT_STAT(XMTSERV);
+ SSYNC();
+ }
+ if (twi_int_status & RCVSERV) {
+ if (iface->readNum > 0) {
+ /* Receive next data */
+ *(iface->transPtr) = bfin_read_TWI_RCV_DATA8();
+ if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ /* Change combine mode into sub mode after
+ * read first data.
+ */
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ /* Get read number from first byte in block
+ * combine mode.
+ */
+ if (iface->readNum == 1 && iface->manual_stop)
+ iface->readNum = *iface->transPtr + 1;
+ }
+ iface->transPtr++;
+ iface->readNum--;
+ } else if (iface->manual_stop) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | STOP);
+ SSYNC();
+ }
+ /* Clear interrupt source */
+ bfin_write_TWI_INT_STAT(RCVSERV);
+ SSYNC();
+ }
+ if (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);
+ SSYNC();
+ iface->result = -1;
+ /* 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);
+ SSYNC();
+ /* If it is a quick transfer, only address bug no data,
+ * not an err, return 1.
+ */
+ if (iface->writeNum == 0 && (mast_stat & BUFRDERR))
+ iface->result = 1;
+ /* If address not acknowledged return -1,
+ * else return 0.
+ */
+ else if (!(mast_stat & ANAK))
+ iface->result = 0;
+ }
+ complete(&iface->complete);
+ return;
+ }
+ if (twi_int_status & MCOMP) {
+ bfin_write_TWI_INT_STAT(MCOMP);
+ SSYNC();
+ if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ if (iface->readNum == 0) {
+ /* set the read number to 1 and ask for manual
+ * stop in block combine mode
+ */
+ iface->readNum = 1;
+ iface->manual_stop = 1;
+ bfin_write_TWI_MASTER_CTL(
+ bfin_read_TWI_MASTER_CTL()
+ | (0xff << 6));
+ } else {
+ /* set the readd number in other
+ * combine mode.
+ */
+ bfin_write_TWI_MASTER_CTL(
+ (bfin_read_TWI_MASTER_CTL() &
+ (~(0xff << 6))) |
+ ( iface->readNum << 6));
+ }
+ /* remove restart bit and enable master receive */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() &
+ ~RSTART);
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
+ MEN | MDIR);
+ SSYNC();
+ } else {
+ iface->result = 1;
+ bfin_write_TWI_INT_MASK(0);
+ bfin_write_TWI_MASTER_CTL(0);
+ SSYNC();
+ complete(&iface->complete);
+ }
+ }
+}
+
+/* Interrupt handler */
+static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
+{
+ struct bfin_twi_iface *iface = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iface->lock, flags);
+ del_timer(&iface->timeout_timer);
+ bfin_twi_handle_interrupt(iface);
+ spin_unlock_irqrestore(&iface->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void bfin_twi_timeout(unsigned long data)
+{
+ struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iface->lock, flags);
+ bfin_twi_handle_interrupt(iface);
+ if (iface->result == 0) {
+ iface->timeout_count--;
+ if (iface->timeout_count > 0) {
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+ } else {
+ iface->result = -1;
+ complete(&iface->complete);
+ }
+ }
+ spin_unlock_irqrestore(&iface->lock, flags);
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int bfin_twi_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct bfin_twi_iface *iface = adap->algo_data;
+ struct i2c_msg *pmsg;
+ int i, ret;
+ int rc = 0;
+
+ if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ return -ENXIO;
+
+ mutex_lock(&iface->twi_lock);
+
+ while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
+ mutex_unlock(&iface->twi_lock);
+ yield();
+ mutex_lock(&iface->twi_lock);
+ }
+
+ 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_STANDARD;
+ iface->manual_stop = 0;
+ iface->transPtr = pmsg->buf;
+ iface->writeNum = iface->readNum = pmsg->len;
+ iface->result = 0;
+ iface->timeout_count = 10;
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(pmsg->addr);
+
+ /* FIFO Initiation. Data in FIFO should be
+ * discarded before start a new operation.
+ */
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+ SSYNC();
+
+ if (pmsg->flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
+ }
+ }
+
+ /* clear int stat */
+ bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+
+ /* Interrupt mask . Enable XMT, RCV interrupt */
+ bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ ((iface->read_write == I2C_SMBUS_READ)?
+ RCVSERV : XMTSERV));
+ SSYNC();
+
+ if (pmsg->len > 0 && pmsg->len <= 255)
+ bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
+ else if (pmsg->len > 255) {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ } else
+ break;
+
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
+ SSYNC();
+
+ wait_for_completion(&iface->complete);
+
+ rc = iface->result;
+ if (rc == 1)
+ ret++;
+ else if (rc == -1)
+ break;
+ }
+
+ /* Release mutex */
+ mutex_unlock(&iface->twi_lock);
+
+ return ret;
+}
+
+/*
+ * SMBus type transfer entrypoint
+ */
+
+int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ struct bfin_twi_iface *iface = adap->algo_data;
+ int rc = 0;
+
+ if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
+ return -ENXIO;
+
+ mutex_lock(&iface->twi_lock);
+
+ while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) {
+ mutex_unlock(&iface->twi_lock);
+ yield();
+ mutex_lock(&iface->twi_lock);
+ }
+
+ iface->writeNum = 0;
+ iface->readNum = 0;
+
+ /* Prepare datas & select mode */
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ iface->transPtr = NULL;
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ break;
+ case I2C_SMBUS_BYTE:
+ if (data == NULL)
+ iface->transPtr = NULL;
+ else {
+ if (read_write == I2C_SMBUS_READ)
+ iface->readNum = 1;
+ else
+ iface->writeNum = 1;
+ iface->transPtr = &data->byte;
+ }
+ iface->cur_mode = TWI_I2C_MODE_STANDARD;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 1;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = 1;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = &data->byte;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = (u8 *)&data->word;
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ iface->writeNum = 2;
+ iface->readNum = 2;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ iface->transPtr = (u8 *)&data->word;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = 0;
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = data->block[0] + 1;
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = data->block;
+ break;
+ default:
+ return -1;
+ }
+
+ iface->result = 0;
+ iface->manual_stop = 0;
+ iface->read_write = read_write;
+ iface->command = command;
+ iface->timeout_count = 10;
+
+ /* FIFO Initiation. Data in FIFO should be discarded before
+ * start a new operation.
+ */
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+
+ /* clear int stat */
+ bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(addr);
+ SSYNC();
+
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
+
+ switch (iface->cur_mode) {
+ case TWI_I2C_MODE_STANDARDSUB:
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ ((iface->read_write == I2C_SMBUS_READ) ?
+ RCVSERV : XMTSERV));
+ SSYNC();
+
+ if (iface->writeNum + 1 <= 255)
+ bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6);
+ else {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ }
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((CONFIG_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);
+ SSYNC();
+
+ if (iface->writeNum > 0)
+ bfin_write_TWI_MASTER_CTL((iface->writeNum + 1) << 6);
+ else
+ bfin_write_TWI_MASTER_CTL(0x1 << 6);
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
+ break;
+ default:
+ bfin_write_TWI_MASTER_CTL(0);
+ if (size != I2C_SMBUS_QUICK) {
+ /* Don't access xmit data register when this is a
+ * read operation.
+ */
+ if (iface->read_write != I2C_SMBUS_READ) {
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ if (iface->writeNum <= 255)
+ bfin_write_TWI_MASTER_CTL(iface->writeNum << 6);
+ else {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ }
+ iface->writeNum--;
+ } else {
+ bfin_write_TWI_XMT_DATA8(iface->command);
+ bfin_write_TWI_MASTER_CTL(1 << 6);
+ }
+ } else {
+ if (iface->readNum > 0 && iface->readNum <= 255)
+ bfin_write_TWI_MASTER_CTL(iface->readNum << 6);
+ else if (iface->readNum > 255) {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ } else {
+ del_timer(&iface->timeout_timer);
+ break;
+ }
+ }
+ }
+ bfin_write_TWI_INT_MASK(MCOMP | MERR |
+ ((iface->read_write == I2C_SMBUS_READ) ?
+ RCVSERV : XMTSERV));
+ SSYNC();
+
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
+ break;
+ }
+ SSYNC();
+
+ wait_for_completion(&iface->complete);
+
+ rc = (iface->result >= 0) ? 0 : -1;
+
+ /* Release mutex */
+ mutex_unlock(&iface->twi_lock);
+
+ return rc;
+}
+
+/*
+ * Return what the adapter supports
+ */
+static u32 bfin_twi_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_I2C;
+}
+
+
+static struct i2c_algorithm bfin_twi_algorithm = {
+ .master_xfer = bfin_twi_master_xfer,
+ .smbus_xfer = bfin_twi_smbus_xfer,
+ .functionality = bfin_twi_functionality,
+};
+
+
+static int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state)
+{
+/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/
+
+ /* Disable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() & ~TWI_ENA);
+ SSYNC();
+
+ return 0;
+}
+
+static int i2c_bfin_twi_resume(struct platform_device *dev)
+{
+/* struct bfin_twi_iface *iface = platform_get_drvdata(dev);*/
+
+ /* Enable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
+ SSYNC();
+
+ return 0;
+}
+
+static int i2c_bfin_twi_probe(struct platform_device *dev)
+{
+ struct bfin_twi_iface *iface = &twi_iface;
+ struct i2c_adapter *p_adap;
+ int rc;
+
+ mutex_init(&(iface->twi_lock));
+ spin_lock_init(&(iface->lock));
+ init_completion(&(iface->complete));
+ iface->irq = IRQ_TWI;
+
+ 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_B_BLACKFIN;
+ strlcpy(p_adap->name, dev->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;
+
+ rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
+ SA_INTERRUPT, dev->name, iface);
+ if (rc) {
+ dev_err(&(p_adap->dev), "i2c-bfin-twi: can't get IRQ %d !\n",
+ iface->irq);
+ return -ENODEV;
+ }
+
+ /* Set TWI internal clock as 10MHz */
+ bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
+
+ /* Set Twi interface clock as specified */
+ bfin_write_TWI_CLKDIV((( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
+ << 8) | (( 5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ )
+ & 0xFF));
+
+ /* Enable TWI */
+ bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA);
+ SSYNC();
+
+ rc = i2c_add_adapter(p_adap);
+ if (rc < 0)
+ free_irq(iface->irq, iface);
+ else
+ platform_set_drvdata(dev, iface);
+
+ return rc;
+}
+
+static int i2c_bfin_twi_remove(struct platform_device *pdev)
+{
+ struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ i2c_del_adapter(&(iface->adap));
+ free_irq(iface->irq, iface);
+
+ return 0;
+}
+
+static struct platform_driver i2c_bfin_twi_driver = {
+ .probe = i2c_bfin_twi_probe,
+ .remove = i2c_bfin_twi_remove,
+ .suspend = i2c_bfin_twi_suspend,
+ .resume = i2c_bfin_twi_resume,
+ .driver = {
+ .name = "i2c-bfin-twi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init i2c_bfin_twi_init(void)
+{
+ pr_info("I2C: Blackfin I2C TWI driver\n");
+
+ return platform_driver_register(&i2c_bfin_twi_driver);
+}
+
+static void __exit i2c_bfin_twi_exit(void)
+{
+ 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);
Index: linux-2.6/MAINTAINERS
===================================================================
--- linux-2.6.orig/MAINTAINERS
+++ linux-2.6/MAINTAINERS
@@ -731,6 +731,13 @@
W: http://blackfin.uclinux.org
S: Supported
+BLACKFIN I2C TWI DRIVER
+P: Sonic Zhang
+M: sonic.zhang@analog.com
+L: uclinux-dist-devel@blackfin.uclinux.org (subscribers-only)
+W: http://blackfin.uclinux.org
+S: Supported
+
BAYCOM/HDLCDRV DRIVERS FOR AX.25
P: Thomas Sailer
M: t.sailer@alumni.ethz.ch
Index: linux-2.6/include/linux/i2c-id.h
===================================================================
--- linux-2.6.orig/include/linux/i2c-id.h
+++ linux-2.6/include/linux/i2c-id.h
@@ -195,6 +195,7 @@
#define I2C_HW_B_EM28XX 0x01001f /* em28xx video capture cards */
#define I2C_HW_B_CX2341X 0x010020 /* Conexant CX2341X MPEG encoder cards */
#define I2C_HW_B_INTELFB 0x010021 /* intel framebuffer driver */
+#define I2C_HW_B_BLACKFIN 0x010022 /* ADI Blackfin I2C TWI driver */
/* --- PCF 8584 based algorithms */
#define I2C_HW_P_LP 0x020000 /* Parallel port interface */
_
Thanks
-Bryan
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm try#2] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-23 5:46 ` [PATCH -mm try#2] " Wu, Bryan
@ 2007-03-23 7:27 ` Jean Delvare
2007-03-23 7:36 ` Wu, Bryan
0 siblings, 1 reply; 14+ messages in thread
From: Jean Delvare @ 2007-03-23 7:27 UTC (permalink / raw)
To: Bryan Wu
Cc: Mike Frysinger, David Brownell, Andrew Morton, Alexey Dobriyan,
linux-kernel
Hi Bryan,
On Fri, 23 Mar 2007 13:46:57 +0800, Wu, Bryan wrote:
> Changlogs:
>
> a) Fixed issues according to Jean's review.
> b) Add MAINTAINS infomation
> c) add I2C_HW_B_BLACKFIN to i2c-id.h
I2C_HW_B_* is traditionally used for drivers built on top of the
i2c-algo-bit driver, which isn't the case of your driver, so it's a bit
confusing. Please instead use:
#define I2C_HW_BLACKFIN 0x190001
I hope we'll be able to get rid of these IDs soon, so we no longer have
to care about this mess.
Other than that I'm OK with the patch this time, I'll push it on my
stack. I'll fix the ID issue myself, no need to resend. This also means
that, from now on, any change to this driver should be provided as an
incremental patch on top of this version.
Thanks,
--
Jean Delvare
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH -mm try#2] Blackfin: on-chip Two Wire Interface I2C driver
2007-03-23 7:27 ` Jean Delvare
@ 2007-03-23 7:36 ` Wu, Bryan
0 siblings, 0 replies; 14+ messages in thread
From: Wu, Bryan @ 2007-03-23 7:36 UTC (permalink / raw)
To: Jean Delvare
Cc: Bryan Wu, Mike Frysinger, David Brownell, Andrew Morton,
Alexey Dobriyan, linux-kernel
On Fri, 2007-03-23 at 08:27 +0100, Jean Delvare wrote:
> Hi Bryan,
>
> On Fri, 23 Mar 2007 13:46:57 +0800, Wu, Bryan wrote:
> > Changlogs:
> >
> > a) Fixed issues according to Jean's review.
> > b) Add MAINTAINS infomation
> > c) add I2C_HW_B_BLACKFIN to i2c-id.h
>
> I2C_HW_B_* is traditionally used for drivers built on top of the
> i2c-algo-bit driver, which isn't the case of your driver, so it's a bit
> confusing. Please instead use:
>
> #define I2C_HW_BLACKFIN 0x190001
>
> I hope we'll be able to get rid of these IDs soon, so we no longer have
> to care about this mess.
Thanks, I appreciate. When you want to get rid of these IDs, I will do
the removing ID job related to this patch.
>
> Other than that I'm OK with the patch this time, I'll push it on my
> stack. I'll fix the ID issue myself, no need to resend. This also means
> that, from now on, any change to this driver should be provided as an
> incremental patch on top of this version.
>
> Thanks,
OK, I will follow this rule definitely.
Thanks again
-Bryan Wu
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2007-03-23 7:36 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-21 10:08 [PATCH -mm 4/4] Blackfin: on-chip Two Wire Interface I2C driver Wu, Bryan
2007-03-21 17:56 ` Jean Delvare
2007-03-21 18:17 ` Bob Copeland
2007-03-21 19:14 ` Mike Frysinger
2007-03-22 7:03 ` Jean Delvare
2007-03-22 7:13 ` Mike Frysinger
2007-03-21 19:22 ` Jean Delvare
2007-03-22 5:39 ` Mike Frysinger
2007-03-22 7:48 ` Jean Delvare
2007-03-22 8:12 ` Mike Frysinger
2007-03-22 9:24 ` Jean Delvare
2007-03-23 5:46 ` [PATCH -mm try#2] " Wu, Bryan
2007-03-23 7:27 ` Jean Delvare
2007-03-23 7:36 ` Wu, Bryan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).