mfd: Improve WM831x AUXADC completion handling
Currently completion of WM831x AUXADC conversions is monitored by checking for convertor enable. Due to the mechanism used to ensure data corruption is avoided when reading AUXADC data there may under heavy I/O be a window where this bit has cleared but the conversion results have not been updated. Data availability is only guaranteed after the AUXADC data interrupt has been asserted. Avoid this by always using the interrupt to detect completion. If the chip IRQ is not set up then we poll the IRQ status register for up to 5ms. If it is set up then we rely on the data done interrupt with a vastly increased timeout, failing the conversion if the interrupt is not generated. This also saves a register read when using interrupts. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Родитель
800e69fbd2
Коммит
7cc1392aab
|
@ -322,7 +322,11 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
|
|||
*/
|
||||
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
|
||||
{
|
||||
int ret, src;
|
||||
int ret, src, irq_masked, timeout;
|
||||
|
||||
/* Are we using the interrupt? */
|
||||
irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
|
||||
irq_masked &= WM831X_AUXADC_DATA_EINT;
|
||||
|
||||
mutex_lock(&wm831x->auxadc_lock);
|
||||
|
||||
|
@ -342,6 +346,9 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Clear any notification from a very late arriving interrupt */
|
||||
try_wait_for_completion(&wm831x->auxadc_done);
|
||||
|
||||
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
|
||||
WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
|
||||
if (ret < 0) {
|
||||
|
@ -349,23 +356,47 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
|
|||
goto disable;
|
||||
}
|
||||
|
||||
/* If an interrupt arrived late clean up after it */
|
||||
try_wait_for_completion(&wm831x->auxadc_done);
|
||||
if (irq_masked) {
|
||||
/* If we're not using interrupts then poll the
|
||||
* interrupt status register */
|
||||
timeout = 5;
|
||||
while (timeout) {
|
||||
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);
|
||||
ret = wm831x_reg_read(wm831x,
|
||||
WM831X_INTERRUPT_STATUS_1);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
|
||||
dev_err(wm831x->dev,
|
||||
"ISR 1 read failed: %d\n", ret);
|
||||
goto disable;
|
||||
}
|
||||
|
||||
if (ret & WM831X_AUX_CVT_ENA) {
|
||||
dev_err(wm831x->dev, "Timed out reading AUXADC\n");
|
||||
/* Did it complete? */
|
||||
if (ret & WM831X_AUXADC_DATA_EINT) {
|
||||
wm831x_reg_write(wm831x,
|
||||
WM831X_INTERRUPT_STATUS_1,
|
||||
WM831X_AUXADC_DATA_EINT);
|
||||
break;
|
||||
} else {
|
||||
dev_err(wm831x->dev,
|
||||
"AUXADC conversion timeout\n");
|
||||
ret = -EBUSY;
|
||||
goto disable;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* If we are using interrupts then wait for the
|
||||
* interrupt to complete. Use an extremely long
|
||||
* timeout to handle situations with heavy load where
|
||||
* the notification of the interrupt may be delayed by
|
||||
* threaded IRQ handling. */
|
||||
if (!wait_for_completion_timeout(&wm831x->auxadc_done,
|
||||
msecs_to_jiffies(500))) {
|
||||
dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
|
||||
ret = -EBUSY;
|
||||
goto disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
|
||||
if (ret < 0) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче