2011-06-02 22:18:53 +04:00
/*
* wm831x - auxadc . c - - AUXADC for Wolfson WM831x PMICs
*
* Copyright 2009 - 2011 Wolfson Microelectronics PLC .
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/mfd/core.h>
# include <linux/slab.h>
2011-06-07 14:47:28 +04:00
# include <linux/list.h>
2011-06-02 22:18:53 +04:00
# include <linux/mfd/wm831x/core.h>
# include <linux/mfd/wm831x/pdata.h>
# include <linux/mfd/wm831x/irq.h>
# include <linux/mfd/wm831x/auxadc.h>
# include <linux/mfd/wm831x/otp.h>
# include <linux/mfd/wm831x/regulator.h>
2011-06-07 14:47:28 +04:00
struct wm831x_auxadc_req {
struct list_head list ;
enum wm831x_auxadc input ;
int val ;
struct completion done ;
} ;
static int wm831x_auxadc_read_irq ( struct wm831x * wm831x ,
enum wm831x_auxadc input )
2011-06-02 22:18:53 +04:00
{
2011-06-07 14:47:28 +04:00
struct wm831x_auxadc_req * req ;
int ret ;
bool ena = false ;
req = kzalloc ( sizeof ( * req ) , GFP_KERNEL ) ;
if ( ! req )
return - ENOMEM ;
init_completion ( & req - > done ) ;
req - > input = input ;
req - > val = - ETIMEDOUT ;
mutex_lock ( & wm831x - > auxadc_lock ) ;
/* Enqueue the request */
list_add ( & req - > list , & wm831x - > auxadc_pending ) ;
ena = ! wm831x - > auxadc_active ;
if ( ena ) {
ret = wm831x_set_bits ( wm831x , WM831X_AUXADC_CONTROL ,
WM831X_AUX_ENA , WM831X_AUX_ENA ) ;
if ( ret ! = 0 ) {
dev_err ( wm831x - > dev , " Failed to enable AUXADC: %d \n " ,
ret ) ;
goto out ;
}
}
/* Enable the conversion if not already running */
if ( ! ( wm831x - > auxadc_active & ( 1 < < input ) ) ) {
ret = wm831x_set_bits ( wm831x , WM831X_AUXADC_SOURCE ,
1 < < input , 1 < < input ) ;
if ( ret ! = 0 ) {
dev_err ( wm831x - > dev ,
" Failed to set AUXADC source: %d \n " , ret ) ;
goto out ;
}
wm831x - > auxadc_active | = 1 < < input ;
}
/* We convert at the fastest rate possible */
if ( ena ) {
ret = wm831x_set_bits ( wm831x , WM831X_AUXADC_CONTROL ,
WM831X_AUX_CVT_ENA |
WM831X_AUX_RATE_MASK ,
WM831X_AUX_CVT_ENA |
WM831X_AUX_RATE_MASK ) ;
if ( ret ! = 0 ) {
dev_err ( wm831x - > dev , " Failed to start AUXADC: %d \n " ,
ret ) ;
goto out ;
}
}
mutex_unlock ( & wm831x - > auxadc_lock ) ;
/* Wait for an interrupt */
wait_for_completion_timeout ( & req - > done , msecs_to_jiffies ( 500 ) ) ;
mutex_lock ( & wm831x - > auxadc_lock ) ;
list_del ( & req - > list ) ;
ret = req - > val ;
out :
mutex_unlock ( & wm831x - > auxadc_lock ) ;
kfree ( req ) ;
return ret ;
}
static irqreturn_t wm831x_auxadc_irq ( int irq , void * irq_data )
{
struct wm831x * wm831x = irq_data ;
struct wm831x_auxadc_req * req ;
int ret , input , val ;
ret = wm831x_reg_read ( wm831x , WM831X_AUXADC_DATA ) ;
if ( ret < 0 ) {
dev_err ( wm831x - > dev ,
" Failed to read AUXADC data: %d \n " , ret ) ;
return IRQ_NONE ;
}
input = ( ( ret & WM831X_AUX_DATA_SRC_MASK )
> > WM831X_AUX_DATA_SRC_SHIFT ) - 1 ;
if ( input = = 14 )
input = WM831X_AUX_CAL ;
2011-06-02 22:18:53 +04:00
2011-06-07 14:47:28 +04:00
val = ret & WM831X_AUX_DATA_MASK ;
mutex_lock ( & wm831x - > auxadc_lock ) ;
/* Disable this conversion, we're about to complete all users */
wm831x_set_bits ( wm831x , WM831X_AUXADC_SOURCE ,
1 < < input , 0 ) ;
wm831x - > auxadc_active & = ~ ( 1 < < input ) ;
/* Turn off the entire convertor if idle */
if ( ! wm831x - > auxadc_active )
wm831x_reg_write ( wm831x , WM831X_AUXADC_CONTROL , 0 ) ;
/* Wake up any threads waiting for this request */
list_for_each_entry ( req , & wm831x - > auxadc_pending , list ) {
if ( req - > input = = input ) {
req - > val = val ;
complete ( & req - > done ) ;
}
}
mutex_unlock ( & wm831x - > auxadc_lock ) ;
return IRQ_HANDLED ;
}
static int wm831x_auxadc_read_polled ( struct wm831x * wm831x ,
enum wm831x_auxadc input )
{
int ret , src , timeout ;
2011-06-02 22:18:53 +04:00
mutex_lock ( & wm831x - > auxadc_lock ) ;
ret = wm831x_set_bits ( wm831x , WM831X_AUXADC_CONTROL ,
WM831X_AUX_ENA , WM831X_AUX_ENA ) ;
if ( ret < 0 ) {
dev_err ( wm831x - > dev , " Failed to enable AUXADC: %d \n " , ret ) ;
goto out ;
}
/* We force a single source at present */
src = input ;
ret = wm831x_reg_write ( wm831x , WM831X_AUXADC_SOURCE ,
1 < < src ) ;
if ( ret < 0 ) {
dev_err ( wm831x - > dev , " Failed to set AUXADC source: %d \n " , ret ) ;
goto out ;
}
ret = wm831x_set_bits ( wm831x , WM831X_AUXADC_CONTROL ,
WM831X_AUX_CVT_ENA , WM831X_AUX_CVT_ENA ) ;
if ( ret < 0 ) {
dev_err ( wm831x - > dev , " Failed to start AUXADC: %d \n " , ret ) ;
goto disable ;
}
2011-06-07 14:47:28 +04:00
/* If we're not using interrupts then poll the
* interrupt status register */
timeout = 5 ;
while ( timeout ) {
msleep ( 1 ) ;
2011-06-02 22:18:53 +04:00
2011-06-07 14:47:28 +04:00
ret = wm831x_reg_read ( wm831x ,
WM831X_INTERRUPT_STATUS_1 ) ;
2011-06-02 22:18:53 +04:00
if ( ret < 0 ) {
dev_err ( wm831x - > dev ,
2011-06-07 14:47:28 +04:00
" ISR 1 read failed: %d \n " , ret ) ;
2011-06-02 22:18:53 +04:00
goto disable ;
}
2011-06-07 14:47:28 +04:00
/* 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 " ) ;
2011-06-02 22:18:53 +04:00
ret = - EBUSY ;
goto disable ;
}
}
2011-06-07 14:47:28 +04:00
ret = wm831x_reg_read ( wm831x , WM831X_AUXADC_DATA ) ;
if ( ret < 0 ) {
dev_err ( wm831x - > dev ,
" Failed to read AUXADC data: %d \n " , ret ) ;
goto disable ;
}
src = ( ( ret & WM831X_AUX_DATA_SRC_MASK )
2011-06-02 22:18:53 +04:00
> > WM831X_AUX_DATA_SRC_SHIFT ) - 1 ;
if ( src = = 14 )
src = WM831X_AUX_CAL ;
if ( src ! = input ) {
dev_err ( wm831x - > dev , " Data from source %d not %d \n " ,
src , input ) ;
ret = - EINVAL ;
} else {
2011-06-07 14:47:28 +04:00
ret & = WM831X_AUX_DATA_MASK ;
2011-06-02 22:18:53 +04:00
}
disable :
wm831x_set_bits ( wm831x , WM831X_AUXADC_CONTROL , WM831X_AUX_ENA , 0 ) ;
out :
mutex_unlock ( & wm831x - > auxadc_lock ) ;
return ret ;
}
2011-06-07 14:47:28 +04:00
/**
* wm831x_auxadc_read : Read a value from the WM831x AUXADC
*
* @ wm831x : Device to read from .
* @ input : AUXADC input to read .
*/
int wm831x_auxadc_read ( struct wm831x * wm831x , enum wm831x_auxadc input )
2011-06-02 22:18:53 +04:00
{
2011-06-07 14:47:28 +04:00
return wm831x - > auxadc_read ( wm831x , input ) ;
2011-06-02 22:18:53 +04:00
}
2011-06-07 14:47:28 +04:00
EXPORT_SYMBOL_GPL ( wm831x_auxadc_read ) ;
2011-06-02 22:18:53 +04:00
/**
* wm831x_auxadc_read_uv : Read a voltage from the WM831x AUXADC
*
* @ wm831x : Device to read from .
* @ input : AUXADC input to read .
*/
int wm831x_auxadc_read_uv ( struct wm831x * wm831x , enum wm831x_auxadc input )
{
int ret ;
ret = wm831x_auxadc_read ( wm831x , input ) ;
if ( ret < 0 )
return ret ;
ret * = 1465 ;
return ret ;
}
EXPORT_SYMBOL_GPL ( wm831x_auxadc_read_uv ) ;
void wm831x_auxadc_init ( struct wm831x * wm831x )
{
int ret ;
mutex_init ( & wm831x - > auxadc_lock ) ;
2011-06-07 14:47:28 +04:00
INIT_LIST_HEAD ( & wm831x - > auxadc_pending ) ;
2012-05-15 01:14:24 +04:00
if ( wm831x - > irq ) {
2011-06-07 14:47:28 +04:00
wm831x - > auxadc_read = wm831x_auxadc_read_irq ;
2011-06-02 22:18:53 +04:00
2012-05-15 01:14:24 +04:00
ret = request_threaded_irq ( wm831x_irq ( wm831x ,
WM831X_IRQ_AUXADC_DATA ) ,
2011-06-02 22:18:53 +04:00
NULL , wm831x_auxadc_irq , 0 ,
" auxadc " , wm831x ) ;
2011-06-07 14:47:28 +04:00
if ( ret < 0 ) {
2011-06-02 22:18:53 +04:00
dev_err ( wm831x - > dev , " AUXADC IRQ request failed: %d \n " ,
ret ) ;
2011-06-07 14:47:28 +04:00
wm831x - > auxadc_read = NULL ;
}
2011-06-02 22:18:53 +04:00
}
2011-06-07 14:47:28 +04:00
if ( ! wm831x - > auxadc_read )
wm831x - > auxadc_read = wm831x_auxadc_read_polled ;
2011-06-02 22:18:53 +04:00
}