2010-02-23 11:08:23

by Mark Brown

[permalink] [raw]
Subject: [PATCH 1/2] mfd: Use completion interrupt for WM835x AUXADC

Use the completion interrupt generated by the device rather than
polling for conversions to complete. As a backup we still check
the state of the AUXADC if we don't get a completion, mostly for
systems that don't have the WM8350 interrupt infrastructure hooked
up.

Signed-off-by: Mark Brown <[email protected]>
---
drivers/mfd/wm8350-core.c | 35 +++++++++++++++++++++++++++++------
include/linux/mfd/wm8350/core.h | 2 ++
2 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 9a970bd..bd75807 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -339,7 +339,6 @@ EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
{
u16 reg, result = 0;
- int tries = 5;

if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
return -EINVAL;
@@ -363,12 +362,13 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
reg |= 1 << channel | WM8350_AUXADC_POLL;
wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);

- do {
- schedule_timeout_interruptible(1);
- reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
- } while ((reg & WM8350_AUXADC_POLL) && --tries);
+ /* We ignore the result of the completion and just check for a
+ * conversion result, allowing us to soldier on if the IRQ
+ * infrastructure is not set up for the chip. */
+ wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5));

- if (!tries)
+ reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
+ if (reg & WM8350_AUXADC_POLL)
dev_err(wm8350->dev, "adc chn %d read timeout\n", channel);
else
result = wm8350_reg_read(wm8350,
@@ -385,6 +385,15 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
}
EXPORT_SYMBOL_GPL(wm8350_read_auxadc);

+static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
+{
+ struct wm8350 *wm8350 = irq_data;
+
+ complete(&wm8350->auxadc_done);
+
+ return IRQ_HANDLED;
+}
+
/*
* Cache is always host endian.
*/
@@ -682,11 +691,22 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
}

mutex_init(&wm8350->auxadc_mutex);
+ init_completion(&wm8350->auxadc_done);

ret = wm8350_irq_init(wm8350, irq, pdata);
if (ret < 0)
goto err;

+ if (wm8350->irq_base) {
+ ret = request_threaded_irq(wm8350->irq_base +
+ WM8350_IRQ_AUXADC_DATARDY,
+ NULL, wm8350_auxadc_irq, 0,
+ "auxadc", wm8350);
+ if (ret < 0)
+ dev_warn(wm8350->dev,
+ "Failed to request AUXADC IRQ: %d\n", ret);
+ }
+
if (pdata && pdata->init) {
ret = pdata->init(wm8350);
if (ret != 0) {
@@ -736,6 +756,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
platform_device_unregister(wm8350->gpio.pdev);
platform_device_unregister(wm8350->codec.pdev);

+ if (wm8350->irq_base)
+ free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
+
wm8350_irq_exit(wm8350);

kfree(wm8350->reg_cache);
diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h
index fae08aa..98fcc97 100644
--- a/include/linux/mfd/wm8350/core.h
+++ b/include/linux/mfd/wm8350/core.h
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
+#include <linux/completion.h>

#include <linux/mfd/wm8350/audio.h>
#include <linux/mfd/wm8350/gpio.h>
@@ -621,6 +622,7 @@ struct wm8350 {
u16 *reg_cache;

struct mutex auxadc_mutex;
+ struct completion auxadc_done;

/* Interrupt handling */
struct mutex irq_lock;
--
1.7.0


2010-02-23 11:08:24

by Mark Brown

[permalink] [raw]
Subject: [PATCH 2/2] mfd: Use completion interrupt for WM831x AUXADC

Use the completion interrupt generated by the device rather than
polling for conversions to complete. As a backup we still check
the status of the AUXADC if we don't get a completion, mostly for
systems that don't have the WM831x interrupt infrastructure hooked
up.

Also reduce the timeout for completion of conversions to 5ms from
the previous 10ms, the lower timeout should be sufficient.

Signed-off-by: Mark Brown <[email protected]>
---
drivers/mfd/wm831x-core.c | 36 +++++++++++++++++++++++++++++-------
include/linux/mfd/wm831x/core.h | 2 ++
2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index c428d9f..07101e9 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -321,7 +321,6 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
*/
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
{
- int tries = 10;
int ret, src;

mutex_lock(&wm831x->auxadc_lock);
@@ -349,13 +348,14 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
goto disable;
}

- do {
- msleep(1);
+ /* Ignore the result to allow us to soldier on without IRQ hookup */
+ wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5));

- ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
- if (ret < 0)
- ret = WM831X_AUX_CVT_ENA;
- } while ((ret & WM831X_AUX_CVT_ENA) && --tries);
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
+ goto disable;
+ }

if (ret & WM831X_AUX_CVT_ENA) {
dev_err(wm831x->dev, "Timed out reading AUXADC\n");
@@ -390,6 +390,15 @@ out:
}
EXPORT_SYMBOL_GPL(wm831x_auxadc_read);

+static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
+{
+ struct wm831x *wm831x = irq_data;
+
+ complete(&wm831x->auxadc_done);
+
+ return IRQ_HANDLED;
+}
+
/**
* wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
*
@@ -1411,6 +1420,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
mutex_init(&wm831x->auxadc_lock);
+ init_completion(&wm831x->auxadc_done);
dev_set_drvdata(wm831x->dev, wm831x);

ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@@ -1523,6 +1533,16 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
if (ret != 0)
goto err;

+ if (wm831x->irq_base) {
+ ret = request_threaded_irq(wm831x->irq_base +
+ WM831X_IRQ_AUXADC_DATA,
+ NULL, wm831x_auxadc_irq, 0,
+ "auxadc", wm831x);
+ if (ret < 0)
+ dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
+ ret);
+ }
+
/* The core device is up, instantiate the subdevices. */
switch (parent) {
case WM8310:
@@ -1593,6 +1613,8 @@ static void wm831x_device_exit(struct wm831x *wm831x)
{
wm831x_otp_exit(wm831x);
mfd_remove_devices(wm831x->dev);
+ if (wm831x->irq_base)
+ free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
wm831x_irq_exit(wm831x);
kfree(wm831x);
}
diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
index 53580b5..5915f6e 100644
--- a/include/linux/mfd/wm831x/core.h
+++ b/include/linux/mfd/wm831x/core.h
@@ -15,6 +15,7 @@
#ifndef __MFD_WM831X_CORE_H__
#define __MFD_WM831X_CORE_H__

+#include <linux/completion.h>
#include <linux/interrupt.h>

/*
@@ -261,6 +262,7 @@ struct wm831x {
int num_gpio;

struct mutex auxadc_lock;
+ struct completion auxadc_done;

/* The WM831x has a security key blocking access to certain
* registers. The mutex is taken by the accessors for locking
--
1.7.0

2010-02-28 23:03:14

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 1/2] mfd: Use completion interrupt for WM835x AUXADC

Hi Mark,

On Tue, Feb 23, 2010 at 11:08:05AM +0000, Mark Brown wrote:
> Use the completion interrupt generated by the device rather than
> polling for conversions to complete. As a backup we still check
> the state of the AUXADC if we don't get a completion, mostly for
> systems that don't have the WM8350 interrupt infrastructure hooked
> up.
Patch applied, many thanks.

Cheers,
Samuel.


> Signed-off-by: Mark Brown <[email protected]>
> ---
> drivers/mfd/wm8350-core.c | 35 +++++++++++++++++++++++++++++------
> include/linux/mfd/wm8350/core.h | 2 ++
> 2 files changed, 31 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
> index 9a970bd..bd75807 100644
> --- a/drivers/mfd/wm8350-core.c
> +++ b/drivers/mfd/wm8350-core.c
> @@ -339,7 +339,6 @@ EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
> int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
> {
> u16 reg, result = 0;
> - int tries = 5;
>
> if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
> return -EINVAL;
> @@ -363,12 +362,13 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
> reg |= 1 << channel | WM8350_AUXADC_POLL;
> wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);
>
> - do {
> - schedule_timeout_interruptible(1);
> - reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
> - } while ((reg & WM8350_AUXADC_POLL) && --tries);
> + /* We ignore the result of the completion and just check for a
> + * conversion result, allowing us to soldier on if the IRQ
> + * infrastructure is not set up for the chip. */
> + wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5));
>
> - if (!tries)
> + reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
> + if (reg & WM8350_AUXADC_POLL)
> dev_err(wm8350->dev, "adc chn %d read timeout\n", channel);
> else
> result = wm8350_reg_read(wm8350,
> @@ -385,6 +385,15 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
> }
> EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
>
> +static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
> +{
> + struct wm8350 *wm8350 = irq_data;
> +
> + complete(&wm8350->auxadc_done);
> +
> + return IRQ_HANDLED;
> +}
> +
> /*
> * Cache is always host endian.
> */
> @@ -682,11 +691,22 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
> }
>
> mutex_init(&wm8350->auxadc_mutex);
> + init_completion(&wm8350->auxadc_done);
>
> ret = wm8350_irq_init(wm8350, irq, pdata);
> if (ret < 0)
> goto err;
>
> + if (wm8350->irq_base) {
> + ret = request_threaded_irq(wm8350->irq_base +
> + WM8350_IRQ_AUXADC_DATARDY,
> + NULL, wm8350_auxadc_irq, 0,
> + "auxadc", wm8350);
> + if (ret < 0)
> + dev_warn(wm8350->dev,
> + "Failed to request AUXADC IRQ: %d\n", ret);
> + }
> +
> if (pdata && pdata->init) {
> ret = pdata->init(wm8350);
> if (ret != 0) {
> @@ -736,6 +756,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
> platform_device_unregister(wm8350->gpio.pdev);
> platform_device_unregister(wm8350->codec.pdev);
>
> + if (wm8350->irq_base)
> + free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
> +
> wm8350_irq_exit(wm8350);
>
> kfree(wm8350->reg_cache);
> diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h
> index fae08aa..98fcc97 100644
> --- a/include/linux/mfd/wm8350/core.h
> +++ b/include/linux/mfd/wm8350/core.h
> @@ -16,6 +16,7 @@
> #include <linux/kernel.h>
> #include <linux/mutex.h>
> #include <linux/interrupt.h>
> +#include <linux/completion.h>
>
> #include <linux/mfd/wm8350/audio.h>
> #include <linux/mfd/wm8350/gpio.h>
> @@ -621,6 +622,7 @@ struct wm8350 {
> u16 *reg_cache;
>
> struct mutex auxadc_mutex;
> + struct completion auxadc_done;
>
> /* Interrupt handling */
> struct mutex irq_lock;
> --
> 1.7.0
>

--
Intel Open Source Technology Centre
http://oss.intel.com/

2010-02-28 23:03:51

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 2/2] mfd: Use completion interrupt for WM831x AUXADC

On Tue, Feb 23, 2010 at 11:08:06AM +0000, Mark Brown wrote:
> Use the completion interrupt generated by the device rather than
> polling for conversions to complete. As a backup we still check
> the status of the AUXADC if we don't get a completion, mostly for
> systems that don't have the WM831x interrupt infrastructure hooked
> up.
Patch applied to my for-next branch, thanks a lot.

Cheers,
Samuel.


> Also reduce the timeout for completion of conversions to 5ms from
> the previous 10ms, the lower timeout should be sufficient.
>
> Signed-off-by: Mark Brown <[email protected]>
> ---
> drivers/mfd/wm831x-core.c | 36 +++++++++++++++++++++++++++++-------
> include/linux/mfd/wm831x/core.h | 2 ++
> 2 files changed, 31 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
> index c428d9f..07101e9 100644
> --- a/drivers/mfd/wm831x-core.c
> +++ b/drivers/mfd/wm831x-core.c
> @@ -321,7 +321,6 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
> */
> int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
> {
> - int tries = 10;
> int ret, src;
>
> mutex_lock(&wm831x->auxadc_lock);
> @@ -349,13 +348,14 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
> goto disable;
> }
>
> - do {
> - msleep(1);
> + /* Ignore the result to allow us to soldier on without IRQ hookup */
> + wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5));
>
> - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
> - if (ret < 0)
> - ret = WM831X_AUX_CVT_ENA;
> - } while ((ret & WM831X_AUX_CVT_ENA) && --tries);
> + ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
> + if (ret < 0) {
> + dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
> + goto disable;
> + }
>
> if (ret & WM831X_AUX_CVT_ENA) {
> dev_err(wm831x->dev, "Timed out reading AUXADC\n");
> @@ -390,6 +390,15 @@ out:
> }
> EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
>
> +static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
> +{
> + struct wm831x *wm831x = irq_data;
> +
> + complete(&wm831x->auxadc_done);
> +
> + return IRQ_HANDLED;
> +}
> +
> /**
> * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
> *
> @@ -1411,6 +1420,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
> mutex_init(&wm831x->io_lock);
> mutex_init(&wm831x->key_lock);
> mutex_init(&wm831x->auxadc_lock);
> + init_completion(&wm831x->auxadc_done);
> dev_set_drvdata(wm831x->dev, wm831x);
>
> ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
> @@ -1523,6 +1533,16 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
> if (ret != 0)
> goto err;
>
> + if (wm831x->irq_base) {
> + ret = request_threaded_irq(wm831x->irq_base +
> + WM831X_IRQ_AUXADC_DATA,
> + NULL, wm831x_auxadc_irq, 0,
> + "auxadc", wm831x);
> + if (ret < 0)
> + dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
> + ret);
> + }
> +
> /* The core device is up, instantiate the subdevices. */
> switch (parent) {
> case WM8310:
> @@ -1593,6 +1613,8 @@ static void wm831x_device_exit(struct wm831x *wm831x)
> {
> wm831x_otp_exit(wm831x);
> mfd_remove_devices(wm831x->dev);
> + if (wm831x->irq_base)
> + free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
> wm831x_irq_exit(wm831x);
> kfree(wm831x);
> }
> diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
> index 53580b5..5915f6e 100644
> --- a/include/linux/mfd/wm831x/core.h
> +++ b/include/linux/mfd/wm831x/core.h
> @@ -15,6 +15,7 @@
> #ifndef __MFD_WM831X_CORE_H__
> #define __MFD_WM831X_CORE_H__
>
> +#include <linux/completion.h>
> #include <linux/interrupt.h>
>
> /*
> @@ -261,6 +262,7 @@ struct wm831x {
> int num_gpio;
>
> struct mutex auxadc_lock;
> + struct completion auxadc_done;
>
> /* The WM831x has a security key blocking access to certain
> * registers. The mutex is taken by the accessors for locking
> --
> 1.7.0
>

--
Intel Open Source Technology Centre
http://oss.intel.com/