From: Ivo Manca <pinkel-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
Cc: Hans de Goede <j.w.r.degoede-fbo2DhPpy/Q@public.gmane.org>,
i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org
Subject: [PATCH][RESEND] i2c-i801: Add basic interrupt support
Date: Wed, 13 Aug 2008 22:05:11 +0200 [thread overview]
Message-ID: <48A33E77.7060502@gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 326 bytes --]
This patch adds basic interrupt support to the i2c-i801 driver,
configurable by the module parameter use_irq.
Signed-off-by: Ivo Manca <pinkel-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
i2c-i801.c | 151
++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 136 insertions(+), 15 deletions(-)
[-- Attachment #2: diff --]
[-- Type: text/plain, Size: 7650 bytes --]
diff -upr linux-2.6.27-rc3/drivers/i2c/busses/i2c-i801.c linux-2.6.27-rc3.new/drivers/i2c/busses/i2c-i801.c
--- linux-2.6.27-rc3/drivers/i2c/busses/i2c-i801.c 2008-08-13 20:45:50.000000000 +0200
+++ linux-2.6.27-rc3.new/drivers/i2c/busses/i2c-i801.c 2008-08-13 21:04:50.000000000 +0200
@@ -61,6 +61,7 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <asm/io.h>
@@ -93,9 +94,9 @@
/* kill bit for SMBHSTCNT */
#define SMBHSTCNT_KILL 2
-/* Other settings */
+/* Timeout settings */
#define MAX_TIMEOUT 100
-#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
+#define INTERRUPT_TIMEOUT (HZ/2)
/* I801 command constants */
#define I801_QUICK 0x00
@@ -120,21 +121,71 @@
#define SMBHSTSTS_INTR 0x02
#define SMBHSTSTS_HOST_BUSY 0x01
+/* Interrupt enabled */
+#define I801_INTREN 0x01
+
+/* Mask for events we normally handle */
+#define I801_HST_STS_MASK_NORM ( \
+ SMBHSTSTS_FAILED | \
+ SMBHSTSTS_BUS_ERR | \
+ SMBHSTSTS_DEV_ERR | \
+ SMBHSTSTS_INTR)
+
+/* Mask for all events */
+#define I801_HST_STS_MASK_ALL ( \
+ SMBHSTSTS_BYTE_DONE | \
+ SMBHSTSTS_SMBALERT_STS | \
+ SMBHSTSTS_FAILED | \
+ SMBHSTSTS_BUS_ERR | \
+ SMBHSTSTS_DEV_ERR | \
+ SMBHSTSTS_INTR)
+
#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | \
SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \
SMBHSTSTS_INTR)
+/* If use_irq is set to anything different than 0, interrupts will be used
+ if availabe. EXPERIMENTAL! */
+static int use_irq;
+module_param(use_irq, bool, S_IRUGO);
+MODULE_PARM_DESC(force, "Use interrupts if available. EXPERIMENTAL!");
+
static unsigned long i801_smba;
static unsigned char i801_original_hstcfg;
+static struct i2c_adapter i801_adapter;
static struct pci_driver i801_driver;
static struct pci_dev *I801_dev;
+struct i2c_i801_algo_data {
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ int status; /* copy of h/w register */
+ bool use_irq;
+};
+
+static struct i2c_i801_algo_data i801_algo_data;
+
#define FEATURE_SMBUS_PEC (1 << 0)
#define FEATURE_BLOCK_BUFFER (1 << 1)
#define FEATURE_BLOCK_PROC (1 << 2)
#define FEATURE_I2C_BLOCK_READ (1 << 3)
static unsigned int i801_features;
+/* interrupt handling: fetch & consume host status out of algo_data */
+static inline int i801_get_status(struct i2c_i801_algo_data *algo_data)
+{
+ unsigned long flags;
+ int status;
+
+ spin_lock_irqsave(&algo_data->lock, flags);
+ status = algo_data->status;
+ algo_data->status = 0;
+ spin_unlock_irqrestore(&algo_data->lock, flags);
+
+ return status;
+}
+
+
/* Make sure the SMBus host is ready to start transmitting.
Return 0 if it is, -EBUSY if it is not. */
static int i801_check_pre(void)
@@ -221,21 +272,33 @@ static int i801_transaction(int xact)
int result;
int timeout = 0;
+ struct i2c_i801_algo_data *algo_data = i801_adapter.algo_data;
+
result = i801_check_pre();
if (result < 0)
return result;
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
* INTREN, SMBSCMD are passed in xact */
- outb_p(xact | I801_START, SMBHSTCNT);
+ if (algo_data->use_irq) {
+ outb_p(xact | I801_START | I801_INTREN, SMBHSTCNT);
- /* We will always wait for a fraction of a second! */
- do {
- msleep(1);
- status = inb_p(SMBHSTSTS);
- } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
+ timeout = wait_event_timeout(algo_data->waitq,
+ ((status = i801_get_status(algo_data))
+ & I801_HST_STS_MASK_NORM), INTERRUPT_TIMEOUT);
+ result = i801_check_post(status, timeout == 0);
+ } else {
+ outb_p(xact | I801_START, SMBHSTCNT);
+
+ /* We will always wait for a fraction of a second! */
+ do {
+ msleep(1);
+ status = inb_p(SMBHSTSTS);
+ } while ((status & SMBHSTSTS_HOST_BUSY) &&
+ (timeout++ < MAX_TIMEOUT));
+ result = i801_check_post(status, timeout >= MAX_TIMEOUT);
+ }
- result = i801_check_post(status, timeout >= MAX_TIMEOUT);
if (result < 0)
return result;
@@ -277,8 +340,7 @@ static int i801_block_transaction_by_blo
outb_p(data->block[i+1], SMBBLKDAT);
}
- status = i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 |
- I801_PEC_EN * hwpec);
+ status = i801_transaction(I801_BLOCK_DATA | I801_PEC_EN * hwpec);
if (status)
return status;
@@ -328,7 +390,7 @@ static int i801_block_transaction_byte_b
else
smbcmd = I801_BLOCK_DATA;
}
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
+ outb_p(smbcmd, SMBHSTCNT);
if (i == 1)
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
@@ -509,7 +571,7 @@ static s32 i801_access(struct i2c_adapte
if(block)
ret = i801_block_transaction(data, read_write, size, hwpec);
else
- ret = i801_transaction(xact | ENABLE_INT9);
+ ret = i801_transaction(xact);
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
time, so we forcibly disable it after every transaction. Turn off
@@ -558,6 +620,7 @@ static struct i2c_adapter i801_adapter =
.id = I2C_HW_SMBUS_I801,
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
+ .algo_data = &i801_algo_data,
};
static struct pci_device_id i801_ids[] = {
@@ -581,8 +644,38 @@ static struct pci_device_id i801_ids[] =
MODULE_DEVICE_TABLE (pci, i801_ids);
+static irqreturn_t i801_isr(int irq, void *dev_id)
+{
+ u8 status = inb(SMBHSTSTS);
+
+ /* bail if it's not ours */
+ if (!(status & I801_HST_STS_MASK_ALL)) {
+ dev_dbg(&I801_dev->dev, "BAILING interrupt\n");
+ return IRQ_NONE;
+ }
+
+ dev_dbg(&I801_dev->dev, "INTERRUPT: IRQ status: 0x%02x\n", status);
+
+ /* ACK */
+ outb((status & I801_HST_STS_MASK_ALL), SMBHSTSTS);
+
+ if (status & I801_HST_STS_MASK_NORM) {
+ struct i2c_adapter *adap = dev_id;
+ struct i2c_i801_algo_data *algo_data = adap->algo_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&algo_data->lock, flags);
+ algo_data->status = status;
+ spin_unlock_irqrestore(&algo_data->lock, flags);
+ wake_up(&algo_data->waitq);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
+ struct i2c_i801_algo_data *algo_data = i801_adapter.algo_data;
unsigned char temp;
int err;
@@ -644,10 +737,32 @@ static int __devinit i801_probe(struct p
}
pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
- if (temp & SMBHSTCFG_SMB_SMI_EN)
+ if (temp & SMBHSTCFG_SMB_SMI_EN) {
dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
- else
+ algo_data->use_irq = false;
+ } else {
dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n");
+ if (use_irq) {
+ if ((request_irq(I801_dev->irq, i801_isr, IRQF_SHARED,
+ i801_driver.name, &i801_adapter))) {
+ dev_err(&dev->dev, "request irq %d failed!\n",
+ I801_dev->irq);
+ algo_data->use_irq = false;
+ } else {
+ dev_dbg(&dev->dev, "SMBus base address: "
+ "0x%04lx, IRQ: %d\n",
+ i801_smba, I801_dev->irq);
+
+ algo_data->use_irq = true;
+ algo_data->status = 0;
+ init_waitqueue_head(&algo_data->waitq);
+ spin_lock_init(&algo_data->lock);
+ }
+ } else {
+ algo_data->use_irq = false;
+ dev_dbg(&dev->dev, "Interrupts disabled.\n");
+ }
+ }
/* Clear special mode bits */
if (i801_features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER))
@@ -674,7 +789,13 @@ exit:
static void __devexit i801_remove(struct pci_dev *dev)
{
+ struct i2c_i801_algo_data *algo_data = i801_adapter.algo_data;
+
i2c_del_adapter(&i801_adapter);
+
+ if (algo_data->use_irq)
+ free_irq(dev->irq, &i801_adapter);
+
pci_write_config_byte(I801_dev, SMBHSTCFG, i801_original_hstcfg);
pci_release_region(dev, SMBBAR);
/*
[-- Attachment #3: Type: text/plain, Size: 157 bytes --]
_______________________________________________
i2c mailing list
i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org
http://lists.lm-sensors.org/mailman/listinfo/i2c
next reply other threads:[~2008-08-13 20:05 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-08-13 20:05 Ivo Manca [this message]
[not found] ` <48A33E77.7060502-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2008-08-16 13:58 ` [PATCH][RESEND] i2c-i801: Add basic interrupt support Jean Delvare
[not found] ` <20080816155856.2e0bcf4c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-08-16 16:22 ` Ivo Manca
[not found] ` <48A6FECC.8020801-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2008-08-16 16:50 ` Jean Delvare
[not found] ` <20080816185013.30fcf4ee-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-08-16 17:41 ` Jean Delvare
[not found] ` <20080816194147.7c0a11dc-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-08-16 18:01 ` Hans de Goede
[not found] ` <48A715FE.80802-fbo2DhPpy/Q@public.gmane.org>
2008-08-16 18:33 ` Jean Delvare
[not found] ` <20080816203347.0af81c5b-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-08-20 4:53 ` Ivo Manca
[not found] ` <48ABA35C.50404-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2008-08-23 8:11 ` Jean Delvare
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=48A33E77.7060502@gmail.com \
--to=pinkel-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org \
--cc=j.w.r.degoede-fbo2DhPpy/Q@public.gmane.org \
--cc=khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.