On Tuesday 16 June 2015 02:48 PM, Wolfram Sang wrote:
Any update on this?
Not yet.
a) there was no code to look at
Actually its simple question, whether we can call i2c_transfer in
pm_power_off fn, where interupts are disabled and i2c_transfer fn may
sleep.
Just to illustrate my point,
I just quickly created something for you. Correct me if I am wrong here.
pm_power_off Usecase:
=====
File: arch/arm64/kernel/process.c
void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
pm_power_off();
}
Dummy pm_power_off Implementation:
=====
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index 03b70f8..e364a2a 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1170,7 +1170,10 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num
i2c_pxa_enable(i2c, true);
for (i = adap->retries; i >= 0; i--) {
- ret = i2c_pxa_do_xfer(i2c, msgs, num);
+ if (msgs[0].flags & I2C_M_PIO)
+ ret = i2c_pxa_do_pio_xfer(i2c, msgs, num);
+ else
+ ret = i2c_pxa_do_xfer(i2c, msgs, num);
if (ret != I2C_RETRY)
goto out;
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index 0464e2d..2d7b11b 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -488,6 +488,52 @@ static void pm800_pages_exit(struct pm80x_chip *chip)
i2c_unregister_device(subchip->gpadc_page);
}
+static struct pm80x_chip *pm80x_chip_g;
+
+#define PM800_SW_PDOWN (1 << 5)
+
+static void pm800_power_off(void)
+{
+ u8 data, buf[2];
+ struct i2c_msg msgs[2];
+ struct i2c_client *client = pm80x_chip_g->client;
+
+ pr_info("turning off power....\n");
+
+ /*
+ * pm_power_off fn get called at the end of machine_power_off(),
+ * so at this stage the irqs are disabled, so we have to use
+ * PIO mode of I2C transaction for both read and write.
+ */
+ /* Read register first */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = I2C_M_PIO;
+ msgs[0].len = 1;
+ msgs[0].buf = buf;
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD | I2C_M_PIO;
+ msgs[1].len = 1;
+ msgs[1].buf = &data;
+
+ buf[0] = PM800_WAKEUP1;
+ if ( __i2c_transfer(client->adapter, msgs, 2) < 0) {
+ pr_err("%s read register fails...\n", __func__);
+ WARN_ON(1);
+ }
+
+ /* issue SW power down */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = I2C_M_PIO;
+ msgs[0].len = 2;
+ msgs[0].buf[0] = PM800_WAKEUP1;
+ msgs[0].buf[1] = data | PM800_SW_PDOWN;
+ if (__i2c_transfer(client->adapter, msgs, 1) < 0) {
+ pr_err("%s write data fails...\n", __func__);
+ WARN_ON(1);
+ }
+}
+
static int device_800_init(struct pm80x_chip *chip,
struct pm80x_platform_data *pdata)
{
@@ -612,6 +658,10 @@ static int pm800_probe(struct i2c_client *client,
if (pdata && pdata->plat_config)
pdata->plat_config(chip, pdata);
+ /* keep global copy, required in power_off fn */
+ pm80x_chip_g = chip;
+ pm_power_off = pm800_power_off;
+
return 0;
err_device_init:
diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h
index 0e949cb..22fda83 100644
--- a/include/uapi/linux/i2c.h
+++ b/include/uapi/linux/i2c.h
@@ -76,6 +76,7 @@ struct i2c_msg {
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if
I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first
received byte */
+#define I2C_M_PIO 0x0200 /* pio mode of transaction */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html