[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