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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox