[i2c] [PATCH 1/2] Add polling transfer for i2c-pxa
Mike Rapoport
mike at compulab.co.il
Thu Dec 6 10:10:26 CET 2007
Hi Jean,
This patchset adds ability to make i2c transfers in interrupts-off contexts and
implements polling transfers for i2c-pxa.
The i2c-core patch includes fixes for the issues you raised. The only problem
left is the mutex nesting, since, as you pointed there's no mutex_trylock_nested...
> I'm curious how many drivers will need this; I'm not sure I see the
> value. This could be implemented at the driver-level as well, and I
> think I'd prefer it that way, unless you have reasons to believe that
> many drivers will want this.
I think that most PMIC drivers will need this. There are cases when you need to
access the PMIC in interrupts-off context, for example during late suspend and
early resume.
Signed-off-by: Mike Rapoport <mike at compulab.co.il>
drivers/i2c/i2c-core.c | 50 +++++++++++++++++++++++++++++++++++------------
include/linux/i2c.h | 5 ++++
2 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index b5e13e4..059c6bd 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -33,6 +33,8 @@
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/completion.h>
+#include <linux/hardirq.h>
+#include <linux/irqflags.h>
#include <asm/uaccess.h>
#include "i2c-core.h"
@@ -868,26 +870,48 @@ module_exit(i2c_exit);
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
+ int (*xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
- if (adap->algo->master_xfer) {
-#ifdef DEBUG
- for (ret = 0; ret < num; ret++) {
- dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
- "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
- ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
- (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
+ if (in_atomic() || irqs_disabled()) {
+ if (adap->algo->master_nosleep_xfer == NULL) {
+ dev_dbg(&adap->dev, "algo has no nosleep xfer");
+ return -EINVAL;
}
-#endif
-
- mutex_lock_nested(&adap->bus_lock, adap->level);
- ret = adap->algo->master_xfer(adap,msgs,num);
- mutex_unlock(&adap->bus_lock);
+ ret = mutex_trylock(&adap->bus_lock);
+ if (ret) {
+ /* We get bus_lock, no ongoing I2C activity on
+ the Linux side */
+ ret = adap->algo->master_nosleep_xfer(adap, msgs, num);
+ mutex_unlock(&adap->bus_lock);
+ return ret;
+ } else {
+ /* I2C activity is ongoing. */
+ return -EAGAIN;
+ }
+ }
- return ret;
+ if (adap->use_nosleep_xfer && adap->algo->master_nosleep_xfer) {
+ xfer = adap->algo->master_nosleep_xfer;
+ } else if (adap->algo->master_xfer) {
+ xfer = adap->algo->master_xfer;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
+
+#ifdef DEBUG
+ for (ret = 0; ret < num; ret++)
+ dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
+ "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
+ ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
+ (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
+#endif
+
+ mutex_lock_nested(&adap->bus_lock, adap->level);
+ ret = xfer(adap, msgs, num);
+ mutex_unlock(&adap->bus_lock);
+
+ return ret;
}
EXPORT_SYMBOL(i2c_transfer);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index a100c9f..bf4dca3 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -290,6 +290,10 @@ struct i2c_algorithm {
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data);
+ /* PIO xfer can be used in no-interrupts context */
+ int (*master_nosleep_xfer)(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num);
+
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
@@ -304,6 +308,7 @@ struct i2c_adapter {
unsigned int class;
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
+ int use_nosleep_xfer;
/* --- administration stuff. */
int (*client_register)(struct i2c_client *);
--
Sincerely yours,
Mike.
More information about the i2c
mailing list