[UPSTREAM] Attempt at improving ADC access
Balaji Rao
balajirrao at openmoko.org
Wed Oct 15 23:02:00 CEST 2008
Hi,
Here's an attempt to improve ADC access in the pcf50633 driver.
I've introduced a method for synchronously reading from the ADC.
The adc external interface consists only of adc_async_read and
adc_sync_read.
Also, don't the queue head/tail manipulation functions require locking ?
Please review.
pcf50633.c | 154 ++++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 107 insertions(+), 47 deletions(-)
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 13b3158..68bc8c0 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -117,6 +117,21 @@ enum pcf50633_suspend_states {
PCF50633_SS_COMPLETED_RESUME,
};
+#define ADC_REQUEST_TYPE_SYNC 1
+#define ADC_REQUEST_TYPE_ASYNC 2
+
+struct adc_request {
+ int mux;
+ int avg;
+ int request_type;
+
+ /* For async reads */
+ void (*callback)(struct pcf50633_data *pcf, int result);
+
+ /* for sync reads */
+ struct completion completion;
+ int result;
+};
struct pcf50633_data {
struct i2c_client *client;
@@ -160,14 +175,10 @@ struct pcf50633_data {
int coldplug_done; /* cleared by probe, set by first work service */
int flag_bat_voltage_read; /* ipc to /sys batt voltage read func */
- int charger_adc_result_raw;
- enum charger_type charger_type;
-
/* we have a FIFO of ADC measurement requests that are used only by
* the workqueue service code after the ADC completion interrupt
*/
- int adc_queue_mux[MAX_ADC_FIFO_DEPTH]; /* which ADC input to use */
- int adc_queue_avg[MAX_ADC_FIFO_DEPTH]; /* amount of averaging */
+ struct adc_request *adc_queue[MAX_ADC_FIFO_DEPTH]; /* amount of averaging */
int adc_queue_head; /* head owned by foreground code */
int adc_queue_tail; /* tail owned by service code */
@@ -299,7 +310,7 @@ static void async_adc_read_setup(struct pcf50633_data *pcf,
}
-static u_int16_t async_adc_complete(struct pcf50633_data *pcf)
+static u_int16_t adc_read_result(struct pcf50633_data *pcf)
{
u_int16_t ret = (__reg_read(pcf, PCF50633_REG_ADCS1) << 2) |
(__reg_read(pcf, PCF50633_REG_ADCS3) &
@@ -556,8 +567,12 @@ static int interpret_charger_type_from_adc(struct pcf50633_data *pcf,
static void configure_pmu_for_charger(struct pcf50633_data *pcf,
- enum charger_type type)
+ int adc_result_raw)
{
+ int type;
+
+ type = interpret_charger_type_from_adc(
+ pcf, adc_result_raw);
switch (type) {
case CHARGER_TYPE_NONE:
pcf50633_usb_curlim_set(pcf, 0);
@@ -605,16 +620,16 @@ static void trigger_next_adc_job_if_any(struct pcf50633_data *pcf)
if (pcf->adc_queue_head == pcf->adc_queue_tail)
return;
async_adc_read_setup(pcf,
- pcf->adc_queue_mux[pcf->adc_queue_tail],
- pcf->adc_queue_avg[pcf->adc_queue_tail]);
+ pcf->adc_queue[pcf->adc_queue_tail]->mux,
+ pcf->adc_queue[pcf->adc_queue_tail]->avg);
}
-static void add_request_to_adc_queue(struct pcf50633_data *pcf,
- int mux, int avg)
+
+static void
+adc_add_request_to_queue(struct pcf50633_data *pcf, struct adc_request *req)
{
int old_head = pcf->adc_queue_head;
- pcf->adc_queue_mux[pcf->adc_queue_head] = mux;
- pcf->adc_queue_avg[pcf->adc_queue_head] = avg;
+ pcf->adc_queue[pcf->adc_queue_head] = req;
pcf->adc_queue_head = (pcf->adc_queue_head + 1) &
(MAX_ADC_FIFO_DEPTH - 1);
@@ -624,6 +639,49 @@ static void add_request_to_adc_queue(struct pcf50633_data *pcf,
trigger_next_adc_job_if_any(pcf);
}
+static int adc_sync_read(struct pcf50633_data *pcf, int mux, int avg)
+{
+
+ struct adc_request *req;
+ int result;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->mux = mux;
+ req->avg = avg;
+ req->request_type = ADC_REQUEST_TYPE_SYNC;
+ init_completion(&req->completion);
+
+ adc_add_request_to_queue(pcf, req);
+
+ wait_for_completion(&req->completion);
+ result = req->result;
+ kfree(req);
+
+ return result;
+}
+
+static int adc_async_read(struct pcf50633_data *pcf, int mux, int avg,
+ void (*callback)(struct pcf50633_data *, int))
+{
+ struct adc_request *req;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->mux = mux;
+ req->avg = avg;
+ req->callback = callback;
+ req->request_type = ADC_REQUEST_TYPE_ASYNC;
+
+ adc_add_request_to_queue(pcf, req);
+
+ return 0;
+}
+
/*
* we get run to handle servicing the async notification from USB stack that
* we got enumerated and allowed to draw a particular amount of current
@@ -744,8 +802,9 @@ static void pcf50633_work_nobat(struct work_struct *work)
pcf->jiffies_last_bat_ins = jiffies;
/* figure out our charging stance */
- add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
- PCF50633_ADCC1_AVERAGE_16);
+ (void)adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1,
+ PCF50633_ADCC1_AVERAGE_16,
+ configure_pmu_for_charger);
goto bail;
}
@@ -768,6 +827,7 @@ static void pcf50633_work(struct work_struct *work)
u_int8_t pcfirq[5];
int ret;
int tail;
+ struct adc_request *req;
mutex_lock(&pcf->working_lock);
pcf->working = 1;
@@ -873,8 +933,9 @@ static void pcf50633_work(struct work_struct *work)
}
/* figure out our initial charging stance */
- add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
- PCF50633_ADCC1_AVERAGE_16);
+ (void)adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1,
+ PCF50633_ADCC1_AVERAGE_16,
+ configure_pmu_for_charger);
pcf->coldplug_done = 1;
}
@@ -912,8 +973,9 @@ static void pcf50633_work(struct work_struct *work)
PCF50633_FEAT_MBC, PMU_EVT_USB_INSERT);
msleep(500); /* debounce, allow to see any ID resistor */
/* completion irq will figure out our charging stance */
- add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
- PCF50633_ADCC1_AVERAGE_16);
+ (void)adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1,
+ PCF50633_ADCC1_AVERAGE_16,
+ configure_pmu_for_charger);
}
if (pcfirq[0] & PCF50633_INT1_USBREM &&
!(pcfirq[0] & PCF50633_INT1_USBINS)) {
@@ -938,8 +1000,9 @@ static void pcf50633_work(struct work_struct *work)
pcf->last_curlim_set = 0;
/* completion irq will figure out our charging stance */
- add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_ADCIN1,
- PCF50633_ADCC1_AVERAGE_16);
+ (void)adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1,
+ PCF50633_ADCC1_AVERAGE_16,
+ configure_pmu_for_charger);
}
}
if (pcfirq[0] & PCF50633_INT1_ALARM) {
@@ -1080,21 +1143,17 @@ static void pcf50633_work(struct work_struct *work)
tail = pcf->adc_queue_tail;
pcf->adc_queue_tail = (pcf->adc_queue_tail + 1) &
(MAX_ADC_FIFO_DEPTH - 1);
+ req = pcf->adc_queue[tail];
+ WARN_ON(!pcf);
- switch (pcf->adc_queue_mux[tail]) {
- case PCF50633_ADCC1_MUX_BATSNS_RES: /* battery voltage */
- pcf->flag_bat_voltage_read = async_adc_complete(pcf);
- break;
- case PCF50633_ADCC1_MUX_ADCIN1: /* charger type */
- pcf->charger_adc_result_raw = async_adc_complete(pcf);
- pcf->charger_type = interpret_charger_type_from_adc(
- pcf, pcf->charger_adc_result_raw);
- configure_pmu_for_charger(pcf, pcf->charger_type);
- break;
- default:
- async_adc_complete(pcf);
- break;
+ if (req->request_type == ADC_REQUEST_TYPE_ASYNC) {
+ req->callback(pcf, adc_read_result(pcf));
+ kfree(req);
+ } else {
+ req->result = adc_read_result(pcf);
+ complete(&req->completion);
}
+
trigger_next_adc_job_if_any(pcf);
}
if (pcfirq[2] & PCF50633_INT3_ONKEY1S) {
@@ -1271,22 +1330,17 @@ static u_int8_t battvolt_scale(u_int16_t battvolt)
u_int16_t pcf50633_battvolt(struct pcf50633_data *pcf)
{
- int count = 10;
+ int ret;
- pcf->flag_bat_voltage_read = -1;
- add_request_to_adc_queue(pcf, PCF50633_ADCC1_MUX_BATSNS_RES,
+ ret = adc_sync_read(pcf, PCF50633_ADCC1_MUX_BATSNS_RES,
PCF50633_ADCC1_AVERAGE_16);
- while ((count--) && (pcf->flag_bat_voltage_read < 0))
- msleep(1);
+ if (ret < 0)
+ return ret;
- if (count < 0) { /* timeout somehow */
- DEBUGPC("pcf50633_battvolt timeout :-(\n");
- return -1;
- }
-
- return adc_to_batt_millivolts(pcf->flag_bat_voltage_read);
+ return adc_to_batt_millivolts(ret);
}
+
EXPORT_SYMBOL_GPL(pcf50633_battvolt);
static ssize_t show_battvolt(struct device *dev, struct device_attribute *attr,
@@ -1918,7 +1972,7 @@ static ssize_t show_charger_type(struct device *dev,
int mode = pcf50633_reg_read(pcf, PCF50633_REG_MBCC7) & PCF56033_MBCC7_USB_MASK;
return sprintf(buf, "%s mode %s\n",
- names_charger_type[pcf->charger_type],
+/*FIXME*/ names_charger_type[0],
names_charger_modes[mode]);
}
@@ -1947,8 +2001,14 @@ static ssize_t show_charger_adc(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
struct pcf50633_data *pcf = i2c_get_clientdata(client);
+ int result;
+
+ result = adc_sync_read(pcf, PCF50633_ADCC1_MUX_ADCIN1,
+ PCF50633_ADCC1_AVERAGE_16);
+ if (result < 0)
+ return result;
- return sprintf(buf, "%d\n", pcf->charger_adc_result_raw);
+ return sprintf(buf, "%d\n", result);
}
static DEVICE_ATTR(charger_adc, 0444, show_charger_adc, NULL);
More information about the openmoko-kernel
mailing list