diff options
-rw-r--r-- | drivers/i2c/chips/pcf50633.c | 153 | ||||
-rw-r--r-- | include/linux/pcf50633.h | 25 |
2 files changed, 129 insertions, 49 deletions
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c index df6c1e69403..568369141f0 100644 --- a/drivers/i2c/chips/pcf50633.c +++ b/drivers/i2c/chips/pcf50633.c @@ -206,7 +206,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) & @@ -462,9 +462,14 @@ 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) +static void +configure_pmu_for_charger(struct pcf50633_data *pcf, + void *unused, 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); @@ -512,16 +517,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); @@ -531,6 +536,64 @@ static void add_request_to_adc_queue(struct pcf50633_data *pcf, trigger_next_adc_job_if_any(pcf); } +static void +__pcf50633_adc_sync_read_callback(struct pcf50633_data *pcf, void *param, int result) +{ + struct adc_request *req; + + /*We know here that the passed param is an adc_request object */ + req = (struct adc_request *)param; + + req->result = result; + complete(&req->completion); +} + +int pcf50633_adc_sync_read(struct pcf50633_data *pcf, int mux, int avg) +{ + + struct adc_request *req; + int result; + + /* req is freed when the result is ready, in pcf50633_work*/ + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->mux = mux; + req->avg = avg; + req->callback = __pcf50633_adc_sync_read_callback; + req->callback_param = req; + init_completion(&req->completion); + + adc_add_request_to_queue(pcf, req); + + wait_for_completion(&req->completion); + result = req->result; + + return result; +} + +int pcf50633_adc_async_read(struct pcf50633_data *pcf, int mux, int avg, + void (*callback)(struct pcf50633_data *, void *,int), + void *callback_param) +{ + struct adc_request *req; + + /* req is freed when the result is ready, in pcf50633_work*/ + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->mux = mux; + req->avg = avg; + req->callback = callback; + req->callback_param = callback_param; + + 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 @@ -651,8 +714,10 @@ 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)pcf50633_adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1, + PCF50633_ADCC1_AVERAGE_16, + configure_pmu_for_charger, + NULL); goto bail; } @@ -675,6 +740,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; @@ -790,8 +856,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)pcf50633_adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1, + PCF50633_ADCC1_AVERAGE_16, + configure_pmu_for_charger, NULL); pcf->coldplug_done = 1; } @@ -829,8 +896,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)pcf50633_adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1, + PCF50633_ADCC1_AVERAGE_16, + configure_pmu_for_charger, NULL); } if (pcfirq[0] & PCF50633_INT1_USBREM && !(pcfirq[0] & PCF50633_INT1_USBINS)) { @@ -855,8 +923,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)pcf50633_adc_async_read(pcf, PCF50633_ADCC1_MUX_ADCIN1, + PCF50633_ADCC1_AVERAGE_16, + configure_pmu_for_charger, NULL); } } if (pcfirq[0] & PCF50633_INT1_ALARM) { @@ -1007,21 +1076,11 @@ 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]; + req->callback(pcf, req->callback_param, + adc_read_result(pcf)); + kfree(req); - 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; - } trigger_next_adc_job_if_any(pcf); } if (pcfirq[2] & PCF50633_INT3_ONKEY1S) { @@ -1198,22 +1257,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 = pcf50633_adc_sync_read(pcf, PCF50633_ADCC1_MUX_BATSNS_RES, PCF50633_ADCC1_AVERAGE_16); - while ((count--) && (pcf->flag_bat_voltage_read < 0)) - msleep(1); - - if (count < 0) { /* timeout somehow */ - DEBUGPC("pcf50633_battvolt timeout :-(\n"); - return -1; - } + if (ret < 0) + return ret; - 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, @@ -1829,6 +1883,8 @@ static ssize_t show_charger_type(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct pcf50633_data *pcf = i2c_get_clientdata(client); + int adc_raw_result, charger_type; + static const char *names_charger_type[] = { [CHARGER_TYPE_NONE] = "none", [CHARGER_TYPE_HOSTUSB] = "host/500mA usb", @@ -1842,8 +1898,11 @@ static ssize_t show_charger_type(struct device *dev, }; int mode = pcf50633_reg_read(pcf, PCF50633_REG_MBCC7) & PCF56033_MBCC7_USB_MASK; + adc_raw_result = pcf50633_adc_sync_read(pcf, PCF50633_ADCC1_MUX_ADCIN1, + PCF50633_ADCC1_AVERAGE_16); + charger_type = interpret_charger_type_from_adc(pcf, adc_raw_result); return sprintf(buf, "%s mode %s\n", - names_charger_type[pcf->charger_type], + names_charger_type[charger_type], names_charger_modes[mode]); } @@ -1872,8 +1931,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 = pcf50633_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); diff --git a/include/linux/pcf50633.h b/include/linux/pcf50633.h index ab73cb99ad0..2968191d0a0 100644 --- a/include/linux/pcf50633.h +++ b/include/linux/pcf50633.h @@ -109,6 +109,14 @@ extern int pcf50633_onoff_set(struct pcf50633_data *pcf, enum pcf50633_regulator_id reg, int on); +extern int +pcf50633_adc_async_read(struct pcf50633_data *pcf, int mux, int avg, + void (*callback)(struct pcf50633_data *, void *, int), + void *callback_param); + +extern int +pcf50633_adc_sync_read(struct pcf50633_data *pcf, int mux, int avg); + extern void pcf50633_backlight_resume(struct pcf50633_data *pcf); @@ -532,6 +540,17 @@ enum pcf50633_reg_mbcs3 { PCF50633_MBCS3_VRES = 0x80, /* 1: Vbat > Vth(RES) */ }; +struct adc_request { + int mux; + int avg; + int result; + void (*callback)(struct pcf50633_data *, void *, int); + void *callback_param; + + /* Used in case of sync requests */ + struct completion completion; +}; + struct pcf50633_data { struct i2c_client *client; struct pcf50633_platform_data *pdata; @@ -575,14 +594,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 */ |