All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] I2C: SiByte: Convert the driver to make use of interrupts
@ 2011-08-18 23:43 ` Matt Turner
  0 siblings, 0 replies; 56+ messages in thread
From: Matt Turner @ 2011-08-18 23:43 UTC (permalink / raw)
  To: Jean Delvare
  Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Ralf Baechle, Guenter Roeck,
	Maciej W. Rozycki, Matt Turner

From: Maciej W. Rozycki <macro-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>

This is a rewrite of large parts of the driver mainly so that it uses
SMBus interrupts to offload the CPU from busy-waiting on status inputs.
As a part of the overhaul of the init and exit calls, all accesses to the
hardware got converted to use accessory functions via an ioremap() cookie.

[mattst88] Added BCM1480 interrupts and rebased minimally.

Signed-off-by: Matt Turner <mattst88-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Maciej W. Rozycki <macro-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>
---
This is the second version of this patch that I've sent. This version
fixes the problem with the ENXIO return.

 drivers/i2c/busses/i2c-sibyte.c |  296 +++++++++++++++++++++++++++++---------
 1 files changed, 226 insertions(+), 70 deletions(-)

diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index 0fe505d..d2f1cf1 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -2,6 +2,7 @@
  * Copyright (C) 2004 Steven J. Hill
  * Copyright (C) 2001,2002,2003 Broadcom Corporation
  * Copyright (C) 1995-2000 Simon G. Vogl
+ * Copyright (C) 2008 Maciej W. Rozycki
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,104 +19,164 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
+#include <linux/param.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 #include <linux/io.h>
+#include <asm/sibyte/sb1250_int.h>
 #include <asm/sibyte/sb1250_regs.h>
 #include <asm/sibyte/sb1250_smbus.h>
+#include <asm/sibyte/bcm1480_int.h>
 
 
 struct i2c_algo_sibyte_data {
-	void *data;		/* private data */
-	int   bus;		/* which bus */
-	void *reg_base;		/* CSR base */
+	wait_queue_head_t	wait;		/* IRQ queue */
+	void __iomem		*csr;		/* mapped CSR handle */
+	phys_t			base;		/* physical CSR base */
+	char			*name;		/* IRQ handler name */
+	spinlock_t		lock;		/* atomiser */
+	int			irq;		/* IRQ line */
+	int			status;		/* IRQ status */
 };
 
-/* ----- global defines ----------------------------------------------- */
-#define SMB_CSR(a,r) ((long)(a->reg_base + r))
 
+static irqreturn_t i2c_sibyte_interrupt(int irq, void *dev_id)
+{
+	struct i2c_adapter *i2c_adap = dev_id;
+	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+	u8 status;
+
+	/*
+	 * Ugh, no way to detect the finish interrupt,
+	 * but if busy it is obviously not one.
+	 */
+	status = __raw_readq(csr + R_SMB_STATUS);
+	if ((status & (M_SMB_ERROR | M_SMB_BUSY)) == M_SMB_BUSY)
+		return IRQ_NONE;
+
+	/*
+	 * Clear the error interrupt (write 1 to clear);
+	 * the finish interrupt was cleared by the read above.
+	 */
+	__raw_writeq(status, csr + R_SMB_STATUS);
+
+	/* Post the status. */
+	spin_lock(&adap->lock);
+	adap->status = status & (M_SMB_ERROR_TYPE | M_SMB_ERROR | M_SMB_BUSY);
+	wake_up(&adap->wait);
+	spin_unlock(&adap->lock);
+
+	return IRQ_HANDLED;
+}
 
-static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
-		      unsigned short flags, char read_write,
-		      u8 command, int size, union i2c_smbus_data * data)
+static s32 i2c_sibyte_smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
+				 unsigned short cflags,
+				 char read_write, u8 command, int size,
+				 union i2c_smbus_data *data)
 {
 	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+	unsigned long flags;
 	int data_bytes = 0;
 	int error;
 
-	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
-		;
+	spin_lock_irqsave(&adap->lock, flags);
+
+	if (adap->status < 0) {
+		error = -EIO;
+		goto out_unlock;
+	}
 
 	switch (size) {
 	case I2C_SMBUS_QUICK:
-		csr_out32((V_SMB_ADDR(addr) |
-			   (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
-			   V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
+		__raw_writeq(V_SMB_ADDR(addr) |
+			     (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
+			     V_SMB_TT_QUICKCMD,
+			     csr + R_SMB_START);
 		break;
 	case I2C_SMBUS_BYTE:
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 1;
 		} else {
-			csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	case I2C_SMBUS_BYTE_DATA:
-		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+		__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 1;
 		} else {
-			csr_out32(V_SMB_LB(data->byte),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_LB(data->byte), csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	case I2C_SMBUS_WORD_DATA:
-		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+		__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 2;
 		} else {
-			csr_out32(V_SMB_LB(data->word & 0xff),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32(V_SMB_MB(data->word >> 8),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_LB(data->word & 0xff),
+				     csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_MB(data->word >> 8),
+				     csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	default:
-		return -EOPNOTSUPP;
+		error = -EOPNOTSUPP;
+		goto out_unlock;
 	}
+	mmiowb();
+	__raw_readq(csr + R_SMB_START);
+	adap->status = -1;
+
+	spin_unlock_irqrestore(&adap->lock, flags);
 
-	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
-		;
+	wait_event_timeout(adap->wait, (adap->status >= 0), HZ);
 
-	error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
-	if (error & M_SMB_ERROR) {
-		/* Clear error bit by writing a 1 */
-		csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
-		return (error & M_SMB_ERROR_TYPE) ? -EIO : -ENXIO;
+	spin_lock_irqsave(&adap->lock, flags);
+
+	if (adap->status > 0 && ((adap->status & (M_SMB_ERROR | M_SMB_ERROR_TYPE)) == M_SMB_ERROR)) {
+		error = -ENXIO;
+		goto out_unlock;
+	}
+	if (adap->status < 0 || (adap->status & (M_SMB_ERROR | M_SMB_BUSY))) {
+		error = -EIO;
+		goto out_unlock;
 	}
 
 	if (data_bytes == 1)
-		data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
+		data->byte = __raw_readq(csr + R_SMB_DATA) & 0xff;
 	if (data_bytes == 2)
-		data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
+		data->word = __raw_readq(csr + R_SMB_DATA) & 0xffff;
 
-	return 0;
+	error = 0;
+
+out_unlock:
+	spin_unlock_irqrestore(&adap->lock, flags);
+
+	return error;
 }
 
-static u32 bit_func(struct i2c_adapter *adap)
+static u32 i2c_sibyte_bit_func(struct i2c_adapter *adap)
 {
 	return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
 		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
@@ -125,8 +186,8 @@ static u32 bit_func(struct i2c_adapter *adap)
 /* -----exported algorithm data: -------------------------------------	*/
 
 static const struct i2c_algorithm i2c_sibyte_algo = {
-	.smbus_xfer	= smbus_xfer,
-	.functionality	= bit_func,
+	.smbus_xfer	= i2c_sibyte_smbus_xfer,
+	.functionality	= i2c_sibyte_bit_func,
 };
 
 /*
@@ -135,37 +196,121 @@ static const struct i2c_algorithm i2c_sibyte_algo = {
 static int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
 {
 	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr;
+	int err;
 
-	/* Register new adapter to i2c module... */
-	i2c_adap->algo = &i2c_sibyte_algo;
+	adap->status = 0;
+	init_waitqueue_head(&adap->wait);
+	spin_lock_init(&adap->lock);
+
+	csr = ioremap(adap->base, R_SMB_PEC + SMB_REGISTER_SPACING);
+	if (!csr) {
+		err = -ENOMEM;
+		goto out;
+	}
+	adap->csr = csr;
 
 	/* Set the requested frequency. */
-	csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
-	csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
+	__raw_writeq(speed, csr + R_SMB_FREQ);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	err = request_irq(adap->irq, i2c_sibyte_interrupt, IRQF_SHARED,
+			  adap->name, i2c_adap);
+	if (err < 0)
+		goto out_unmap;
+
+	/* Enable finish and error interrupts. */
+	__raw_writeq(M_SMB_FINISH_INTR | M_SMB_ERR_INTR, csr + R_SMB_CONTROL);
+
+	/* Register new adapter to i2c module... */
+	err = i2c_add_numbered_adapter(i2c_adap);
+	if (err < 0)
+		goto out_unirq;
+
+	return 0;
 
-	return i2c_add_numbered_adapter(i2c_adap);
+out_unirq:
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	free_irq(adap->irq, i2c_adap);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+out_unmap:
+	iounmap(csr);
+out:
+	return err;
 }
 
+static void i2c_sibyte_remove_bus(struct i2c_adapter *i2c_adap)
+{
+	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+
+	i2c_del_adapter(i2c_adap);
+
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
 
-static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
-	{ NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
-	{ NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
+	free_irq(adap->irq, i2c_adap);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+
+	iounmap(csr);
+}
+
+static struct i2c_algo_sibyte_data i2c_sibyte_board_data[2] = {
+#ifdef CONFIG_SIBYTE_SB1250
+	{
+		.name	= "sb1250-smbus-0",
+		.base	= A_SMB_0,
+		.irq	= K_INT_SMB_0,
+	},
+	{
+		.name	= "sb1250-smbus-1",
+		.base	= A_SMB_1,
+		.irq	= K_INT_SMB_1,
+	}
+#else
+	{
+		.name	= "bcm1480-smbus-0",
+		.base	= A_SMB_0,
+		.irq	= K_BCM1480_INT_SMB_0,
+	},
+	{
+		.name	= "bcm1480-smbus-1",
+		.base	= A_SMB_1,
+		.irq	= K_BCM1480_INT_SMB_1,
+	}
+#endif
 };
 
-static struct i2c_adapter sibyte_board_adapter[2] = {
+static struct i2c_adapter i2c_sibyte_board_adapter[2] = {
 	{
 		.owner		= THIS_MODULE,
 		.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
-		.algo		= NULL,
-		.algo_data	= &sibyte_board_data[0],
+		.algo		= &i2c_sibyte_algo,
+		.algo_data	= &i2c_sibyte_board_data[0],
 		.nr		= 0,
 		.name		= "SiByte SMBus 0",
 	},
 	{
 		.owner		= THIS_MODULE,
 		.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
-		.algo		= NULL,
-		.algo_data	= &sibyte_board_data[1],
+		.algo		= &i2c_sibyte_algo,
+		.algo_data	= &i2c_sibyte_board_data[1],
 		.nr		= 1,
 		.name		= "SiByte SMBus 1",
 	},
@@ -173,21 +318,32 @@ static struct i2c_adapter sibyte_board_adapter[2] = {
 
 static int __init i2c_sibyte_init(void)
 {
+	int err;
+
 	pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n");
-	if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0)
-		return -ENODEV;
-	if (i2c_sibyte_add_bus(&sibyte_board_adapter[1],
-			       K_SMB_FREQ_400KHZ) < 0) {
-		i2c_del_adapter(&sibyte_board_adapter[0]);
-		return -ENODEV;
-	}
+
+	err = i2c_sibyte_add_bus(&i2c_sibyte_board_adapter[0],
+				 K_SMB_FREQ_100KHZ);
+	if (err < 0)
+		goto out;
+
+	err = i2c_sibyte_add_bus(&i2c_sibyte_board_adapter[1],
+				 K_SMB_FREQ_400KHZ);
+	if (err < 0)
+		goto out_remove;
+
 	return 0;
+
+out_remove:
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[0]);
+out:
+	return err;
 }
 
 static void __exit i2c_sibyte_exit(void)
 {
-	i2c_del_adapter(&sibyte_board_adapter[0]);
-	i2c_del_adapter(&sibyte_board_adapter[1]);
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[1]);
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[0]);
 }
 
 module_init(i2c_sibyte_init);
-- 
1.7.3.4

^ permalink raw reply related	[flat|nested] 56+ messages in thread
* [PATCH] I2C: SiByte: Convert the driver to make use of interrupts
@ 2010-12-06  6:38 Matt Turner
       [not found] ` <1291617494-18430-1-git-send-email-mattst88-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  0 siblings, 1 reply; 56+ messages in thread
From: Matt Turner @ 2010-12-06  6:38 UTC (permalink / raw)
  To: Jean Delvare
  Cc: linux-i2c, linux-mips, Ralf Baechle, Maciej W. Rozycki, Matt Turner

From: Maciej W. Rozycki <macro@linux-mips.org>

This is a rewrite of large parts of the driver mainly so that it uses
SMBus interrupts to offload the CPU from busy-waiting on status inputs.
As a part of the overhaul of the init and exit calls, all accesses to the
hardware got converted to use accessory functions via an ioremap() cookie.

Minimally rebased by Matt Turner.

Tested-by: Matt Turner <mattst88@gmail.com>
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
This patch was originally sent in May 2008 [1], but appears to have been lost.

I believe this patch depends on
[PATCH 1/3] RTC: SMBus support for the M41T80, etc. driver

Please review the change in return values (search for ENXIO). I wasn't entirely
sure how this code should look. (The code the original patch was against just
returned -1 on error. See 102b59c6d6d30fb6560177fd1ae8a34c4c163897). Please
apply if acceptable.

[1] http://lists.lm-sensors.org/pipermail/i2c/2008-May/003638.html

 drivers/i2c/busses/i2c-sibyte.c |  278 +++++++++++++++++++++++++++++----------
 1 files changed, 208 insertions(+), 70 deletions(-)

diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index 0fe505d..283747c 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -2,6 +2,7 @@
  * Copyright (C) 2004 Steven J. Hill
  * Copyright (C) 2001,2002,2003 Broadcom Corporation
  * Copyright (C) 1995-2000 Simon G. Vogl
+ * Copyright (C) 2008 Maciej W. Rozycki
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,104 +19,159 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
+#include <linux/param.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 #include <linux/io.h>
+#include <asm/sibyte/sb1250_int.h>
 #include <asm/sibyte/sb1250_regs.h>
 #include <asm/sibyte/sb1250_smbus.h>
 
 
 struct i2c_algo_sibyte_data {
-	void *data;		/* private data */
-	int   bus;		/* which bus */
-	void *reg_base;		/* CSR base */
+	wait_queue_head_t	wait;		/* IRQ queue */
+	void __iomem		*csr;		/* mapped CSR handle */
+	phys_t			base;		/* physical CSR base */
+	char			*name;		/* IRQ handler name */
+	spinlock_t		lock;		/* atomiser */
+	int			irq;		/* IRQ line */
+	int			status;		/* IRQ status */
 };
 
-/* ----- global defines ----------------------------------------------- */
-#define SMB_CSR(a,r) ((long)(a->reg_base + r))
 
+static irqreturn_t i2c_sibyte_interrupt(int irq, void *dev_id)
+{
+	struct i2c_adapter *i2c_adap = dev_id;
+	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+	u8 status;
+
+	/*
+	 * Ugh, no way to detect the finish interrupt,
+	 * but if busy it is obviously not one.
+	 */
+	status = __raw_readq(csr + R_SMB_STATUS);
+	if ((status & (M_SMB_ERROR | M_SMB_BUSY)) == M_SMB_BUSY)
+		return IRQ_NONE;
 
-static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
-		      unsigned short flags, char read_write,
-		      u8 command, int size, union i2c_smbus_data * data)
+	/*
+	 * Clear the error interrupt (write 1 to clear);
+	 * the finish interrupt was cleared by the read above.
+	 */
+	__raw_writeq(status, csr + R_SMB_STATUS);
+
+	/* Post the status. */
+	spin_lock_irq(&adap->lock);
+	adap->status = status & (M_SMB_ERROR_TYPE | M_SMB_ERROR | M_SMB_BUSY);
+	wake_up(&adap->wait);
+	spin_unlock_irq(&adap->lock);
+
+	return IRQ_HANDLED;
+}
+
+static s32 i2c_sibyte_smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
+				 unsigned short cflags,
+				 char read_write, u8 command, int size,
+				 union i2c_smbus_data *data)
 {
 	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+	unsigned long flags;
 	int data_bytes = 0;
 	int error;
 
-	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
-		;
+	spin_lock_irqsave(&adap->lock, flags);
+
+	if (adap->status < 0) {
+		error = -EIO;
+		goto out_unlock;
+	}
 
 	switch (size) {
 	case I2C_SMBUS_QUICK:
-		csr_out32((V_SMB_ADDR(addr) |
-			   (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
-			   V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
+		__raw_writeq(V_SMB_ADDR(addr) |
+			     (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
+			     V_SMB_TT_QUICKCMD,
+			     csr + R_SMB_START);
 		break;
 	case I2C_SMBUS_BYTE:
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 1;
 		} else {
-			csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	case I2C_SMBUS_BYTE_DATA:
-		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+		__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 1;
 		} else {
-			csr_out32(V_SMB_LB(data->byte),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_LB(data->byte), csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	case I2C_SMBUS_WORD_DATA:
-		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+		__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 2;
 		} else {
-			csr_out32(V_SMB_LB(data->word & 0xff),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32(V_SMB_MB(data->word >> 8),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_LB(data->word & 0xff),
+				     csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_MB(data->word >> 8),
+				     csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	default:
-		return -EOPNOTSUPP;
+		error = -EOPNOTSUPP;
+		goto out_unlock;
 	}
+	mmiowb();
+	__raw_readq(csr + R_SMB_START);
+	adap->status = -1;
+
+	spin_unlock_irqrestore(&adap->lock, flags);
+
+	wait_event_timeout(adap->wait, (adap->status >= 0), HZ);
 
-	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
-		;
+	spin_lock_irqsave(&adap->lock, flags);
 
-	error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
-	if (error & M_SMB_ERROR) {
-		/* Clear error bit by writing a 1 */
-		csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
-		return (error & M_SMB_ERROR_TYPE) ? -EIO : -ENXIO;
+	if (adap->status < 0 || (adap->status & (M_SMB_ERROR | M_SMB_BUSY))) {
+		error = -EIO;
+		goto out_unlock;
 	}
 
 	if (data_bytes == 1)
-		data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
+		data->byte = __raw_readq(csr + R_SMB_DATA) & 0xff;
 	if (data_bytes == 2)
-		data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
+		data->word = __raw_readq(csr + R_SMB_DATA) & 0xffff;
 
-	return 0;
+	error = 0;
+
+out_unlock:
+	spin_unlock_irqrestore(&adap->lock, flags);
+
+	return error;
 }
 
-static u32 bit_func(struct i2c_adapter *adap)
+static u32 i2c_sibyte_bit_func(struct i2c_adapter *adap)
 {
 	return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
 		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
@@ -125,8 +181,8 @@ static u32 bit_func(struct i2c_adapter *adap)
 /* -----exported algorithm data: -------------------------------------	*/
 
 static const struct i2c_algorithm i2c_sibyte_algo = {
-	.smbus_xfer	= smbus_xfer,
-	.functionality	= bit_func,
+	.smbus_xfer	= i2c_sibyte_smbus_xfer,
+	.functionality	= i2c_sibyte_bit_func,
 };
 
 /*
@@ -135,37 +191,108 @@ static const struct i2c_algorithm i2c_sibyte_algo = {
 static int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
 {
 	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr;
+	int err;
 
-	/* Register new adapter to i2c module... */
-	i2c_adap->algo = &i2c_sibyte_algo;
+	adap->status = 0;
+	init_waitqueue_head(&adap->wait);
+	spin_lock_init(&adap->lock);
+
+	csr = ioremap(adap->base, R_SMB_PEC + SMB_REGISTER_SPACING);
+	if (!csr) {
+		err = -ENOMEM;
+		goto out;
+	}
+	adap->csr = csr;
 
 	/* Set the requested frequency. */
-	csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
-	csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
+	__raw_writeq(speed, csr + R_SMB_FREQ);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	err = request_irq(adap->irq, i2c_sibyte_interrupt, IRQF_SHARED,
+			  adap->name, i2c_adap);
+	if (err < 0)
+		goto out_unmap;
+
+	/* Enable finish and error interrupts. */
+	__raw_writeq(M_SMB_FINISH_INTR | M_SMB_ERR_INTR, csr + R_SMB_CONTROL);
+
+	/* Register new adapter to i2c module... */
+	err = i2c_add_numbered_adapter(i2c_adap);
+	if (err < 0)
+		goto out_unirq;
 
-	return i2c_add_numbered_adapter(i2c_adap);
+	return 0;
+
+out_unirq:
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	free_irq(adap->irq, i2c_adap);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+out_unmap:
+	iounmap(csr);
+out:
+	return err;
 }
 
+static void i2c_sibyte_remove_bus(struct i2c_adapter *i2c_adap)
+{
+	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+
+	i2c_del_adapter(i2c_adap);
+
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	free_irq(adap->irq, i2c_adap);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+
+	iounmap(csr);
+}
 
-static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
-	{ NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
-	{ NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
+static struct i2c_algo_sibyte_data i2c_sibyte_board_data[2] = {
+	{
+		.name	= "sb1250-smbus-0",
+		.base	= A_SMB_0,
+		.irq	= K_INT_SMB_0,
+	},
+	{
+		.name	= "sb1250-smbus-1",
+		.base	= A_SMB_1,
+		.irq	= K_INT_SMB_1,
+	}
 };
 
-static struct i2c_adapter sibyte_board_adapter[2] = {
+static struct i2c_adapter i2c_sibyte_board_adapter[2] = {
 	{
 		.owner		= THIS_MODULE,
 		.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
-		.algo		= NULL,
-		.algo_data	= &sibyte_board_data[0],
+		.algo		= &i2c_sibyte_algo,
+		.algo_data	= &i2c_sibyte_board_data[0],
 		.nr		= 0,
 		.name		= "SiByte SMBus 0",
 	},
 	{
 		.owner		= THIS_MODULE,
 		.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
-		.algo		= NULL,
-		.algo_data	= &sibyte_board_data[1],
+		.algo		= &i2c_sibyte_algo,
+		.algo_data	= &i2c_sibyte_board_data[1],
 		.nr		= 1,
 		.name		= "SiByte SMBus 1",
 	},
@@ -173,21 +300,32 @@ static struct i2c_adapter sibyte_board_adapter[2] = {
 
 static int __init i2c_sibyte_init(void)
 {
+	int err;
+
 	pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n");
-	if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0)
-		return -ENODEV;
-	if (i2c_sibyte_add_bus(&sibyte_board_adapter[1],
-			       K_SMB_FREQ_400KHZ) < 0) {
-		i2c_del_adapter(&sibyte_board_adapter[0]);
-		return -ENODEV;
-	}
+
+	err = i2c_sibyte_add_bus(&i2c_sibyte_board_adapter[0],
+				 K_SMB_FREQ_100KHZ);
+	if (err < 0)
+		goto out;
+
+	err = i2c_sibyte_add_bus(&i2c_sibyte_board_adapter[1],
+				 K_SMB_FREQ_400KHZ);
+	if (err < 0)
+		goto out_remove;
+
 	return 0;
+
+out_remove:
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[0]);
+out:
+	return err;
 }
 
 static void __exit i2c_sibyte_exit(void)
 {
-	i2c_del_adapter(&sibyte_board_adapter[0]);
-	i2c_del_adapter(&sibyte_board_adapter[1]);
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[1]);
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[0]);
 }
 
 module_init(i2c_sibyte_init);
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 56+ messages in thread
* [PATCH] I2C: SiByte: Convert the driver to make use of interrupts
@ 2008-05-13  3:28 Maciej W. Rozycki
  0 siblings, 0 replies; 56+ messages in thread
From: Maciej W. Rozycki @ 2008-05-13  3:28 UTC (permalink / raw)
  To: Jean Delvare, Andrew Morton; +Cc: i2c, linux-mips, linux-kernel

 This is a rewrite of large parts of the driver mainly so that it uses 
SMBus interrupts to offload the CPU from busy-waiting on status inputs.  
As a part of the overhaul of the init and exit calls, all accesses to the 
hardware got converted to use accessory functions via an ioremap() cookie.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
---
 This patch depends on patch-2.6.26-rc1-20080505-swarm-i2c-16 -- submitted 
as a part of the recent set of M41T80 RTC changes.

 Jean, please let me know if you prefer the ioremap() changes separately.  
Similarly, how about the -EINVAL/-EIO error codes?  Also please note how I
hesitated from adding second space after final full stops within comments.

  Maciej

patch-2.6.26-rc1-20080505-sibyte-i2c-irq-5
diff -up --recursive --new-file linux-2.6.26-rc1-20080505.macro/drivers/i2c/busses/i2c-sibyte.c linux-2.6.26-rc1-20080505/drivers/i2c/busses/i2c-sibyte.c
--- linux-2.6.26-rc1-20080505.macro/drivers/i2c/busses/i2c-sibyte.c	2008-05-11 02:25:56.000000000 +0000
+++ linux-2.6.26-rc1-20080505/drivers/i2c/busses/i2c-sibyte.c	2008-05-12 06:00:16.000000000 +0000
@@ -2,6 +2,7 @@
  * Copyright (C) 2004 Steven J. Hill
  * Copyright (C) 2001,2002,2003 Broadcom Corporation
  * Copyright (C) 1995-2000 Simon G. Vogl
+ * Copyright (C) 2008 Maciej W. Rozycki
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,104 +19,159 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
+#include <linux/param.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 #include <asm/io.h>
+#include <asm/sibyte/sb1250_int.h>
 #include <asm/sibyte/sb1250_regs.h>
 #include <asm/sibyte/sb1250_smbus.h>
 
 
 struct i2c_algo_sibyte_data {
-	void *data;		/* private data */
-	int   bus;		/* which bus */
-	void *reg_base;		/* CSR base */
+	wait_queue_head_t	wait;		/* IRQ queue */
+	void __iomem		*csr;		/* mapped CSR handle */
+	phys_t			base;		/* physical CSR base */
+	char			*name;		/* IRQ handler name */
+	spinlock_t		lock;		/* atomiser */
+	int			irq;		/* IRQ line */
+	int			status;		/* IRQ status */
 };
 
-/* ----- global defines ----------------------------------------------- */
-#define SMB_CSR(a,r) ((long)(a->reg_base + r))
 
+static irqreturn_t i2c_sibyte_interrupt(int irq, void *dev_id)
+{
+	struct i2c_adapter *i2c_adap = dev_id;
+	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+	u8 status;
+
+	/*
+	 * Ugh, no way to detect the finish interrupt,
+	 * but if busy it is obviously not one.
+	 */
+	status = __raw_readq(csr + R_SMB_STATUS);
+	if ((status & (M_SMB_ERROR | M_SMB_BUSY)) == M_SMB_BUSY)
+		return IRQ_NONE;
+
+	/*
+	 * Clear the error interrupt (write 1 to clear);
+	 * the finish interrupt was cleared by the read above.
+	 */
+	__raw_writeq(status, csr + R_SMB_STATUS);
+
+	/* Post the status. */
+	spin_lock_irq(&adap->lock);
+	adap->status = status & (M_SMB_ERROR_TYPE | M_SMB_ERROR | M_SMB_BUSY);
+	wake_up(&adap->wait);
+	spin_unlock_irq(&adap->lock);
+
+	return IRQ_HANDLED;
+}
 
-static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
-		      unsigned short flags, char read_write,
-		      u8 command, int size, union i2c_smbus_data * data)
+static s32 i2c_sibyte_smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
+				 unsigned short cflags,
+				 char read_write, u8 command, int size,
+				 union i2c_smbus_data *data)
 {
 	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+	unsigned long flags;
 	int data_bytes = 0;
 	int error;
 
-	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
-		;
+	spin_lock_irqsave(&adap->lock, flags);
+
+	if (adap->status < 0) {
+		error = -EIO;
+		goto out_unlock;
+	}
 
 	switch (size) {
 	case I2C_SMBUS_QUICK:
-		csr_out32((V_SMB_ADDR(addr) |
-			   (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
-			   V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
+		__raw_writeq(V_SMB_ADDR(addr) |
+			     (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
+			     V_SMB_TT_QUICKCMD,
+			     csr + R_SMB_START);
 		break;
 	case I2C_SMBUS_BYTE:
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 1;
 		} else {
-			csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	case I2C_SMBUS_BYTE_DATA:
-		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+		__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 1;
 		} else {
-			csr_out32(V_SMB_LB(data->byte),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_LB(data->byte), csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	case I2C_SMBUS_WORD_DATA:
-		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+		__raw_writeq(V_SMB_CMD(command), csr + R_SMB_CMD);
 		if (read_write == I2C_SMBUS_READ) {
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE,
+				     csr + R_SMB_START);
 			data_bytes = 2;
 		} else {
-			csr_out32(V_SMB_LB(data->word & 0xff),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32(V_SMB_MB(data->word >> 8),
-				  SMB_CSR(adap, R_SMB_DATA));
-			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
-				  SMB_CSR(adap, R_SMB_START));
+			__raw_writeq(V_SMB_LB(data->word & 0xff),
+				     csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_MB(data->word >> 8),
+				     csr + R_SMB_DATA);
+			__raw_writeq(V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE,
+				     csr + R_SMB_START);
 		}
 		break;
 	default:
-		return -1;      /* XXXKW better error code? */
+		error = -EINVAL;
+		goto out_unlock;
 	}
+	mmiowb();
+	__raw_readq(csr + R_SMB_START);
+	adap->status = -1;
+
+	spin_unlock_irqrestore(&adap->lock, flags);
 
-	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
-		;
+	wait_event_timeout(adap->wait, (adap->status >= 0), HZ);
 
-	error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
-	if (error & M_SMB_ERROR) {
-		/* Clear error bit by writing a 1 */
-		csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
-		return -1;      /* XXXKW better error code? */
+	spin_lock_irqsave(&adap->lock, flags);
+
+	if (adap->status < 0 || (adap->status & (M_SMB_ERROR | M_SMB_BUSY))) {
+		error = -EIO;
+		goto out_unlock;
 	}
 
 	if (data_bytes == 1)
-		data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
+		data->byte = __raw_readq(csr + R_SMB_DATA) & 0xff;
 	if (data_bytes == 2)
-		data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
+		data->word = __raw_readq(csr + R_SMB_DATA) & 0xffff;
 
-	return 0;
+	error = 0;
+
+out_unlock:
+	spin_unlock_irqrestore(&adap->lock, flags);
+
+	return error;
 }
 
-static u32 bit_func(struct i2c_adapter *adap)
+static u32 i2c_sibyte_bit_func(struct i2c_adapter *adap)
 {
 	return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
 		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
@@ -125,8 +181,8 @@ static u32 bit_func(struct i2c_adapter *
 /* -----exported algorithm data: -------------------------------------	*/
 
 static const struct i2c_algorithm i2c_sibyte_algo = {
-	.smbus_xfer	= smbus_xfer,
-	.functionality	= bit_func,
+	.smbus_xfer	= i2c_sibyte_smbus_xfer,
+	.functionality	= i2c_sibyte_bit_func,
 };
 
 /*
@@ -135,30 +191,101 @@ static const struct i2c_algorithm i2c_si
 static int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
 {
 	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr;
+	int err;
 
-	/* Register new adapter to i2c module... */
-	i2c_adap->algo = &i2c_sibyte_algo;
+	adap->status = 0;
+	init_waitqueue_head(&adap->wait);
+	spin_lock_init(&adap->lock);
+
+	csr = ioremap(adap->base, R_SMB_PEC + SMB_REGISTER_SPACING);
+	if (!csr) {
+		err = -ENOMEM;
+		goto out;
+	}
+	adap->csr = csr;
 
 	/* Set the requested frequency. */
-	csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
-	csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
+	__raw_writeq(speed, csr + R_SMB_FREQ);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	err = request_irq(adap->irq, i2c_sibyte_interrupt, IRQF_SHARED,
+			  adap->name, i2c_adap);
+	if (err < 0)
+		goto out_unmap;
 
-	return i2c_add_numbered_adapter(i2c_adap);
+	/* Enable finish and error interrupts. */
+	__raw_writeq(M_SMB_FINISH_INTR | M_SMB_ERR_INTR, csr + R_SMB_CONTROL);
+
+	/* Register new adapter to i2c module... */
+	err = i2c_add_numbered_adapter(i2c_adap);
+	if (err < 0)
+		goto out_unirq;
+
+	return 0;
+
+out_unirq:
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	free_irq(adap->irq, i2c_adap);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+out_unmap:
+	iounmap(csr);
+out:
+	return err;
 }
 
+static void i2c_sibyte_remove_bus(struct i2c_adapter *i2c_adap)
+{
+	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+	void __iomem *csr = adap->csr;
+
+	i2c_del_adapter(i2c_adap);
+
+	/* Disable interrupts. */
+	__raw_writeq(0, csr + R_SMB_CONTROL);
+	mmiowb();
+	__raw_readq(csr + R_SMB_CONTROL);
+
+	free_irq(adap->irq, i2c_adap);
+
+	/* Clear any pending error interrupt. */
+	__raw_writeq(__raw_readq(csr + R_SMB_STATUS), csr + R_SMB_STATUS);
+
+	iounmap(csr);
+}
 
-static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
-	{ NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
-	{ NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
+static struct i2c_algo_sibyte_data i2c_sibyte_board_data[2] = {
+	{
+		.name	= "sb1250-smbus-0",
+		.base	= A_SMB_0,
+		.irq	= K_INT_SMB_0,
+	},
+	{
+		.name	= "sb1250-smbus-1",
+		.base	= A_SMB_1,
+		.irq	= K_INT_SMB_1,
+	}
 };
 
-static struct i2c_adapter sibyte_board_adapter[2] = {
+static struct i2c_adapter i2c_sibyte_board_adapter[2] = {
 	{
 		.owner		= THIS_MODULE,
 		.id		= I2C_HW_SIBYTE,
 		.class		= I2C_CLASS_HWMON,
-		.algo		= NULL,
-		.algo_data	= &sibyte_board_data[0],
+		.algo		= &i2c_sibyte_algo,
+		.algo_data	= &i2c_sibyte_board_data[0],
 		.nr		= 0,
 		.name		= "SiByte SMBus 0",
 	},
@@ -166,8 +293,8 @@ static struct i2c_adapter sibyte_board_a
 		.owner		= THIS_MODULE,
 		.id		= I2C_HW_SIBYTE,
 		.class		= I2C_CLASS_HWMON,
-		.algo		= NULL,
-		.algo_data	= &sibyte_board_data[1],
+		.algo		= &i2c_sibyte_algo,
+		.algo_data	= &i2c_sibyte_board_data[1],
 		.nr		= 1,
 		.name		= "SiByte SMBus 1",
 	},
@@ -175,21 +302,32 @@ static struct i2c_adapter sibyte_board_a
 
 static int __init i2c_sibyte_init(void)
 {
+	int err;
+
 	pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n");
-	if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0)
-		return -ENODEV;
-	if (i2c_sibyte_add_bus(&sibyte_board_adapter[1],
-			       K_SMB_FREQ_400KHZ) < 0) {
-		i2c_del_adapter(&sibyte_board_adapter[0]);
-		return -ENODEV;
-	}
+
+	err = i2c_sibyte_add_bus(&i2c_sibyte_board_adapter[0],
+				 K_SMB_FREQ_100KHZ);
+	if (err < 0)
+		goto out;
+
+	err = i2c_sibyte_add_bus(&i2c_sibyte_board_adapter[1],
+				 K_SMB_FREQ_400KHZ);
+	if (err < 0)
+		goto out_remove;
+
 	return 0;
+
+out_remove:
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[0]);
+out:
+	return err;
 }
 
 static void __exit i2c_sibyte_exit(void)
 {
-	i2c_del_adapter(&sibyte_board_adapter[0]);
-	i2c_del_adapter(&sibyte_board_adapter[1]);
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[1]);
+	i2c_sibyte_remove_bus(&i2c_sibyte_board_adapter[0]);
 }
 
 module_init(i2c_sibyte_init);

^ permalink raw reply	[flat|nested] 56+ messages in thread

end of thread, other threads:[~2012-12-18 12:08 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-18 23:43 [PATCH] I2C: SiByte: Convert the driver to make use of interrupts Matt Turner
2011-08-18 23:43 ` Matt Turner
     [not found] ` <1313710991-3596-1-git-send-email-mattst88-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2011-08-22 20:02   ` Guenter Roeck
2011-08-22 20:02     ` Guenter Roeck
2011-08-24 15:36   ` Matt Turner
2011-08-24 15:36     ` Matt Turner
     [not found]     ` <CAEdQ38E6qqVAKC1MkAWto5yeU9N2uoyGY1Y5431kNUNL_yc8EA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-09-02 13:21       ` Jean Delvare
2011-09-02 13:21         ` Jean Delvare
2011-09-02 13:35         ` Maciej W. Rozycki
2011-09-03  8:30   ` Jean Delvare
2011-09-03  8:30     ` Jean Delvare
     [not found]     ` <20110903103036.161616a5-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2011-10-31  9:53       ` Jean Delvare
2011-10-31  9:53         ` Jean Delvare
2011-10-31  9:53         ` Jean Delvare
     [not found]         ` <20111031105354.4b888e44-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2012-01-10 14:38           ` Jean Delvare
2012-01-10 14:38             ` Jean Delvare
2012-01-10 15:31             ` Maciej W. Rozycki
     [not found]             ` <20120110153834.531664db-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2012-01-12 21:19               ` Matt Turner
2012-01-12 21:19                 ` Matt Turner
     [not found]                 ` <CAEdQ38FpG11m50pwg2=tu1fJRRg=zixFKLsPmVPOzWNBCjbNBg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-03-31  6:23                   ` Jean Delvare
2012-03-31  6:23                     ` Jean Delvare
     [not found]                     ` <20120331082346.26cc95cb-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2012-03-31 12:11                       ` Matt Turner
2012-03-31 12:11                         ` Matt Turner
     [not found]                         ` <CAEdQ38Ez+8DudAaJY7HZu9jbisk_KMbBO5h=s+P4pjJ0Va-zWw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-04-03 12:26                           ` Maciej W. Rozycki
2012-04-03 12:26                             ` Maciej W. Rozycki
2012-06-30 16:35                           ` Matt Turner
2012-06-30 16:35                             ` Matt Turner
     [not found]                             ` <CAEdQ38EDKndUcdBu1tZ_dOuhweVRW6aA=YKb6kUE3gUQJiwWoQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-07-19 21:01                               ` Maciej W. Rozycki
2012-07-19 21:01                                 ` Maciej W. Rozycki
     [not found]                                 ` <alpine.LFD.2.00.1207160208570.12288-FBDaDh2CBnQu/uO211tRtWD2FQJk+8+b@public.gmane.org>
2012-12-18 12:08                                   ` Jean Delvare
2012-12-18 12:08                                     ` Jean Delvare
2012-12-18 12:08                                     ` Jean Delvare
  -- strict thread matches above, loose matches on Subject: below --
2010-12-06  6:38 Matt Turner
     [not found] ` <1291617494-18430-1-git-send-email-mattst88-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2010-12-06 14:59   ` Guenter Roeck
2010-12-06 14:59     ` Guenter Roeck
2010-12-06 17:30   ` Guenter Roeck
2010-12-06 17:30     ` Guenter Roeck
     [not found]     ` <20101206173040.GA18372-IzeFyvvaP7pWk0Htik3J/w@public.gmane.org>
2010-12-06 17:40       ` Matt Turner
2010-12-06 17:40         ` Matt Turner
     [not found]         ` <AANLkTikGgfBuj086eRvy4VzzyE2suJCL9z=SfmOiFiPx-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-12-06 18:02           ` Guenter Roeck
2010-12-06 18:02             ` Guenter Roeck
2010-12-06 18:04             ` Matt Turner
2010-12-06 17:56     ` Maciej W. Rozycki
     [not found]       ` <alpine.LFD.2.00.1012061739200.17185-FBDaDh2CBnQu/uO211tRtWD2FQJk+8+b@public.gmane.org>
2010-12-06 18:02         ` Matt Turner
2010-12-06 18:02           ` Matt Turner
     [not found]           ` <AANLkTikWRsgHao_eb4W47b=E4vm6z=hi36JE_VBtD6Rg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-12-07  2:26             ` Maciej W. Rozycki
2010-12-07  2:26               ` Maciej W. Rozycki
     [not found]               ` <alpine.LFD.2.00.1012070148050.17185-FBDaDh2CBnQu/uO211tRtWD2FQJk+8+b@public.gmane.org>
2010-12-07  5:14                 ` Guenter Roeck
2010-12-07  5:14                   ` Guenter Roeck
     [not found]                   ` <20101207051438.GA20144-IzeFyvvaP7pWk0Htik3J/w@public.gmane.org>
2010-12-07 14:30                     ` Maciej W. Rozycki
2010-12-07 14:30                       ` Maciej W. Rozycki
     [not found]                       ` <alpine.LFD.2.00.1012070740130.17185-FBDaDh2CBnQu/uO211tRtWD2FQJk+8+b@public.gmane.org>
2010-12-07 14:41                         ` Guenter Roeck
2010-12-07 14:41                           ` Guenter Roeck
2010-12-07  6:23   ` Guenter Roeck
2010-12-07  6:23     ` Guenter Roeck
2008-05-13  3:28 Maciej W. Rozycki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.