/* * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include #include "../core/pmic.h" #define DEF_ADC_0 0x008000 #define DEF_ADC_3 0x0001c0 #define ADC_NB_AVAILABLE 2 #define MAX_CHANNEL 7 #define MC13892_ADC0_TS_M_LSH 14 #define MC13892_ADC0_TS_M_WID 3 static int pmic_adc_major; static struct class *pmic_adc_class; /* * Maximun allowed variation in the three X/Y co-ordinates acquired from * touch-screen */ #ifdef CONFIG_TOUCH_MXC_DELTA_X #define DELTA_X_MAX CONFIG_TOUCH_MXC_DELTA_X #else #define DELTA_X_MAX 50 #endif #ifdef CONFIG_TOUCH_MXC_DELTA_Y #define DELTA_Y_MAX CONFIG_TOUCH_MXC_DELTA_Y #else #define DELTA_Y_MAX 50 #endif /* * ADC 0 */ #define ADC_WAIT_TSI_0 0x001400 #define ADC_INC 0x030000 #define ADC_BIS 0x800000 #define ADC_CHRGRAW_D5 0x008000 /* * ADC 1 */ #define ADC_EN 0x000001 #define ADC_SGL_CH 0x000002 #define ADC_ADSEL 0x000008 #define ADC_CH_0_POS 5 #define ADC_CH_0_MASK 0x0000E0 #define ADC_CH_1_POS 8 #define ADC_CH_1_MASK 0x000700 #define ADC_DELAY_POS 11 #define ADC_DELAY_MASK 0x07F800 #define ADC_ATO 0x080000 #define ASC_ADC 0x100000 #define ADC_WAIT_TSI_1 0x200001 #define ADC_NO_ADTRIG 0x200000 /* * ADC 2 - 4 */ #define ADD1_RESULT_MASK 0x00000FFC #define ADD2_RESULT_MASK 0x00FFC000 #define ADC_TS_MASK 0x00FFCFFC #define ADC_WCOMP 0x040000 #define ADC_WCOMP_H_POS 0 #define ADC_WCOMP_L_POS 9 #define ADC_WCOMP_H_MASK 0x00003F #define ADC_WCOMP_L_MASK 0x007E00 #define ADC_MODE_MASK 0x00003F #define ADC_INT_BISDONEI 0x02 #define ADC_TSMODE_MASK 0x007000 typedef enum adc_state { ADC_FREE, ADC_USED, ADC_MONITORING, } t_adc_state; typedef enum reading_mode { /*! * Enables lithium cell reading */ M_LITHIUM_CELL = 0x000001, /*! * Enables charge current reading */ M_CHARGE_CURRENT = 0x000002, /*! * Enables battery current reading */ M_BATTERY_CURRENT = 0x000004, } t_reading_mode; typedef struct { /*! * Delay before first conversion */ unsigned int delay; /*! * sets the ATX bit for delay on all conversion */ bool conv_delay; /*! * Sets the single channel mode */ bool single_channel; /*! * Channel selection 1 */ t_channel channel_0; /*! * Channel selection 2 */ t_channel channel_1; /*! * Used to configure ADC mode with t_reading_mode */ t_reading_mode read_mode; /*! * Sets the Touch screen mode */ bool read_ts; /*! * Wait TSI event before touch screen reading */ bool wait_tsi; /*! * Sets CHRGRAW scaling to divide by 5 * Only supported on 2.0 and higher */ bool chrgraw_devide_5; /*! * Return ADC values */ unsigned int value[8]; /*! * Return touch screen values */ t_touch_screen ts_value; } t_adc_param; static int pmic_adc_filter(t_touch_screen *ts_curr); int mc13892_adc_request(bool read_ts); int mc13892_adc_release(int adc_index); t_reading_mode mc13892_set_read_mode(t_channel channel); PMIC_STATUS mc13892_adc_read_ts(t_touch_screen *touch_sample, int wait_tsi); /* internal function */ static void callback_tsi(void *); static void callback_adcdone(void *); static void callback_adcbisdone(void *); static int swait; static int suspend_flag; static wait_queue_head_t suspendq; /* EXPORTED FUNCTIONS */ EXPORT_SYMBOL(pmic_adc_init); EXPORT_SYMBOL(pmic_adc_deinit); EXPORT_SYMBOL(pmic_adc_convert); EXPORT_SYMBOL(pmic_adc_convert_8x); EXPORT_SYMBOL(pmic_adc_set_touch_mode); EXPORT_SYMBOL(pmic_adc_get_touch_mode); EXPORT_SYMBOL(pmic_adc_get_touch_sample); static DECLARE_COMPLETION(adcdone_it); static DECLARE_COMPLETION(adcbisdone_it); static DECLARE_COMPLETION(adc_tsi); static pmic_event_callback_t tsi_event; static pmic_event_callback_t event_adc; static pmic_event_callback_t event_adc_bis; static bool data_ready_adc_1; static bool data_ready_adc_2; static bool adc_ts; static bool wait_ts; static bool monitor_en; static bool monitor_adc; static DECLARE_MUTEX(convert_mutex); static DECLARE_WAIT_QUEUE_HEAD(queue_adc_busy); static t_adc_state adc_dev[2]; static unsigned channel_num[] = { 0, 1, 3, 4, 2, 0, 1, 3, 4, -1, 5, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static bool pmic_adc_ready; int is_pmic_adc_ready() { return pmic_adc_ready; } EXPORT_SYMBOL(is_pmic_adc_ready); static int pmic_adc_suspend(struct platform_device *pdev, pm_message_t state) { suspend_flag = 1; CHECK_ERROR(pmic_write_reg(REG_ADC0, DEF_ADC_0, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC1, 0, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC2, 0, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC3, DEF_ADC_3, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC4, 0, PMIC_ALL_BITS)); return 0; }; static int pmic_adc_resume(struct platform_device *pdev) { /* nothing for mc13892 adc */ unsigned int adc_0_reg, adc_1_reg, reg_mask; suspend_flag = 0; /* let interrupt of TSI again */ adc_0_reg = ADC_WAIT_TSI_0; reg_mask = ADC_WAIT_TSI_0; CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, reg_mask)); adc_1_reg = ADC_WAIT_TSI_1 | (ADC_BIS * adc_ts); CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, PMIC_ALL_BITS)); while (swait > 0) { swait--; wake_up_interruptible(&suspendq); } return 0; }; static void callback_tsi(void *unused) { pr_debug("*** TSI IT mc13892 PMIC_ADC_GET_TOUCH_SAMPLE ***\n"); if (wait_ts) { complete(&adc_tsi); pmic_event_mask(EVENT_TSI); } } static void callback_adcdone(void *unused) { if (data_ready_adc_1) complete(&adcdone_it); } static void callback_adcbisdone(void *unused) { pr_debug("* adcdone bis it callback *\n"); if (data_ready_adc_2) complete(&adcbisdone_it); } static int pmic_adc_filter(t_touch_screen *ts_curr) { unsigned int ydiff, xdiff; unsigned int sample_sumx, sample_sumy; if (ts_curr->contact_resistance == 0) { ts_curr->x_position = 0; ts_curr->y_position = 0; return 0; } ydiff = abs(ts_curr->y_position1 - ts_curr->y_position2); if (ydiff > DELTA_Y_MAX) { pr_debug("pmic_adc_filter: Ret pos y\n"); return -1; } xdiff = abs(ts_curr->x_position1 - ts_curr->x_position2); if (xdiff > DELTA_X_MAX) { pr_debug("mc13892_adc_filter: Ret pos x\n"); return -1; } sample_sumx = ts_curr->x_position1 + ts_curr->x_position2; sample_sumy = ts_curr->y_position1 + ts_curr->y_position2; ts_curr->y_position = sample_sumy / 2; ts_curr->x_position = sample_sumx / 2; return 0; } int pmic_adc_init(void) { unsigned int reg_value = 0, i = 0; if (suspend_flag == 1) return -EBUSY; for (i = 0; i < ADC_NB_AVAILABLE; i++) adc_dev[i] = ADC_FREE; CHECK_ERROR(pmic_write_reg(REG_ADC0, DEF_ADC_0, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC1, 0, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC2, 0, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC3, DEF_ADC_3, PMIC_ALL_BITS)); CHECK_ERROR(pmic_write_reg(REG_ADC4, 0, PMIC_ALL_BITS)); reg_value = 0x001000; data_ready_adc_1 = false; data_ready_adc_2 = false; adc_ts = false; wait_ts = false; monitor_en = false; monitor_adc = false; /* sub to ADCDone IT */ event_adc.param = NULL; event_adc.func = callback_adcdone; CHECK_ERROR(pmic_event_subscribe(EVENT_ADCDONEI, event_adc)); /* sub to ADCDoneBis IT */ event_adc_bis.param = NULL; event_adc_bis.func = callback_adcbisdone; CHECK_ERROR(pmic_event_subscribe(EVENT_ADCBISDONEI, event_adc_bis)); /* sub to Touch Screen IT */ tsi_event.param = NULL; tsi_event.func = callback_tsi; CHECK_ERROR(pmic_event_subscribe(EVENT_TSI, tsi_event)); return PMIC_SUCCESS; } PMIC_STATUS pmic_adc_deinit(void) { CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCDONEI, event_adc)); CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCBISDONEI, event_adc_bis)); CHECK_ERROR(pmic_event_unsubscribe(EVENT_TSI, tsi_event)); return PMIC_SUCCESS; } int mc13892_adc_init_param(t_adc_param * adc_param) { int i = 0; if (suspend_flag == 1) return -EBUSY; adc_param->delay = 0; adc_param->conv_delay = false; adc_param->single_channel = false; adc_param->channel_0 = BATTERY_VOLTAGE; adc_param->channel_1 = BATTERY_VOLTAGE; adc_param->read_mode = 0; adc_param->wait_tsi = 0; adc_param->chrgraw_devide_5 = true; adc_param->read_ts = false; adc_param->ts_value.x_position = 0; adc_param->ts_value.y_position = 0; adc_param->ts_value.contact_resistance = 0; for (i = 0; i <= MAX_CHANNEL; i++) adc_param->value[i] = 0; return 0; } PMIC_STATUS mc13892_adc_convert(t_adc_param * adc_param) { bool use_bis = false; unsigned int adc_0_reg = 0, adc_1_reg = 0, reg_1 = 0, result_reg = 0, i = 0; unsigned int result = 0, temp = 0; pmic_version_t mc13892_ver; pr_debug("mc13892 ADC - mc13892_adc_convert ....\n"); if (suspend_flag == 1) return -EBUSY; if (adc_param->wait_tsi) { /* configure adc to wait tsi interrupt */ INIT_COMPLETION(adc_tsi); /*for ts don't use bis */ /*put ts in interrupt mode */ /* still kep reference? */ adc_0_reg = 0x001400 | (ADC_BIS * 0); pmic_event_unmask(EVENT_TSI); CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, PMIC_ALL_BITS)); /*for ts don't use bis */ adc_1_reg = 0x200001 | (ADC_BIS * 0); CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, PMIC_ALL_BITS)); pr_debug("wait tsi ....\n"); wait_ts = true; wait_for_completion_interruptible(&adc_tsi); wait_ts = false; } down(&convert_mutex); use_bis = mc13892_adc_request(adc_param->read_ts); if (use_bis < 0) { pr_debug("process has received a signal and got interrupted\n"); return -EINTR; } /* CONFIGURE ADC REG 0 */ adc_0_reg = 0; adc_1_reg = 0; if (adc_param->read_ts == false) { adc_0_reg = adc_param->read_mode & 0x00003F; /* add auto inc */ adc_0_reg |= ADC_INC; if (use_bis) { /* add adc bis */ adc_0_reg |= ADC_BIS; } mc13892_ver = pmic_get_version(); if (mc13892_ver.revision >= 20) if (adc_param->chrgraw_devide_5) adc_0_reg |= ADC_CHRGRAW_D5; if (adc_param->single_channel) adc_1_reg |= ADC_SGL_CH; if (adc_param->conv_delay) adc_1_reg |= ADC_ATO; if (adc_param->single_channel) adc_1_reg |= ADC_SGL_CH; adc_1_reg |= (adc_param->channel_0 << ADC_CH_0_POS) & ADC_CH_0_MASK; adc_1_reg |= (adc_param->channel_1 << ADC_CH_1_POS) & ADC_CH_1_MASK; } else { adc_0_reg = 0x002400 | (ADC_BIS * use_bis) | ADC_INC; } pr_debug("Write Reg %i = %x\n", REG_ADC0, adc_0_reg); /*Change has been made here */ CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, ADC_INC | ADC_BIS | ADC_CHRGRAW_D5 | 0xfff00ff)); /* CONFIGURE ADC REG 1 */ if (adc_param->read_ts == false) { adc_1_reg |= ADC_NO_ADTRIG; adc_1_reg |= ADC_EN; adc_1_reg |= (adc_param->delay << ADC_DELAY_POS) & ADC_DELAY_MASK; if (use_bis) adc_1_reg |= ADC_BIS; } else { /* configure and start convert to read x and y position */ /* configure to read 2 value in channel selection 1 & 2 */ adc_1_reg = 0x100409 | (ADC_BIS * use_bis) | ADC_NO_ADTRIG; /* set ATOx = 5, it could be better for ts ADC */ adc_1_reg |= 0x002800; } reg_1 = adc_1_reg; if (use_bis == 0) { data_ready_adc_1 = false; adc_1_reg |= ASC_ADC; data_ready_adc_1 = true; pr_debug("Write Reg %i = %x\n", REG_ADC1, adc_1_reg); INIT_COMPLETION(adcdone_it); CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, ADC_SGL_CH | ADC_ATO | ADC_ADSEL | ADC_CH_0_MASK | ADC_CH_1_MASK | ADC_NO_ADTRIG | ADC_EN | ADC_DELAY_MASK | ASC_ADC | ADC_BIS)); pr_debug("wait adc done \n"); wait_for_completion_interruptible(&adcdone_it); data_ready_adc_1 = false; } else { data_ready_adc_2 = false; adc_1_reg |= ASC_ADC; data_ready_adc_2 = true; INIT_COMPLETION(adcbisdone_it); CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, 0xFFFFFF)); temp = 0x800000; CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF)); pr_debug("wait adc done bis\n"); wait_for_completion_interruptible(&adcbisdone_it); data_ready_adc_2 = false; } /* read result and store in adc_param */ result = 0; if (use_bis == 0) result_reg = REG_ADC2; else result_reg = REG_ADC4; CHECK_ERROR(pmic_write_reg(REG_ADC1, 4 << ADC_CH_1_POS, ADC_CH_0_MASK | ADC_CH_1_MASK)); for (i = 0; i <= 3; i++) { CHECK_ERROR(pmic_read_reg(result_reg, &result, PMIC_ALL_BITS)); adc_param->value[i] = ((result & ADD1_RESULT_MASK) >> 2); adc_param->value[i + 4] = ((result & ADD2_RESULT_MASK) >> 14); pr_debug("value[%d] = %d, value[%d] = %d\n", i, adc_param->value[i], i + 4, adc_param->value[i + 4]); } if (adc_param->read_ts) { adc_param->ts_value.x_position = adc_param->value[0]; adc_param->ts_value.x_position1 = adc_param->value[0]; adc_param->ts_value.x_position2 = adc_param->value[1]; adc_param->ts_value.y_position = adc_param->value[3]; adc_param->ts_value.y_position1 = adc_param->value[3]; adc_param->ts_value.y_position2 = adc_param->value[4]; adc_param->ts_value.contact_resistance = adc_param->value[6]; CHECK_ERROR(pmic_write_reg(REG_ADC0, 0x0, ADC_TSMODE_MASK)); } /*if (adc_param->read_ts) { adc_param->ts_value.x_position = adc_param->value[2]; adc_param->ts_value.y_position = adc_param->value[5]; adc_param->ts_value.contact_resistance = adc_param->value[6]; } */ mc13892_adc_release(use_bis); up(&convert_mutex); return PMIC_SUCCESS; } t_reading_mode mc13892_set_read_mode(t_channel channel) { t_reading_mode read_mode = 0; switch (channel) { case CHARGE_CURRENT: read_mode = M_CHARGE_CURRENT; break; case BATTERY_CURRENT: read_mode = M_BATTERY_CURRENT; break; default: read_mode = 0; } return read_mode; } PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result) { t_adc_param adc_param; PMIC_STATUS ret; if (suspend_flag == 1) return -EBUSY; channel = channel_num[channel]; if (channel == -1) { pr_debug("Wrong channel ID\n"); return PMIC_PARAMETER_ERROR; } mc13892_adc_init_param(&adc_param); pr_debug("pmic_adc_convert\n"); adc_param.read_ts = false; adc_param.single_channel = true; adc_param.read_mode = mc13892_set_read_mode(channel); /* Find the group */ if (channel <= 7) adc_param.channel_0 = channel; else return PMIC_PARAMETER_ERROR; ret = mc13892_adc_convert(&adc_param); *result = adc_param.value[0]; return ret; } PMIC_STATUS pmic_adc_convert_8x(t_channel channel, unsigned short *result) { t_adc_param adc_param; int i; PMIC_STATUS ret; if (suspend_flag == 1) return -EBUSY; channel = channel_num[channel]; if (channel == -1) { pr_debug("Wrong channel ID\n"); return PMIC_PARAMETER_ERROR; } mc13892_adc_init_param(&adc_param); pr_debug("pmic_adc_convert_8x\n"); adc_param.read_ts = false; adc_param.single_channel = true; adc_param.read_mode = mc13892_set_read_mode(channel); if (channel <= 7) { adc_param.channel_0 = channel; adc_param.channel_1 = channel; } else return PMIC_PARAMETER_ERROR; ret = mc13892_adc_convert(&adc_param); for (i = 0; i <= 7; i++) result[i] = adc_param.value[i]; return ret; } PMIC_STATUS pmic_adc_set_touch_mode(t_touch_mode touch_mode) { if (suspend_flag == 1) return -EBUSY; CHECK_ERROR(pmic_write_reg(REG_ADC0, BITFVAL(MC13892_ADC0_TS_M, touch_mode), BITFMASK(MC13892_ADC0_TS_M))); return PMIC_SUCCESS; } PMIC_STATUS pmic_adc_get_touch_mode(t_touch_mode * touch_mode) { unsigned int value; if (suspend_flag == 1) return -EBUSY; CHECK_ERROR(pmic_read_reg(REG_ADC0, &value, PMIC_ALL_BITS)); *touch_mode = BITFEXT(value, MC13892_ADC0_TS_M); return PMIC_SUCCESS; } PMIC_STATUS pmic_adc_get_touch_sample(t_touch_screen *touch_sample, int wait) { if (mc13892_adc_read_ts(touch_sample, wait) != 0) return PMIC_ERROR; if (0 == pmic_adc_filter(touch_sample)) return PMIC_SUCCESS; else return PMIC_ERROR; } PMIC_STATUS mc13892_adc_read_ts(t_touch_screen *ts_value, int wait_tsi) { t_adc_param param; pr_debug("mc13892_adc : mc13892_adc_read_ts\n"); if (suspend_flag == 1) return -EBUSY; if (wait_ts) { pr_debug("mc13892_adc : error TS busy \n"); return PMIC_ERROR; } mc13892_adc_init_param(¶m); param.wait_tsi = wait_tsi; param.read_ts = true; if (mc13892_adc_convert(¶m) != 0) return PMIC_ERROR; /* check if x-y is ok */ if (param.ts_value.contact_resistance < 1000) { ts_value->x_position = param.ts_value.x_position; ts_value->x_position1 = param.ts_value.x_position1; ts_value->x_position2 = param.ts_value.x_position2; ts_value->y_position = param.ts_value.y_position; ts_value->y_position1 = param.ts_value.y_position1; ts_value->y_position2 = param.ts_value.y_position2; ts_value->contact_resistance = param.ts_value.contact_resistance + 1; } else { ts_value->x_position = 0; ts_value->y_position = 0; ts_value->contact_resistance = 0; } return PMIC_SUCCESS; } int mc13892_adc_request(bool read_ts) { int adc_index = -1; if (read_ts != 0) { /*for ts we use bis=0 */ if (adc_dev[0] == ADC_USED) return -1; /*no wait here */ adc_dev[0] = ADC_USED; adc_index = 0; } else { /*for other adc use bis = 1 */ if (adc_dev[1] == ADC_USED) { return -1; /*no wait here */ } adc_dev[1] = ADC_USED; adc_index = 1; } pr_debug("mc13892_adc : request ADC %d\n", adc_index); return adc_index; } int mc13892_adc_release(int adc_index) { while (suspend_flag == 1) { swait++; /* Block if the device is suspended */ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) return -ERESTARTSYS; } pr_debug("mc13892_adc : release ADC %d\n", adc_index); if ((adc_dev[adc_index] == ADC_MONITORING) || (adc_dev[adc_index] == ADC_USED)) { adc_dev[adc_index] = ADC_FREE; wake_up(&queue_adc_busy); return 0; } return -1; } #ifdef DEBUG static t_adc_param adc_param_db; static ssize_t adc_info(struct device *dev, struct device_attribute *attr, char *buf) { int *value = adc_param_db.value; pr_debug("adc_info\n"); pr_debug("ch0\t\t%d\n", adc_param_db.channel_0); pr_debug("ch1\t\t%d\n", adc_param_db.channel_1); pr_debug("d5\t\t%d\n", adc_param_db.chrgraw_devide_5); pr_debug("conv delay\t%d\n", adc_param_db.conv_delay); pr_debug("delay\t\t%d\n", adc_param_db.delay); pr_debug("read mode\t%d\n", adc_param_db.read_mode); pr_debug("read ts\t\t%d\n", adc_param_db.read_ts); pr_debug("single ch\t%d\n", adc_param_db.single_channel); pr_debug("wait ts int\t%d\n", adc_param_db.wait_tsi); pr_debug("value0-3:\t%d\t%d\t%d\t%d\n", value[0], value[1], value[2], value[3]); pr_debug("value4-7:\t%d\t%d\t%d\t%d\n", value[4], value[5], value[6], value[7]); return 0; } enum { ADC_SET_CH0 = 0, ADC_SET_CH1, ADC_SET_DV5, ADC_SET_CON_DELAY, ADC_SET_DELAY, ADC_SET_RM, ADC_SET_RT, ADC_SET_S_CH, ADC_SET_WAIT_TS, ADC_INIT_P, ADC_START, ADC_TS, ADC_TS_READ, ADC_TS_CAL, ADC_CMD_MAX }; static const char *const adc_cmd[ADC_CMD_MAX] = { [ADC_SET_CH0] = "ch0", [ADC_SET_CH1] = "ch1", [ADC_SET_DV5] = "dv5", [ADC_SET_CON_DELAY] = "cd", [ADC_SET_DELAY] = "dl", [ADC_SET_RM] = "rm", [ADC_SET_RT] = "rt", [ADC_SET_S_CH] = "sch", [ADC_SET_WAIT_TS] = "wt", [ADC_INIT_P] = "init", [ADC_START] = "start", [ADC_TS] = "touch", [ADC_TS_READ] = "touchr", [ADC_TS_CAL] = "cal" }; static int cmd(unsigned int index, int value) { t_touch_screen ts; switch (index) { case ADC_SET_CH0: adc_param_db.channel_0 = value; break; case ADC_SET_CH1: adc_param_db.channel_1 = value; break; case ADC_SET_DV5: adc_param_db.chrgraw_devide_5 = value; break; case ADC_SET_CON_DELAY: adc_param_db.conv_delay = value; break; case ADC_SET_RM: adc_param_db.read_mode = value; break; case ADC_SET_RT: adc_param_db.read_ts = value; break; case ADC_SET_S_CH: adc_param_db.single_channel = value; break; case ADC_SET_WAIT_TS: adc_param_db.wait_tsi = value; break; case ADC_INIT_P: mc13892_adc_init_param(&adc_param_db); break; case ADC_START: mc13892_adc_convert(&adc_param_db); break; case ADC_TS: pmic_adc_get_touch_sample(&ts, 1); pr_debug("x = %d\n", ts.x_position); pr_debug("y = %d\n", ts.y_position); pr_debug("p = %d\n", ts.contact_resistance); break; case ADC_TS_READ: pmic_adc_get_touch_sample(&ts, 0); pr_debug("x = %d\n", ts.x_position); pr_debug("y = %d\n", ts.y_position); pr_debug("p = %d\n", ts.contact_resistance); break; case ADC_TS_CAL: break; default: pr_debug("error command\n"); break; } return 0; } static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int state = 0; const char *const *s; char *p, *q; int error; int len, value = 0; pr_debug("adc_ctl\n"); q = NULL; q = memchr(buf, ' ', count); if (q != NULL) { len = q - buf; q += 1; value = simple_strtoul(q, NULL, 10); } else { p = memchr(buf, '\n', count); len = p ? p - buf : count; } for (s = &adc_cmd[state]; state < ADC_CMD_MAX; s++, state++) { if (*s && !strncmp(buf, *s, len)) break; } if (state < ADC_CMD_MAX && *s) error = cmd(state, value); else error = -EINVAL; return count; } #else static ssize_t adc_info(struct device *dev, struct device_attribute *attr, char *buf) { return 0; } static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return count; } #endif /*! * This function triggers a conversion and returns sampling results of each * specified channel. * * @param channels This input parameter is bitmap to specify channels * to be sampled. * @param result The pointer to array to store sampling results. * The memory should be allocated by the caller of this * function. * * @return This function returns PMIC_SUCCESS if successful. */ PMIC_STATUS pmic_adc_convert_multichnnel(t_channel channels, unsigned short *result) { t_adc_param adc_param; int i; PMIC_STATUS ret; if (suspend_flag == 1) { return -EBUSY; } mc13892_adc_init_param(&adc_param); pr_debug("pmic_adc_convert_multichnnel\n"); channels = channel_num[channels]; if (channels == -1) { pr_debug("Wrong channel ID\n"); return PMIC_PARAMETER_ERROR; } adc_param.read_ts = false; adc_param.single_channel = false; if ((channels >= 0) && (channels <= 7)) { adc_param.channel_0 = channels; adc_param.channel_1 = ((channels + 4) % 4) + 4; } else { return PMIC_PARAMETER_ERROR; } adc_param.read_mode = 0x00003f; adc_param.read_ts = false; ret = mc13892_adc_convert(&adc_param); for (i = 0; i <= 7; i++) { result[i] = adc_param.value[i]; } return ret; } /*! * This function starts a Battery Current mode conversion. * * @param mode Conversion mode. * @param result Battery Current measurement result. * if \a mode = ADC_8CHAN_1X, the result is \n * result[0] = (BATTP - BATT_I) \n * if \a mode = ADC_1CHAN_8X, the result is \n * result[0] = BATTP \n * result[1] = BATT_I \n * result[2] = BATTP \n * result[3] = BATT_I \n * result[4] = BATTP \n * result[5] = BATT_I \n * result[6] = BATTP \n * result[7] = BATT_I * * @return This function returns PMIC_SUCCESS if successful. */ PMIC_STATUS pmic_adc_get_battery_current(t_conversion_mode mode, unsigned short *result) { PMIC_STATUS ret; t_channel channel; if (suspend_flag == 1) { return -EBUSY; } channel = BATTERY_CURRENT; if (mode == ADC_8CHAN_1X) { ret = pmic_adc_convert(channel, result); } else { ret = pmic_adc_convert_8x(channel, result); } return ret; } /*! * This function implements the open method on a MC13892 ADC device. * * @param inode pointer on the node * @param file pointer on the file * @return This function returns 0. */ static int pmic_adc_open(struct inode *inode, struct file *file) { while (suspend_flag == 1) { swait++; /* Block if the device is suspended */ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) { return -ERESTARTSYS; } } pr_debug("mc13892_adc : mc13892_adc_open()\n"); return 0; } /*! * This function implements the release method on a MC13892 ADC device. * * @param inode pointer on the node * @param file pointer on the file * @return This function returns 0. */ static int pmic_adc_free(struct inode *inode, struct file *file) { pr_debug("mc13892_adc : mc13892_adc_free()\n"); return 0; } /*! * This function implements IOCTL controls on a MC13892 ADC device. * * @param inode pointer on the node * @param file pointer on the file * @param cmd the command * @param arg the parameter * @return This function returns 0 if successful. */ static int pmic_adc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { t_adc_convert_param *convert_param; t_touch_mode touch_mode; t_touch_screen touch_sample; unsigned short b_current; if ((_IOC_TYPE(cmd) != 'p') && (_IOC_TYPE(cmd) != 'D')) return -ENOTTY; while (suspend_flag == 1) { swait++; /* Block if the device is suspended */ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) { return -ERESTARTSYS; } } switch (cmd) { case PMIC_ADC_INIT: CHECK_ERROR(pmic_adc_init()); break; case PMIC_ADC_DEINIT: CHECK_ERROR(pmic_adc_deinit()); break; case PMIC_ADC_CONVERT: if ((convert_param = kmalloc(sizeof(t_adc_convert_param), GFP_KERNEL)) == NULL) { return -ENOMEM; } if (copy_from_user(convert_param, (t_adc_convert_param *) arg, sizeof(t_adc_convert_param))) { kfree(convert_param); return -EFAULT; } CHECK_ERROR_KFREE(pmic_adc_convert(convert_param->channel, convert_param->result), (kfree(convert_param))); if (copy_to_user((t_adc_convert_param *) arg, convert_param, sizeof(t_adc_convert_param))) { kfree(convert_param); return -EFAULT; } kfree(convert_param); break; case PMIC_ADC_CONVERT_8X: if ((convert_param = kmalloc(sizeof(t_adc_convert_param), GFP_KERNEL)) == NULL) { return -ENOMEM; } if (copy_from_user(convert_param, (t_adc_convert_param *) arg, sizeof(t_adc_convert_param))) { kfree(convert_param); return -EFAULT; } CHECK_ERROR_KFREE(pmic_adc_convert_8x(convert_param->channel, convert_param->result), (kfree(convert_param))); if (copy_to_user((t_adc_convert_param *) arg, convert_param, sizeof(t_adc_convert_param))) { kfree(convert_param); return -EFAULT; } kfree(convert_param); break; case PMIC_ADC_CONVERT_MULTICHANNEL: if ((convert_param = kmalloc(sizeof(t_adc_convert_param), GFP_KERNEL)) == NULL) { return -ENOMEM; } if (copy_from_user(convert_param, (t_adc_convert_param *) arg, sizeof(t_adc_convert_param))) { kfree(convert_param); return -EFAULT; } CHECK_ERROR_KFREE(pmic_adc_convert_multichnnel (convert_param->channel, convert_param->result), (kfree(convert_param))); if (copy_to_user((t_adc_convert_param *) arg, convert_param, sizeof(t_adc_convert_param))) { kfree(convert_param); return -EFAULT; } kfree(convert_param); break; case PMIC_ADC_SET_TOUCH_MODE: CHECK_ERROR(pmic_adc_set_touch_mode((t_touch_mode) arg)); break; case PMIC_ADC_GET_TOUCH_MODE: CHECK_ERROR(pmic_adc_get_touch_mode(&touch_mode)); if (copy_to_user((t_touch_mode *) arg, &touch_mode, sizeof(t_touch_mode))) { return -EFAULT; } break; case PMIC_ADC_GET_TOUCH_SAMPLE: CHECK_ERROR(pmic_adc_get_touch_sample(&touch_sample, 1)); if (copy_to_user((t_touch_screen *) arg, &touch_sample, sizeof(t_touch_screen))) { return -EFAULT; } break; case PMIC_ADC_GET_BATTERY_CURRENT: CHECK_ERROR(pmic_adc_get_battery_current(ADC_8CHAN_1X, &b_current)); if (copy_to_user((unsigned short *)arg, &b_current, sizeof(unsigned short))) { return -EFAULT; } break; default: pr_debug("pmic_adc_ioctl: unsupported ioctl command 0x%x\n", cmd); return -EINVAL; } return 0; } static struct file_operations mc13892_adc_fops = { .owner = THIS_MODULE, .ioctl = pmic_adc_ioctl, .open = pmic_adc_open, .release = pmic_adc_free, }; static struct cdev pmic_adc_cdev; static DEVICE_ATTR(adc, 0644, adc_info, adc_ctl); static int pmic_adc_module_probe(struct platform_device *pdev) { int ret = 0; struct device * sdev; dev_t devid; pr_debug("PMIC ADC start probe\n"); if( (ret = alloc_chrdev_region(&devid, 0, 8, "pmic_adc")) < 0 ) { pr_debug(KERN_ERR "Unable to allocate device range for pmic_adc\n"); return ret; } pmic_adc_major = MAJOR(devid); if (pmic_adc_major < 0) { pr_debug(KERN_ERR "Unable to get a major for pmic_adc\n"); ret = pmic_adc_major; goto unreg_char; } cdev_init(&pmic_adc_cdev, &mc13892_adc_fops); ret =cdev_add(&pmic_adc_cdev, devid, 8); if (ret < 0) { pr_err("pmic_adc: cannot add character device\n"); goto unreg_char; } pmic_adc_class = class_create(THIS_MODULE, "pmic_adc"); if (IS_ERR(pmic_adc_class)) { pr_debug(KERN_ERR "Error creating pmic_adc class.\n"); ret = PTR_ERR(pmic_adc_class); goto unreg_char; } sdev = device_create(pmic_adc_class, NULL, devid, NULL, "pmic_adc"); if (IS_ERR(sdev) ) { pr_debug(KERN_ERR "Error creating pmic_adc class device.\n"); ret = PTR_ERR(sdev); goto cl_destroy; } ret = device_create_file(&(pdev->dev), &dev_attr_adc); if (ret) { pr_debug("Can't create device file!\n"); ret = -ENODEV; goto dev_destroy; } init_waitqueue_head(&suspendq); ret = pmic_adc_init(); if (ret != PMIC_SUCCESS) { pr_debug("Error in pmic_adc_init.\n"); goto rm_dev_file; } pmic_adc_ready = 1; printk(KERN_DEBUG"PMIC ADC successfully probed\n"); return 0; rm_dev_file: device_remove_file(&(pdev->dev), &dev_attr_adc); dev_destroy: device_destroy(pmic_adc_class, MKDEV(pmic_adc_major, 0)); cl_destroy: class_destroy(pmic_adc_class); unreg_char: unregister_chrdev(pmic_adc_major, "pmic_adc"); return ret; } static int pmic_adc_module_remove(struct platform_device *pdev) { pmic_adc_deinit(); pmic_adc_ready = 0; pr_debug("PMIC ADC successfully removed\n"); return 0; } static void pmic_adc_module_shutdown(struct platform_device *pdev) { /** * Stop the ADC by calling the deinit function. Without this * call, we have problems to 'shutdown' the system through a * shell reboot call when the touch screen is enabled. * The the system doesnt reboot. */ pmic_adc_deinit(); pmic_adc_ready = 0; } static struct platform_driver pmic_adc_driver_ldm = { .driver = { .name = "pmic_adc", }, .suspend = pmic_adc_suspend, .resume = pmic_adc_resume, .probe = pmic_adc_module_probe, .remove = pmic_adc_module_remove, .shutdown = pmic_adc_module_shutdown, }; static int __init pmic_adc_module_init(void) { pr_debug("PMIC ADC driver loading...\n"); return platform_driver_register(&pmic_adc_driver_ldm); } static void __exit pmic_adc_module_exit(void) { platform_driver_unregister(&pmic_adc_driver_ldm); pr_debug("PMIC ADC driver successfully unloaded\n"); } module_init(pmic_adc_module_init); module_exit(pmic_adc_module_exit); MODULE_DESCRIPTION("PMIC ADC device driver"); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_LICENSE("GPL");