public inbox for linux-i2c@vger.kernel.org
 help / color / mirror / Atom feed
From: David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
To: i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org
Subject: [patch 2.6.25-git] i2c: smbalert# support
Date: Fri, 2 May 2008 04:10:44 -0700	[thread overview]
Message-ID: <200805020410.44723.david-b@pacbell.net> (raw)
In-Reply-To: <200804161027.49943.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>

Infrastructure supporting SMBALERT# interrupts and the related SMBus
protocols.  These are defined as "optional" by the spec.

 - The i2c_adapter now includes a work_struct doing the work of talking
   to the Alert Response Address until nobody responds any more (and
   hence the IRQ is no longer asserted).  Follows SMBus 2.0 not 1.1;
   there seems to be no point in trying to handle ten-bit addresses.

 - Some of the ways that work_struct could be driven:

     * Adapter driver provides an IRQ, which is bound to a handler
       which schedules that work_struct (using keventd for now).
       NOTE:  it's nicest if this is edge triggered, but the code
       should handle level triggered IRQs too.

     * Adapter driver schedules that work struct itself, maybe even
       on a workqueue of its own.  It asks the core to set it up by
       setting i2c_adapter.do_setup_alert ... SMBALERT# could be a
       subcase of the adapter's normal interrupt handler.  (Or, some
       boards may want to use polling.)

 - The "i2c-gpio" driver now handles an optional named resource for
   that SMBus alert signal.  Named since, when this is substituted
   for a misbehaving "native" driver, positional ids should be left
   alone.  (It might be better to put this logic into i2c core, to
   apply whenever the i2c_adapter.dev.parent is a platform device.)

 - There's a new driver method used to report that a given device has
   issued an alert. Its parameter includes the one bit of information
   provided by the device in its alert response message.

The IRQ driven code path is always enabled, if it's available.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
CHANGES since last post:  cope with updated "new style binding";
work better with (preferred) edge triggered IRQs.

 drivers/i2c/busses/i2c-gpio.c |   10 ++
 drivers/i2c/i2c-core.c        |  146 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c.h           |   14 ++++
 3 files changed, 170 insertions(+)

--- a/drivers/i2c/busses/i2c-gpio.c	2008-05-02 00:15:29.000000000 -0700
+++ b/drivers/i2c/busses/i2c-gpio.c	2008-05-02 01:53:33.000000000 -0700
@@ -82,6 +82,7 @@ static int __init i2c_gpio_probe(struct 
 	struct i2c_gpio_platform_data *pdata;
 	struct i2c_algo_bit_data *bit_data;
 	struct i2c_adapter *adap;
+	struct resource *smbalert;
 	int ret;
 
 	pdata = pdev->dev.platform_data;
@@ -143,6 +144,15 @@ static int __init i2c_gpio_probe(struct 
 	adap->class = I2C_CLASS_HWMON;
 	adap->dev.parent = &pdev->dev;
 
+	smbalert = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+			"smbalert#");
+	if (smbalert) {
+		adap->irq = smbalert->start;
+		if ((IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE)
+				& smbalert->flags)
+			adap->alert_edge_triggered = 1;
+	}
+
 	/*
 	 * If "dev->id" is negative we consider it as zero.
 	 * The reason to do so is to avoid sysfs names that only make
--- a/drivers/i2c/i2c-core.c	2008-05-02 00:15:29.000000000 -0700
+++ b/drivers/i2c/i2c-core.c	2008-05-02 01:52:36.000000000 -0700
@@ -33,6 +33,8 @@
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/completion.h>
+#include <linux/interrupt.h>
+
 #include <linux/hardirq.h>
 #include <linux/irqflags.h>
 #include <linux/semaphore.h>
@@ -433,6 +435,108 @@ static int i2c_do_add_adapter(struct dev
 	return 0;
 }
 
+/*
+ * The IRQ handler needs to hand work off to a task which can issue SMBus
+ * calls, because those sleeping calls can't be made in IRQ context.
+ */
+static void smbus_alert(struct work_struct *work)
+{
+	struct i2c_adapter	*bus;
+
+	bus = container_of(work, struct i2c_adapter, alert);
+	for (;;) {
+		s32			status;
+		unsigned short		addr;
+		struct i2c_client	*client;
+		bool			flag;
+
+		/* Devices with pending alerts reply in address order, low
+		 * to high, because of arbitration.  After responding, an
+		 * SMBus device stops asserting SMBALERT# ... so we can
+		 * re-enable the IRQ as soon as read_byte() gets no reply.
+		 *
+		 * NOTE that SMBus 2.0 reserves 10-bit addresess for future
+		 * use.  We neither handle them, nor try to use PEC here.
+		 */
+		status = i2c_smbus_read_byte(bus->ara);
+		if (status < 0)
+			break;
+		flag = status & 1;
+		addr = status >> 1;
+
+		dev_dbg(&bus->dev, "SMBALERT# %d from dev 0x%02x\n", flag, addr);
+
+		/* Notify any driver for the device which issued the alert.
+		 * The locking ensures it won't disappear while we do that.
+		 */
+		mutex_lock(&core_lock);
+		list_for_each_entry(client, &bus->clients, list) {
+			if (client->addr != addr)
+				continue;
+			if (client->flags & I2C_CLIENT_TEN)
+				continue;
+			if (!client->driver)
+				break;
+
+			/* Drivers should either disable alerts or provide
+			 * at least a minimal handler.
+			 *
+			 * REVISIT:  drop lock while we call alert().
+			 */
+			if (client->driver->alert)
+				client->driver->alert(client, flag);
+			else
+				dev_warn(&client->dev, "no driver alert()!\n");
+			break;
+		}
+		mutex_unlock(&core_lock);
+	}
+
+	/* reenable level-triggered IRQs */
+	if (!bus->alert_edge_triggered)
+		enable_irq(bus->irq);
+}
+
+static irqreturn_t smbus_irq(int irq, void *adap)
+{
+	struct i2c_adapter	*bus = adap;
+
+	/* disable level-triggered IRQs until we handle them */
+	if (!bus->alert_edge_triggered)
+		disable_irq_nosync(irq);
+
+	schedule_work(&bus->alert);
+	return IRQ_HANDLED;
+}
+
+static int smbalert_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+	return 0;
+}
+
+static int smbalert_remove(struct i2c_client *c)
+{
+	return 0;
+}
+
+static const struct i2c_device_id smbalert_ids[] = {
+	{ "smbus_alert", 0, },
+	{ /* LIST END */ },
+};
+
+static struct i2c_driver smbalert_driver = {
+	.driver = {
+		.name	= "smbus_alert",
+	},
+	.probe		= smbalert_probe,
+	.remove		= smbalert_remove,
+	.id_table	= smbalert_ids,
+};
+
+static const struct i2c_board_info ara_board_info = {
+	I2C_BOARD_INFO("smbus_alert", 0x0c),
+};
+
 static int i2c_register_adapter(struct i2c_adapter *adap)
 {
 	int res = 0, dummy;
@@ -441,6 +545,9 @@ static int i2c_register_adapter(struct i
 	mutex_init(&adap->clist_lock);
 	INIT_LIST_HEAD(&adap->clients);
 
+	/* Setup SMBALERT# infrastructure. */
+	INIT_WORK(&adap->alert, smbus_alert);
+
 	mutex_lock(&core_lock);
 
 	/* Add the adapter to the driver core.
@@ -459,6 +566,33 @@ static int i2c_register_adapter(struct i
 	if (res)
 		goto out_list;
 
+	/* If we'll be handling SMBus alerts, register the alert responder
+	 * address so that nobody else can accidentally claim it.
+	 * Handling can be done either through our IRQ handler, or by the
+	 * adapter (from its handler, periodic polling, or whatever).
+	 *
+	 * NOTE that if we manage the IRQ, we *MUST* know if it's level or
+	 * edge triggered in order to hand it to the workqueue correctly.
+	 * If triggering the alert seems to wedge the system, you probably
+	 * should have said it's level triggered.
+	 */
+	if (adap->irq > 0 || adap->do_setup_alert)
+		adap->ara = i2c_new_device(adap, &ara_board_info);
+
+	if (adap->irq > 0 && adap->ara) {
+		res = devm_request_irq(&adap->dev, adap->irq, smbus_irq,
+				0, "smbus_alert", adap);
+		if (res == 0) {
+			dev_info(&adap->dev,
+				"supports SMBALERT#, %s trigger\n",
+				adap->alert_edge_triggered
+					? "edge" : "level");
+			adap->has_alert_irq = 1;
+		} else
+			res = 0;
+
+	}
+
 	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
 
 	/* create pre-declared device nodes for new-style drivers */
@@ -635,6 +769,12 @@ int i2c_del_adapter(struct i2c_adapter *
 		}
 	}
 
+	if (adap->has_alert_irq) {
+		devm_free_irq(&adap->dev, adap->irq, adap);
+		adap->has_alert_irq = 0;
+	}
+	cancel_work_sync(&adap->alert);
+
 	/* clean up the sysfs representation */
 	init_completion(&adap->dev_released);
 	device_unregister(&adap->dev);
@@ -922,6 +1062,9 @@ static int __init i2c_init(void)
 	retval = bus_register(&i2c_bus_type);
 	if (retval)
 		return retval;
+	retval = i2c_add_driver(&smbalert_driver);
+	if (retval)
+		goto alert_err;
 	retval = class_register(&i2c_adapter_class);
 	if (retval)
 		goto bus_err;
@@ -933,6 +1076,8 @@ static int __init i2c_init(void)
 class_err:
 	class_unregister(&i2c_adapter_class);
 bus_err:
+	i2c_del_driver(&smbalert_driver);
+alert_err:
 	bus_unregister(&i2c_bus_type);
 	return retval;
 }
@@ -941,6 +1086,7 @@ static void __exit i2c_exit(void)
 {
 	i2c_del_driver(&dummy_driver);
 	class_unregister(&i2c_adapter_class);
+	i2c_del_driver(&smbalert_driver);
 	bus_unregister(&i2c_bus_type);
 }
 
--- a/include/linux/i2c.h	2008-05-02 00:15:29.000000000 -0700
+++ b/include/linux/i2c.h	2008-05-02 01:53:43.000000000 -0700
@@ -34,6 +34,7 @@
 #include <linux/device.h>	/* for struct device */
 #include <linux/sched.h>	/* for completion */
 #include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 /* --- General options ------------------------------------------------	*/
 
@@ -134,6 +135,11 @@ struct i2c_driver {
 	int (*suspend)(struct i2c_client *, pm_message_t mesg);
 	int (*resume)(struct i2c_client *);
 
+	/* SMBus alert protocol support; the low bit of the code sometimes
+	 * passes event data (e.g. exceeding upper vs lower limit).
+	 */
+	void (*alert)(struct i2c_client *, bool flag);
+
 	/* a ioctl like command that can be used to perform specific functions
 	 * with the device.
 	 */
@@ -332,6 +338,14 @@ struct i2c_adapter {
 	struct list_head clients;	/* DEPRECATED */
 	char name[48];
 	struct completion dev_released;
+
+	/* SMBALERT# support */
+	unsigned		do_setup_alert:1;
+	unsigned		has_alert_irq:1;
+	unsigned		alert_edge_triggered:1;
+	int			irq;
+	struct i2c_client	*ara;
+	struct work_struct	alert;
 };
 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
 

_______________________________________________
i2c mailing list
i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org
http://lists.lm-sensors.org/mailman/listinfo/i2c

       reply	other threads:[~2008-05-02 11:10 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <200804161434.54335.laurentp@cse-semaphore.com>
     [not found] ` <20080416153043.02f89554@hyperion.delvare>
     [not found]   ` <200804161027.49943.david-b@pacbell.net>
     [not found]     ` <200804161027.49943.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-05-02 11:10       ` David Brownell [this message]
     [not found]         ` <200805020410.44723.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-05-05  5:56           ` [patch 2.6.265-rc1] i2c: smbalert# support David Brownell
     [not found]             ` <200805042256.49252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-09-23 22:32               ` [patch 2.6.27-rc7] " David Brownell
     [not found]                 ` <200809231532.40083.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-09-26  1:07                   ` [lm-sensors] " Trent Piepho
     [not found]                     ` <Pine.LNX.4.64.0809251728130.7680-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-09-26  2:01                       ` David Brownell
     [not found]                         ` <200809251902.00201.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-09-27  0:29                           ` Trent Piepho
     [not found]                             ` <Pine.LNX.4.64.0809261652040.7680-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-10-17 19:04                               ` David Brownell
     [not found]                                 ` <200810171204.54448.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-10-17 20:43                                   ` Trent Piepho
2008-11-18  8:15                   ` Jean Delvare
     [not found]                     ` <20081118091546.421d6b78-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-18 22:01                       ` David Brownell
     [not found]                         ` <200811181401.34809.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-19  9:51                           ` [lm-sensors] " Trent Piepho
     [not found]                             ` <Pine.LNX.4.64.0811190140510.11673-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-11-19 15:16                               ` Jean Delvare
     [not found]                                 ` <20081119161632.2d0bde9e-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-20 22:00                                   ` Trent Piepho
     [not found]                                     ` <Pine.LNX.4.64.0811201354310.11673-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-11-20 22:56                                       ` David Brownell
     [not found]                                         ` <200811201456.36551.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-22  0:55                                           ` Trent Piepho
     [not found]                                             ` <Pine.LNX.4.64.0811211621340.18022-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-11-22  2:58                                               ` David Brownell
2008-11-21  8:42                                       ` Jean Delvare
     [not found]                                         ` <20081121094218.34ecd82a-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-22  6:04                                           ` Trent Piepho
     [not found]                                             ` <Pine.LNX.4.64.0811212122370.18022-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-11-22 10:13                                               ` Jean Delvare
     [not found]                                                 ` <20081122111340.0bfc2c05-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-22 20:28                                                   ` Trent Piepho
     [not found]                                                     ` <Pine.LNX.4.64.0811221055470.18022-3bmvVOk6DZ+DGx/iekXGtrjh7zpefjiS@public.gmane.org>
2008-11-23 21:45                                                       ` Jean Delvare
2008-11-19 13:57                           ` Jean Delvare
     [not found]                             ` <20081119145712.1abaa63f-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-19 18:08                               ` David Brownell
2008-11-21 14:18                           ` Jean Delvare
     [not found]                             ` <20081121151808.324ca78c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-21 16:24                               ` David Brownell
     [not found]                                 ` <200811210824.55601.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-21 19:22                                   ` Jean Delvare
     [not found]                                     ` <20081121202223.0261fb9c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-21 21:54                                       ` David Brownell
     [not found]                                         ` <200811211354.51501.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-22  9:03                                           ` Jean Delvare
     [not found]                                             ` <20081122100344.61cf42b7-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-22  9:48                                               ` David Brownell

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=200805020410.44723.david-b@pacbell.net \
    --to=david-b-ybekhbn/0ldr7s880joybq@public.gmane.org \
    --cc=i2c-GZX6beZjE8VD60Wz+7aTrA@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