Loading Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt +7 −1 Original line number Diff line number Diff line Loading @@ -13,7 +13,10 @@ Required properties: - address-cells : Must be one. - size-cells : Must be zero. - interrupts : The USR bank peripheral VADC interrupt. - interrupt-names : Should contain "eoc-int-en-set". - interrupt-names : Should contain "eoc-int-en-set" for EOC, "high-thr-en-set" for high threshold interrupts and "low-thr-en-set" for low threshold interrupts. High and low threshold interrupts are to be enabled if VADC_USR needs to support recurring measurement. - qcom,adc-bit-resolution : Bit resolution of the ADC. - qcom,adc-vdd-reference : Voltage reference used by the ADC. Loading @@ -25,6 +28,9 @@ Optional properties: - qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the PMIC type and revision for applying the appropriate temperature compensation parameters. -qcom,vadc-meas-int-mode : Enable VADC_USR to handle requests to perform recurring measurements for any one supported channel along with supporting single conversion requests. Client required property: - qcom,<consumer name>-vadc : The phandle to the corresponding vadc device. Loading drivers/hwmon/qpnp-adc-voltage.c +467 −25 Original line number Diff line number Diff line Loading @@ -47,6 +47,9 @@ #define QPNP_VADC_STATUS1_REQ_STS BIT(1) #define QPNP_VADC_STATUS1_EOC BIT(0) #define QPNP_VADC_STATUS1_REQ_STS_EOC_MASK 0x3 #define QPNP_VADC_STATUS1_OP_MODE_MASK 0x18 #define QPNP_VADC_MEAS_INT_MODE 0x2 #define QPNP_VADC_STATUS2 0x9 #define QPNP_VADC_STATUS2_CONV_SEQ_STATE 6 #define QPNP_VADC_STATUS2_FIFO_NOT_EMPTY_FLAG BIT(1) Loading @@ -55,34 +58,31 @@ #define QPNP_VADC_CONV_TIMEOUT_ERR 2 #define QPNP_VADC_MODE_CTL 0x40 #define QPNP_VADC_OP_MODE_SHIFT 4 #define QPNP_VADC_OP_MODE_SHIFT 3 #define QPNP_VADC_VREF_XO_THM_FORCE BIT(2) #define QPNP_VADC_AMUX_TRIM_EN BIT(1) #define QPNP_VADC_ADC_TRIM_EN BIT(0) #define QPNP_VADC_TRIM_EN BIT(0) #define QPNP_VADC_EN_CTL1 0x46 #define QPNP_VADC_ADC_EN BIT(7) #define QPNP_VADC_ADC_CH_SEL_CTL 0x48 #define QPNP_VADC_ADC_DIG_PARAM 0x50 #define QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 3 #define QPNP_VADC_EN BIT(7) #define QPNP_VADC_CH_SEL_CTL 0x48 #define QPNP_VADC_DIG_PARAM 0x50 #define QPNP_VADC_DIG_DEC_RATIO_SEL_SHIFT 3 #define QPNP_VADC_HW_SETTLE_DELAY 0x51 #define QPNP_VADC_CONV_REQ 0x52 #define QPNP_VADC_CONV_REQ_SET BIT(7) #define QPNP_VADC_CONV_SEQ_CTL 0x54 #define QPNP_VADC_CONV_SEQ_HOLDOFF_SHIFT 4 #define QPNP_VADC_CONV_SEQ_TRIG_CTL 0x55 #define QPNP_VADC_MEAS_INTERVAL_CTL 0x57 #define QPNP_VADC_CONV_SEQ_FALLING_EDGE 0x0 #define QPNP_VADC_CONV_SEQ_RISING_EDGE 0x1 #define QPNP_VADC_CONV_SEQ_EDGE_SHIFT 7 #define QPNP_VADC_FAST_AVG_CTL 0x5a #define QPNP_VADC_M0_LOW_THR_LSB 0x5c #define QPNP_VADC_M0_LOW_THR_MSB 0x5d #define QPNP_VADC_M0_HIGH_THR_LSB 0x5e #define QPNP_VADC_M0_HIGH_THR_MSB 0x5f #define QPNP_VADC_M1_LOW_THR_LSB 0x69 #define QPNP_VADC_M1_LOW_THR_MSB 0x6a #define QPNP_VADC_M1_HIGH_THR_LSB 0x6b #define QPNP_VADC_M1_HIGH_THR_MSB 0x6c #define QPNP_VADC_LOW_THR_LSB 0x5c #define QPNP_VADC_LOW_THR_MSB 0x5d #define QPNP_VADC_HIGH_THR_LSB 0x5e #define QPNP_VADC_HIGH_THR_MSB 0x5f #define QPNP_VADC_ACCESS 0xd0 #define QPNP_VADC_ACCESS_DATA 0xa5 #define QPNP_VADC_PERH_RESET_CTL3 0xda Loading @@ -100,6 +100,21 @@ #define QPNP_VADC_CONV_TIME_MAX 2100 #define QPNP_ADC_COMPLETION_TIMEOUT HZ #define QPNP_VADC_ERR_COUNT 20 #define QPNP_OP_MODE_SHIFT 3 #define QPNP_VADC_THR_LSB_MASK(val) (val & 0xff) #define QPNP_VADC_THR_MSB_MASK(val) ((val & 0xff00) >> 8) #define QPNP_MIN_TIME 2000 #define QPNP_MAX_TIME 2000 #define QPNP_RETRY 100 struct qpnp_vadc_mode_state { bool meas_int_mode; bool meas_int_request_in_queue; bool vadc_meas_int_enable; struct qpnp_adc_tm_btm_param *param; struct qpnp_adc_amux vadc_meas_amux; }; struct qpnp_vadc_chip { struct device *dev; Loading @@ -115,6 +130,11 @@ struct qpnp_vadc_chip { bool vadc_poll_eoc; u8 revision_ana_minor; u8 revision_dig_major; struct workqueue_struct *high_thr_wq; struct workqueue_struct *low_thr_wq; struct work_struct trigger_high_thr_work; struct work_struct trigger_low_thr_work; struct qpnp_vadc_mode_state *state_copy; struct sensor_device_attribute sens_attr[0]; }; Loading @@ -135,6 +155,11 @@ static struct qpnp_vadc_scale_fn vadc_scale_fn[] = { [SCALE_NCP_03WF683_THERM] = {qpnp_adc_scale_therm_ncp03}, }; static struct qpnp_adc_tm_reverse_scale_fn adc_vadc_rscale_fn[] = { [SCALE_RVADC_ABSOLUTE] = {qpnp_adc_absolute_rthr}, [SCALE_RVADC_PMIC_THERM] = {qpnp_adc_scale_millidegc_pmic_voltage_thr}, }; static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg, u8 *data) { Loading Loading @@ -202,12 +227,25 @@ static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc) return 0; } static int32_t qpnp_vadc_mode_select(struct qpnp_vadc_chip *vadc, u8 mode_ctl) { int rc; mode_ctl |= (QPNP_VADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN); rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MODE_CTL, mode_ctl); if (rc < 0) pr_err("vadc write mode selection err:%d\n", rc); return rc; } static int32_t qpnp_vadc_enable(struct qpnp_vadc_chip *vadc, bool state) { int rc = 0; u8 data = 0; data = QPNP_VADC_ADC_EN; data = QPNP_VADC_EN; if (state) { rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_EN_CTL1, data); Loading @@ -217,7 +255,7 @@ static int32_t qpnp_vadc_enable(struct qpnp_vadc_chip *vadc, bool state) } } else { rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_EN_CTL1, (~data & QPNP_VADC_ADC_EN)); (~data & QPNP_VADC_EN)); if (rc < 0) { pr_err("VADC disable failed\n"); return rc; Loading @@ -238,13 +276,13 @@ static int32_t qpnp_vadc_status_debug(struct qpnp_vadc_chip *vadc) return rc; } rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_ADC_DIG_PARAM, &dig); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_DIG_PARAM, &dig); if (rc < 0) { pr_err("digital param read failed with %d\n", rc); return rc; } rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_ADC_CH_SEL_CTL, &chan); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_CH_SEL_CTL, &chan); if (rc < 0) { pr_err("channel read failed with %d\n", rc); return rc; Loading Loading @@ -288,16 +326,15 @@ static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc, /* Mode selection */ mode_ctrl |= ((chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT) | (QPNP_VADC_ADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN)); (QPNP_VADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN)); rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MODE_CTL, mode_ctrl); if (rc < 0) { pr_err("Mode configure write error\n"); return rc; } /* Channel selection */ rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ADC_CH_SEL_CTL, rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_CH_SEL_CTL, chan_prop->amux_channel); if (rc < 0) { pr_err("Channel configure error\n"); Loading @@ -306,8 +343,8 @@ static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc, /* Digital parameter setup */ decimation = chan_prop->decimation << QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT; rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ADC_DIG_PARAM, decimation); QPNP_VADC_DIG_DEC_RATIO_SEL_SHIFT; rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_DIG_PARAM, decimation); if (rc < 0) { pr_err("Digital parameter configure write error\n"); return rc; Loading @@ -321,6 +358,10 @@ static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc, return rc; } pr_debug("mode:%d, channel:%d, decimation:%d, hw_settle:%d\n", mode_ctrl, chan_prop->amux_channel, decimation, chan_prop->hw_settle_time); if (chan_prop->mode_sel == (ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT)) { /* Normal measurement mode */ Loading Loading @@ -472,6 +513,33 @@ static void qpnp_vadc_work(struct work_struct *work) return; } static void qpnp_vadc_low_thr_fn(struct work_struct *work) { struct qpnp_vadc_chip *vadc = container_of(work, struct qpnp_vadc_chip, trigger_low_thr_work); vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = false; vadc->state_copy->param->threshold_notification( ADC_TM_LOW_STATE, vadc->state_copy->param->btm_ctx); return; } static void qpnp_vadc_high_thr_fn(struct work_struct *work) { struct qpnp_vadc_chip *vadc = container_of(work, struct qpnp_vadc_chip, trigger_high_thr_work); vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = false; vadc->state_copy->param->threshold_notification( ADC_TM_HIGH_STATE, vadc->state_copy->param->btm_ctx); return; } static irqreturn_t qpnp_vadc_isr(int irq, void *dev_id) { struct qpnp_vadc_chip *vadc = dev_id; Loading @@ -481,6 +549,34 @@ static irqreturn_t qpnp_vadc_isr(int irq, void *dev_id) return IRQ_HANDLED; } static irqreturn_t qpnp_vadc_low_thr_isr(int irq, void *data) { struct qpnp_vadc_chip *vadc = data; u8 mode_ctl = 0; mode_ctl = ADC_OP_NORMAL_MODE; /* Set measurement in single measurement mode */ qpnp_vadc_mode_select(vadc, mode_ctl); qpnp_vadc_enable(vadc, false); schedule_work(&vadc->trigger_low_thr_work); return IRQ_HANDLED; } static irqreturn_t qpnp_vadc_high_thr_isr(int irq, void *data) { struct qpnp_vadc_chip *vadc = data; u8 mode_ctl = 0; mode_ctl = ADC_OP_NORMAL_MODE; /* Set measurement in single measurement mode */ qpnp_vadc_mode_select(vadc, mode_ctl); qpnp_vadc_enable(vadc, false); schedule_work(&vadc->trigger_high_thr_work); return IRQ_HANDLED; } static int32_t qpnp_vadc_version_check(struct qpnp_vadc_chip *dev) { uint8_t revision; Loading Loading @@ -1178,6 +1274,123 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc, } EXPORT_SYMBOL(qpnp_get_vadc_gain_and_offset); static int32_t qpnp_vadc_wait_for_req_sts_check(struct qpnp_vadc_chip *vadc) { u8 status1 = 0; int rc, count = 0; /* Re-enable the peripheral */ rc = qpnp_vadc_enable(vadc, true); if (rc) { pr_err("vadc re-enable peripheral failed with %d\n", rc); return rc; } /* The VADC_TM bank needs to be disabled for new conversion request */ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1); if (rc) { pr_err("vadc read status1 failed with %d\n", rc); return rc; } /* Disable the bank if a conversion is occuring */ while ((status1 & QPNP_VADC_STATUS1_REQ_STS) && (count < QPNP_RETRY)) { /* Wait time is based on the optimum sampling rate * and adding enough time buffer to account for ADC conversions * occuring on different peripheral banks */ usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1); if (rc < 0) { pr_err("vadc disable failed with %d\n", rc); return rc; } count++; } if (count >= QPNP_RETRY) pr_err("QPNP vadc status req bit did not fall low!!\n"); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1); /* Disable the peripheral */ rc = qpnp_vadc_enable(vadc, false); if (rc < 0) pr_err("vadc peripheral disable failed with %d\n", rc); return rc; } static int32_t qpnp_vadc_manage_meas_int_requests(struct qpnp_vadc_chip *chip) { struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev); int rc = 0, dt_index = 0; u8 mode_ctl = 0; pr_debug("meas_int_mode:0x%x, mode_ctl:%0x\n", vadc->state_copy->meas_int_mode, mode_ctl); if (vadc->state_copy->meas_int_mode) { pr_debug("meas interval in progress. Procced to disable it\n"); /* measurement interval in progress. Proceed to disable it */ mode_ctl = ADC_OP_NORMAL_MODE; rc = qpnp_vadc_mode_select(vadc, mode_ctl); if (rc < 0) { pr_err("NORM mode select failed with %d\n", rc); return rc; } /* Disable bank */ rc = qpnp_vadc_enable(vadc, false); if (rc) { pr_err("Disable bank failed with %d\n", rc); return rc; } /* Check if a conversion is in progress */ rc = qpnp_vadc_wait_for_req_sts_check(vadc); if (rc < 0) { pr_err("req_sts check failed with %d\n", rc); return rc; } vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = true; } else if (vadc->state_copy->meas_int_request_in_queue) { /* put the meas interval back in queue */ pr_debug("put meas interval back in queue\n"); vadc->adc->amux_prop->amux_channel = vadc->state_copy->vadc_meas_amux.channel_num; while ((vadc->adc->adc_channels[dt_index].channel_num != vadc->adc->amux_prop->amux_channel) && (dt_index < vadc->max_channels_available)) dt_index++; if (dt_index >= vadc->max_channels_available) { pr_err("not a valid VADC channel\n"); rc = -EINVAL; return rc; } vadc->adc->amux_prop->decimation = vadc->adc->amux_prop->decimation; vadc->adc->amux_prop->hw_settle_time = vadc->adc->amux_prop->hw_settle_time; vadc->adc->amux_prop->fast_avg_setup = vadc->adc->amux_prop->fast_avg_setup; vadc->adc->amux_prop->mode_sel = ADC_OP_MEASUREMENT_INTERVAL; rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop); if (rc) { pr_err("vadc configure failed with %d\n", rc); return rc; } vadc->state_copy->meas_int_mode = true; vadc->state_copy->meas_int_request_in_queue = false; } dev_set_drvdata(vadc->dev, vadc); return 0; } struct qpnp_vadc_chip *qpnp_get_vadc(struct device *dev, const char *name) { struct qpnp_vadc_chip *vadc; Loading Loading @@ -1211,6 +1424,9 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc, mutex_lock(&vadc->adc->adc_lock); if (vadc->state_copy->vadc_meas_int_enable) qpnp_vadc_manage_meas_int_requests(vadc); if (!vadc->vadc_init_calib) { rc = qpnp_vadc_version_check(vadc); if (rc) Loading Loading @@ -1346,6 +1562,9 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc, vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result); fail_unlock: if (vadc->state_copy->vadc_meas_int_enable) qpnp_vadc_manage_meas_int_requests(vadc); mutex_unlock(&vadc->adc->adc_lock); return rc; Loading Loading @@ -1502,6 +1721,175 @@ fail: } EXPORT_SYMBOL(qpnp_vadc_iadc_sync_complete_request); static int32_t qpnp_vadc_thr_update(struct qpnp_vadc_chip *vadc, int32_t high_thr, int32_t low_thr) { int rc = 0; pr_debug("client requested high:%d and low:%d\n", high_thr, low_thr); rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_LOW_THR_LSB, QPNP_VADC_THR_LSB_MASK(low_thr)); if (rc < 0) { pr_err("low threshold lsb setting failed, err:%d\n", rc); return rc; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_LOW_THR_MSB, QPNP_VADC_THR_MSB_MASK(low_thr)); if (rc < 0) { pr_err("low threshold msb setting failed, err:%d\n", rc); return rc; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HIGH_THR_LSB, QPNP_VADC_THR_LSB_MASK(high_thr)); if (rc < 0) { pr_err("high threshold lsb setting failed, err:%d\n", rc); return rc; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HIGH_THR_MSB, QPNP_VADC_THR_MSB_MASK(high_thr)); if (rc < 0) { pr_err("high threshold msb setting failed, err:%d\n", rc); return rc; } pr_debug("client requested high:%d and low:%d\n", high_thr, low_thr); return rc; } int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param) { uint32_t channel, scale_type = 0; uint32_t low_thr = 0, high_thr = 0; int rc = 0, idx = 0; struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev); if (qpnp_vadc_is_valid(vadc)) return -EPROBE_DEFER; if (!vadc->state_copy->vadc_meas_int_enable) { pr_err("Recurring measurement interval not available\n"); return -EINVAL; } if (param->threshold_notification == NULL) { pr_debug("No notification for high/low temp??\n"); return -EINVAL; } mutex_lock(&vadc->adc->adc_lock); channel = param->channel; while (idx < vadc->max_channels_available) { if (vadc->adc->adc_channels[idx].channel_num == channel) break; else idx++; } if (idx >= vadc->max_channels_available) { pr_err("not a valid VADC channel\n"); rc = -EINVAL; goto fail_unlock; } scale_type = vadc->adc->adc_channels[idx].adc_scale_fn; if (scale_type >= SCALE_RSCALE_NONE) { rc = -EBADF; goto fail_unlock; } pr_debug("channel:%d, scale_type:%d, dt_idx:%d", channel, scale_type, idx); vadc->adc->amux_prop->amux_channel = channel; vadc->adc->amux_prop->decimation = vadc->adc->adc_channels[idx].adc_decimation; vadc->adc->amux_prop->hw_settle_time = vadc->adc->adc_channels[idx].hw_settle_time; vadc->adc->amux_prop->fast_avg_setup = vadc->adc->adc_channels[idx].fast_avg_setup; vadc->adc->amux_prop->mode_sel = ADC_OP_MEASUREMENT_INTERVAL; adc_vadc_rscale_fn[scale_type].chan(vadc, param, &low_thr, &high_thr); if (param->timer_interval >= ADC_MEAS1_INTERVAL_NONE) { pr_err("Invalid timer interval :%d\n", param->timer_interval); goto fail_unlock; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MEAS_INTERVAL_CTL, param->timer_interval); if (rc) { pr_err("vadc meas timer failed with %d\n", rc); goto fail_unlock; } rc = qpnp_vadc_thr_update(vadc, high_thr, low_thr); if (rc) { pr_err("vadc thr update failed with %d\n", rc); goto fail_unlock; } rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop); if (rc) { pr_err("vadc configure failed with %d\n", rc); goto fail_unlock; } vadc->state_copy->meas_int_mode = true; vadc->state_copy->param = param; vadc->state_copy->vadc_meas_amux.channel_num = channel; vadc->state_copy->vadc_meas_amux.adc_decimation = vadc->adc->amux_prop->decimation; vadc->state_copy->vadc_meas_amux.hw_settle_time = vadc->adc->amux_prop->hw_settle_time; vadc->state_copy->vadc_meas_amux.fast_avg_setup = vadc->adc->amux_prop->fast_avg_setup; vadc->state_copy->meas_int_request_in_queue = false; dev_set_drvdata(vadc->dev, vadc); fail_unlock: mutex_unlock(&vadc->adc->adc_lock); return rc; } EXPORT_SYMBOL(qpnp_vadc_channel_monitor); int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip) { struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev); u8 mode_ctl = 0; if (qpnp_vadc_is_valid(vadc)) return -EPROBE_DEFER; if (!vadc->state_copy->vadc_meas_int_enable) { pr_err("Recurring measurement interval not available\n"); return -EINVAL; } vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = false; dev_set_drvdata(vadc->dev, vadc); mode_ctl = ADC_OP_NORMAL_MODE; /* Set measurement in single measurement mode */ qpnp_vadc_mode_select(vadc, mode_ctl); qpnp_vadc_enable(vadc, false); return 0; } EXPORT_SYMBOL(qpnp_vadc_end_channel_monitor); static ssize_t qpnp_adc_show(struct device *dev, struct device_attribute *devattr, char *buf) { Loading Loading @@ -1589,6 +1977,13 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) return -ENOMEM; } vadc->state_copy = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_vadc_mode_state), GFP_KERNEL); if (!vadc->state_copy) { dev_err(&spmi->dev, "Unable to allocate memory\n"); return -ENOMEM; } vadc->adc = adc_qpnp; rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc); if (rc) { Loading Loading @@ -1651,6 +2046,49 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) } else device_init_wakeup(vadc->dev, 1); vadc->state_copy->vadc_meas_int_enable = of_property_read_bool(node, "qcom,vadc-meas-int-mode"); if (vadc->state_copy->vadc_meas_int_enable) { vadc->adc->adc_high_thr_irq = spmi_get_irq_byname(spmi, NULL, "high-thr-en-set"); if (vadc->adc->adc_high_thr_irq < 0) { pr_err("Invalid irq\n"); rc = -ENXIO; goto err_setup; } vadc->adc->adc_low_thr_irq = spmi_get_irq_byname(spmi, NULL, "low-thr-en-set"); if (vadc->adc->adc_low_thr_irq < 0) { pr_err("Invalid irq\n"); rc = -ENXIO; goto err_setup; } rc = devm_request_irq(&spmi->dev, vadc->adc->adc_high_thr_irq, qpnp_vadc_high_thr_isr, IRQF_TRIGGER_RISING, "qpnp_vadc_high_interrupt", vadc); if (rc) { dev_err(&spmi->dev, "failed to request adc irq\n"); goto err_setup; } else { enable_irq_wake(vadc->adc->adc_high_thr_irq); } rc = devm_request_irq(&spmi->dev, vadc->adc->adc_low_thr_irq, qpnp_vadc_low_thr_isr, IRQF_TRIGGER_RISING, "qpnp_vadc_low_interrupt", vadc); if (rc) { dev_err(&spmi->dev, "failed to request adc irq\n"); goto err_setup; } else { enable_irq_wake(vadc->adc->adc_low_thr_irq); } INIT_WORK(&vadc->trigger_high_thr_work, qpnp_vadc_high_thr_fn); INIT_WORK(&vadc->trigger_low_thr_work, qpnp_vadc_low_thr_fn); } vadc->vadc_iadc_sync_lock = false; dev_set_drvdata(&spmi->dev, vadc); list_add(&vadc->list, &qpnp_vadc_device_list); Loading Loading @@ -1698,8 +2136,11 @@ static int qpnp_vadc_suspend_noirq(struct device *dev) struct qpnp_vadc_chip *vadc = dev_get_drvdata(dev); u8 status = 0; if (vadc->vadc_poll_eoc) { qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status); if (((status & QPNP_VADC_STATUS1_OP_MODE_MASK) >> QPNP_VADC_OP_MODE_SHIFT) == QPNP_VADC_MEAS_INT_MODE) { pr_debug("Meas interval in progress\n"); } else if (vadc->vadc_poll_eoc) { status &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK; pr_debug("vadc conversion status=%d\n", status); if (status != QPNP_VADC_STATUS1_EOC) { Loading @@ -1708,6 +2149,7 @@ static int qpnp_vadc_suspend_noirq(struct device *dev) return -EBUSY; } } return 0; } Loading include/linux/qpnp/qpnp-adc.h +37 −1 Original line number Diff line number Diff line Loading @@ -251,7 +251,6 @@ enum qpnp_adc_scale_fn_type { SCALE_NONE, }; /** * enum qpnp_adc_tm_rscale_fn_type - Scaling function used to convert the * channels input voltage/temperature to corresponding ADC code that is Loading @@ -274,6 +273,20 @@ enum qpnp_adc_tm_rscale_fn_type { SCALE_RSCALE_NONE, }; /** * enum qpnp_vadc_rscale_fn_type - Scaling function used to convert the * channels input voltage/temperature to corresponding ADC code that is * applied for thresholds. Check the corresponding channels scaling to * determine the appropriate temperature/voltage units that are passed * to the scaling function. The order below should match the one in the * driver for qpnp_adc_scale_fn[]. */ enum qpnp_vadc_rscale_fn_type { SCALE_RVADC_ABSOLUTE = 0, SCALE_RVADC_PMIC_THERM = 3, SCALE_RVADC_SCALE_NONE, }; /** * enum qpnp_adc_fast_avg_ctl - Provides ability to obtain single result * from the ADC that is an average of multiple measurement Loading Loading @@ -1511,6 +1524,24 @@ int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev, * returns internal mapped PMIC number and revision id. */ int qpnp_adc_get_revid_version(struct device *dev); /** * qpnp_vadc_channel_monitor() - Configures kernel clients a channel to * monitor the corresponding ADC channel for threshold detection. * Driver passes the high/low voltage threshold along * with the notification callback once the set thresholds * are crossed. * @param: Structure pointer of qpnp_adc_tm_btm_param type. * Clients pass the low/high temperature along with the threshold * notification callback. */ int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param); /** * qpnp_vadc_end_channel_monitor() - Disables recurring measurement mode for * VADC_USR and disables the bank. * @param: device instance for the VADC */ int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip); #else static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev, uint32_t channel, Loading Loading @@ -1656,6 +1687,11 @@ static inline int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev, { return -ENXIO; } static inline int qpnp_adc_get_revid_version(struct device *dev) { return -ENXIO; } static inline int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param) { return -ENXIO; } static inline int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip) { return -ENXIO; } #endif /* Public API */ Loading Loading
Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt +7 −1 Original line number Diff line number Diff line Loading @@ -13,7 +13,10 @@ Required properties: - address-cells : Must be one. - size-cells : Must be zero. - interrupts : The USR bank peripheral VADC interrupt. - interrupt-names : Should contain "eoc-int-en-set". - interrupt-names : Should contain "eoc-int-en-set" for EOC, "high-thr-en-set" for high threshold interrupts and "low-thr-en-set" for low threshold interrupts. High and low threshold interrupts are to be enabled if VADC_USR needs to support recurring measurement. - qcom,adc-bit-resolution : Bit resolution of the ADC. - qcom,adc-vdd-reference : Voltage reference used by the ADC. Loading @@ -25,6 +28,9 @@ Optional properties: - qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the PMIC type and revision for applying the appropriate temperature compensation parameters. -qcom,vadc-meas-int-mode : Enable VADC_USR to handle requests to perform recurring measurements for any one supported channel along with supporting single conversion requests. Client required property: - qcom,<consumer name>-vadc : The phandle to the corresponding vadc device. Loading
drivers/hwmon/qpnp-adc-voltage.c +467 −25 Original line number Diff line number Diff line Loading @@ -47,6 +47,9 @@ #define QPNP_VADC_STATUS1_REQ_STS BIT(1) #define QPNP_VADC_STATUS1_EOC BIT(0) #define QPNP_VADC_STATUS1_REQ_STS_EOC_MASK 0x3 #define QPNP_VADC_STATUS1_OP_MODE_MASK 0x18 #define QPNP_VADC_MEAS_INT_MODE 0x2 #define QPNP_VADC_STATUS2 0x9 #define QPNP_VADC_STATUS2_CONV_SEQ_STATE 6 #define QPNP_VADC_STATUS2_FIFO_NOT_EMPTY_FLAG BIT(1) Loading @@ -55,34 +58,31 @@ #define QPNP_VADC_CONV_TIMEOUT_ERR 2 #define QPNP_VADC_MODE_CTL 0x40 #define QPNP_VADC_OP_MODE_SHIFT 4 #define QPNP_VADC_OP_MODE_SHIFT 3 #define QPNP_VADC_VREF_XO_THM_FORCE BIT(2) #define QPNP_VADC_AMUX_TRIM_EN BIT(1) #define QPNP_VADC_ADC_TRIM_EN BIT(0) #define QPNP_VADC_TRIM_EN BIT(0) #define QPNP_VADC_EN_CTL1 0x46 #define QPNP_VADC_ADC_EN BIT(7) #define QPNP_VADC_ADC_CH_SEL_CTL 0x48 #define QPNP_VADC_ADC_DIG_PARAM 0x50 #define QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 3 #define QPNP_VADC_EN BIT(7) #define QPNP_VADC_CH_SEL_CTL 0x48 #define QPNP_VADC_DIG_PARAM 0x50 #define QPNP_VADC_DIG_DEC_RATIO_SEL_SHIFT 3 #define QPNP_VADC_HW_SETTLE_DELAY 0x51 #define QPNP_VADC_CONV_REQ 0x52 #define QPNP_VADC_CONV_REQ_SET BIT(7) #define QPNP_VADC_CONV_SEQ_CTL 0x54 #define QPNP_VADC_CONV_SEQ_HOLDOFF_SHIFT 4 #define QPNP_VADC_CONV_SEQ_TRIG_CTL 0x55 #define QPNP_VADC_MEAS_INTERVAL_CTL 0x57 #define QPNP_VADC_CONV_SEQ_FALLING_EDGE 0x0 #define QPNP_VADC_CONV_SEQ_RISING_EDGE 0x1 #define QPNP_VADC_CONV_SEQ_EDGE_SHIFT 7 #define QPNP_VADC_FAST_AVG_CTL 0x5a #define QPNP_VADC_M0_LOW_THR_LSB 0x5c #define QPNP_VADC_M0_LOW_THR_MSB 0x5d #define QPNP_VADC_M0_HIGH_THR_LSB 0x5e #define QPNP_VADC_M0_HIGH_THR_MSB 0x5f #define QPNP_VADC_M1_LOW_THR_LSB 0x69 #define QPNP_VADC_M1_LOW_THR_MSB 0x6a #define QPNP_VADC_M1_HIGH_THR_LSB 0x6b #define QPNP_VADC_M1_HIGH_THR_MSB 0x6c #define QPNP_VADC_LOW_THR_LSB 0x5c #define QPNP_VADC_LOW_THR_MSB 0x5d #define QPNP_VADC_HIGH_THR_LSB 0x5e #define QPNP_VADC_HIGH_THR_MSB 0x5f #define QPNP_VADC_ACCESS 0xd0 #define QPNP_VADC_ACCESS_DATA 0xa5 #define QPNP_VADC_PERH_RESET_CTL3 0xda Loading @@ -100,6 +100,21 @@ #define QPNP_VADC_CONV_TIME_MAX 2100 #define QPNP_ADC_COMPLETION_TIMEOUT HZ #define QPNP_VADC_ERR_COUNT 20 #define QPNP_OP_MODE_SHIFT 3 #define QPNP_VADC_THR_LSB_MASK(val) (val & 0xff) #define QPNP_VADC_THR_MSB_MASK(val) ((val & 0xff00) >> 8) #define QPNP_MIN_TIME 2000 #define QPNP_MAX_TIME 2000 #define QPNP_RETRY 100 struct qpnp_vadc_mode_state { bool meas_int_mode; bool meas_int_request_in_queue; bool vadc_meas_int_enable; struct qpnp_adc_tm_btm_param *param; struct qpnp_adc_amux vadc_meas_amux; }; struct qpnp_vadc_chip { struct device *dev; Loading @@ -115,6 +130,11 @@ struct qpnp_vadc_chip { bool vadc_poll_eoc; u8 revision_ana_minor; u8 revision_dig_major; struct workqueue_struct *high_thr_wq; struct workqueue_struct *low_thr_wq; struct work_struct trigger_high_thr_work; struct work_struct trigger_low_thr_work; struct qpnp_vadc_mode_state *state_copy; struct sensor_device_attribute sens_attr[0]; }; Loading @@ -135,6 +155,11 @@ static struct qpnp_vadc_scale_fn vadc_scale_fn[] = { [SCALE_NCP_03WF683_THERM] = {qpnp_adc_scale_therm_ncp03}, }; static struct qpnp_adc_tm_reverse_scale_fn adc_vadc_rscale_fn[] = { [SCALE_RVADC_ABSOLUTE] = {qpnp_adc_absolute_rthr}, [SCALE_RVADC_PMIC_THERM] = {qpnp_adc_scale_millidegc_pmic_voltage_thr}, }; static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg, u8 *data) { Loading Loading @@ -202,12 +227,25 @@ static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc) return 0; } static int32_t qpnp_vadc_mode_select(struct qpnp_vadc_chip *vadc, u8 mode_ctl) { int rc; mode_ctl |= (QPNP_VADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN); rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MODE_CTL, mode_ctl); if (rc < 0) pr_err("vadc write mode selection err:%d\n", rc); return rc; } static int32_t qpnp_vadc_enable(struct qpnp_vadc_chip *vadc, bool state) { int rc = 0; u8 data = 0; data = QPNP_VADC_ADC_EN; data = QPNP_VADC_EN; if (state) { rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_EN_CTL1, data); Loading @@ -217,7 +255,7 @@ static int32_t qpnp_vadc_enable(struct qpnp_vadc_chip *vadc, bool state) } } else { rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_EN_CTL1, (~data & QPNP_VADC_ADC_EN)); (~data & QPNP_VADC_EN)); if (rc < 0) { pr_err("VADC disable failed\n"); return rc; Loading @@ -238,13 +276,13 @@ static int32_t qpnp_vadc_status_debug(struct qpnp_vadc_chip *vadc) return rc; } rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_ADC_DIG_PARAM, &dig); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_DIG_PARAM, &dig); if (rc < 0) { pr_err("digital param read failed with %d\n", rc); return rc; } rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_ADC_CH_SEL_CTL, &chan); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_CH_SEL_CTL, &chan); if (rc < 0) { pr_err("channel read failed with %d\n", rc); return rc; Loading Loading @@ -288,16 +326,15 @@ static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc, /* Mode selection */ mode_ctrl |= ((chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT) | (QPNP_VADC_ADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN)); (QPNP_VADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN)); rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MODE_CTL, mode_ctrl); if (rc < 0) { pr_err("Mode configure write error\n"); return rc; } /* Channel selection */ rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ADC_CH_SEL_CTL, rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_CH_SEL_CTL, chan_prop->amux_channel); if (rc < 0) { pr_err("Channel configure error\n"); Loading @@ -306,8 +343,8 @@ static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc, /* Digital parameter setup */ decimation = chan_prop->decimation << QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT; rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ADC_DIG_PARAM, decimation); QPNP_VADC_DIG_DEC_RATIO_SEL_SHIFT; rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_DIG_PARAM, decimation); if (rc < 0) { pr_err("Digital parameter configure write error\n"); return rc; Loading @@ -321,6 +358,10 @@ static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc, return rc; } pr_debug("mode:%d, channel:%d, decimation:%d, hw_settle:%d\n", mode_ctrl, chan_prop->amux_channel, decimation, chan_prop->hw_settle_time); if (chan_prop->mode_sel == (ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT)) { /* Normal measurement mode */ Loading Loading @@ -472,6 +513,33 @@ static void qpnp_vadc_work(struct work_struct *work) return; } static void qpnp_vadc_low_thr_fn(struct work_struct *work) { struct qpnp_vadc_chip *vadc = container_of(work, struct qpnp_vadc_chip, trigger_low_thr_work); vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = false; vadc->state_copy->param->threshold_notification( ADC_TM_LOW_STATE, vadc->state_copy->param->btm_ctx); return; } static void qpnp_vadc_high_thr_fn(struct work_struct *work) { struct qpnp_vadc_chip *vadc = container_of(work, struct qpnp_vadc_chip, trigger_high_thr_work); vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = false; vadc->state_copy->param->threshold_notification( ADC_TM_HIGH_STATE, vadc->state_copy->param->btm_ctx); return; } static irqreturn_t qpnp_vadc_isr(int irq, void *dev_id) { struct qpnp_vadc_chip *vadc = dev_id; Loading @@ -481,6 +549,34 @@ static irqreturn_t qpnp_vadc_isr(int irq, void *dev_id) return IRQ_HANDLED; } static irqreturn_t qpnp_vadc_low_thr_isr(int irq, void *data) { struct qpnp_vadc_chip *vadc = data; u8 mode_ctl = 0; mode_ctl = ADC_OP_NORMAL_MODE; /* Set measurement in single measurement mode */ qpnp_vadc_mode_select(vadc, mode_ctl); qpnp_vadc_enable(vadc, false); schedule_work(&vadc->trigger_low_thr_work); return IRQ_HANDLED; } static irqreturn_t qpnp_vadc_high_thr_isr(int irq, void *data) { struct qpnp_vadc_chip *vadc = data; u8 mode_ctl = 0; mode_ctl = ADC_OP_NORMAL_MODE; /* Set measurement in single measurement mode */ qpnp_vadc_mode_select(vadc, mode_ctl); qpnp_vadc_enable(vadc, false); schedule_work(&vadc->trigger_high_thr_work); return IRQ_HANDLED; } static int32_t qpnp_vadc_version_check(struct qpnp_vadc_chip *dev) { uint8_t revision; Loading Loading @@ -1178,6 +1274,123 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc, } EXPORT_SYMBOL(qpnp_get_vadc_gain_and_offset); static int32_t qpnp_vadc_wait_for_req_sts_check(struct qpnp_vadc_chip *vadc) { u8 status1 = 0; int rc, count = 0; /* Re-enable the peripheral */ rc = qpnp_vadc_enable(vadc, true); if (rc) { pr_err("vadc re-enable peripheral failed with %d\n", rc); return rc; } /* The VADC_TM bank needs to be disabled for new conversion request */ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1); if (rc) { pr_err("vadc read status1 failed with %d\n", rc); return rc; } /* Disable the bank if a conversion is occuring */ while ((status1 & QPNP_VADC_STATUS1_REQ_STS) && (count < QPNP_RETRY)) { /* Wait time is based on the optimum sampling rate * and adding enough time buffer to account for ADC conversions * occuring on different peripheral banks */ usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1); if (rc < 0) { pr_err("vadc disable failed with %d\n", rc); return rc; } count++; } if (count >= QPNP_RETRY) pr_err("QPNP vadc status req bit did not fall low!!\n"); rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1); /* Disable the peripheral */ rc = qpnp_vadc_enable(vadc, false); if (rc < 0) pr_err("vadc peripheral disable failed with %d\n", rc); return rc; } static int32_t qpnp_vadc_manage_meas_int_requests(struct qpnp_vadc_chip *chip) { struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev); int rc = 0, dt_index = 0; u8 mode_ctl = 0; pr_debug("meas_int_mode:0x%x, mode_ctl:%0x\n", vadc->state_copy->meas_int_mode, mode_ctl); if (vadc->state_copy->meas_int_mode) { pr_debug("meas interval in progress. Procced to disable it\n"); /* measurement interval in progress. Proceed to disable it */ mode_ctl = ADC_OP_NORMAL_MODE; rc = qpnp_vadc_mode_select(vadc, mode_ctl); if (rc < 0) { pr_err("NORM mode select failed with %d\n", rc); return rc; } /* Disable bank */ rc = qpnp_vadc_enable(vadc, false); if (rc) { pr_err("Disable bank failed with %d\n", rc); return rc; } /* Check if a conversion is in progress */ rc = qpnp_vadc_wait_for_req_sts_check(vadc); if (rc < 0) { pr_err("req_sts check failed with %d\n", rc); return rc; } vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = true; } else if (vadc->state_copy->meas_int_request_in_queue) { /* put the meas interval back in queue */ pr_debug("put meas interval back in queue\n"); vadc->adc->amux_prop->amux_channel = vadc->state_copy->vadc_meas_amux.channel_num; while ((vadc->adc->adc_channels[dt_index].channel_num != vadc->adc->amux_prop->amux_channel) && (dt_index < vadc->max_channels_available)) dt_index++; if (dt_index >= vadc->max_channels_available) { pr_err("not a valid VADC channel\n"); rc = -EINVAL; return rc; } vadc->adc->amux_prop->decimation = vadc->adc->amux_prop->decimation; vadc->adc->amux_prop->hw_settle_time = vadc->adc->amux_prop->hw_settle_time; vadc->adc->amux_prop->fast_avg_setup = vadc->adc->amux_prop->fast_avg_setup; vadc->adc->amux_prop->mode_sel = ADC_OP_MEASUREMENT_INTERVAL; rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop); if (rc) { pr_err("vadc configure failed with %d\n", rc); return rc; } vadc->state_copy->meas_int_mode = true; vadc->state_copy->meas_int_request_in_queue = false; } dev_set_drvdata(vadc->dev, vadc); return 0; } struct qpnp_vadc_chip *qpnp_get_vadc(struct device *dev, const char *name) { struct qpnp_vadc_chip *vadc; Loading Loading @@ -1211,6 +1424,9 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc, mutex_lock(&vadc->adc->adc_lock); if (vadc->state_copy->vadc_meas_int_enable) qpnp_vadc_manage_meas_int_requests(vadc); if (!vadc->vadc_init_calib) { rc = qpnp_vadc_version_check(vadc); if (rc) Loading Loading @@ -1346,6 +1562,9 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc, vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result); fail_unlock: if (vadc->state_copy->vadc_meas_int_enable) qpnp_vadc_manage_meas_int_requests(vadc); mutex_unlock(&vadc->adc->adc_lock); return rc; Loading Loading @@ -1502,6 +1721,175 @@ fail: } EXPORT_SYMBOL(qpnp_vadc_iadc_sync_complete_request); static int32_t qpnp_vadc_thr_update(struct qpnp_vadc_chip *vadc, int32_t high_thr, int32_t low_thr) { int rc = 0; pr_debug("client requested high:%d and low:%d\n", high_thr, low_thr); rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_LOW_THR_LSB, QPNP_VADC_THR_LSB_MASK(low_thr)); if (rc < 0) { pr_err("low threshold lsb setting failed, err:%d\n", rc); return rc; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_LOW_THR_MSB, QPNP_VADC_THR_MSB_MASK(low_thr)); if (rc < 0) { pr_err("low threshold msb setting failed, err:%d\n", rc); return rc; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HIGH_THR_LSB, QPNP_VADC_THR_LSB_MASK(high_thr)); if (rc < 0) { pr_err("high threshold lsb setting failed, err:%d\n", rc); return rc; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HIGH_THR_MSB, QPNP_VADC_THR_MSB_MASK(high_thr)); if (rc < 0) { pr_err("high threshold msb setting failed, err:%d\n", rc); return rc; } pr_debug("client requested high:%d and low:%d\n", high_thr, low_thr); return rc; } int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param) { uint32_t channel, scale_type = 0; uint32_t low_thr = 0, high_thr = 0; int rc = 0, idx = 0; struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev); if (qpnp_vadc_is_valid(vadc)) return -EPROBE_DEFER; if (!vadc->state_copy->vadc_meas_int_enable) { pr_err("Recurring measurement interval not available\n"); return -EINVAL; } if (param->threshold_notification == NULL) { pr_debug("No notification for high/low temp??\n"); return -EINVAL; } mutex_lock(&vadc->adc->adc_lock); channel = param->channel; while (idx < vadc->max_channels_available) { if (vadc->adc->adc_channels[idx].channel_num == channel) break; else idx++; } if (idx >= vadc->max_channels_available) { pr_err("not a valid VADC channel\n"); rc = -EINVAL; goto fail_unlock; } scale_type = vadc->adc->adc_channels[idx].adc_scale_fn; if (scale_type >= SCALE_RSCALE_NONE) { rc = -EBADF; goto fail_unlock; } pr_debug("channel:%d, scale_type:%d, dt_idx:%d", channel, scale_type, idx); vadc->adc->amux_prop->amux_channel = channel; vadc->adc->amux_prop->decimation = vadc->adc->adc_channels[idx].adc_decimation; vadc->adc->amux_prop->hw_settle_time = vadc->adc->adc_channels[idx].hw_settle_time; vadc->adc->amux_prop->fast_avg_setup = vadc->adc->adc_channels[idx].fast_avg_setup; vadc->adc->amux_prop->mode_sel = ADC_OP_MEASUREMENT_INTERVAL; adc_vadc_rscale_fn[scale_type].chan(vadc, param, &low_thr, &high_thr); if (param->timer_interval >= ADC_MEAS1_INTERVAL_NONE) { pr_err("Invalid timer interval :%d\n", param->timer_interval); goto fail_unlock; } rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MEAS_INTERVAL_CTL, param->timer_interval); if (rc) { pr_err("vadc meas timer failed with %d\n", rc); goto fail_unlock; } rc = qpnp_vadc_thr_update(vadc, high_thr, low_thr); if (rc) { pr_err("vadc thr update failed with %d\n", rc); goto fail_unlock; } rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop); if (rc) { pr_err("vadc configure failed with %d\n", rc); goto fail_unlock; } vadc->state_copy->meas_int_mode = true; vadc->state_copy->param = param; vadc->state_copy->vadc_meas_amux.channel_num = channel; vadc->state_copy->vadc_meas_amux.adc_decimation = vadc->adc->amux_prop->decimation; vadc->state_copy->vadc_meas_amux.hw_settle_time = vadc->adc->amux_prop->hw_settle_time; vadc->state_copy->vadc_meas_amux.fast_avg_setup = vadc->adc->amux_prop->fast_avg_setup; vadc->state_copy->meas_int_request_in_queue = false; dev_set_drvdata(vadc->dev, vadc); fail_unlock: mutex_unlock(&vadc->adc->adc_lock); return rc; } EXPORT_SYMBOL(qpnp_vadc_channel_monitor); int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip) { struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev); u8 mode_ctl = 0; if (qpnp_vadc_is_valid(vadc)) return -EPROBE_DEFER; if (!vadc->state_copy->vadc_meas_int_enable) { pr_err("Recurring measurement interval not available\n"); return -EINVAL; } vadc->state_copy->meas_int_mode = false; vadc->state_copy->meas_int_request_in_queue = false; dev_set_drvdata(vadc->dev, vadc); mode_ctl = ADC_OP_NORMAL_MODE; /* Set measurement in single measurement mode */ qpnp_vadc_mode_select(vadc, mode_ctl); qpnp_vadc_enable(vadc, false); return 0; } EXPORT_SYMBOL(qpnp_vadc_end_channel_monitor); static ssize_t qpnp_adc_show(struct device *dev, struct device_attribute *devattr, char *buf) { Loading Loading @@ -1589,6 +1977,13 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) return -ENOMEM; } vadc->state_copy = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_vadc_mode_state), GFP_KERNEL); if (!vadc->state_copy) { dev_err(&spmi->dev, "Unable to allocate memory\n"); return -ENOMEM; } vadc->adc = adc_qpnp; rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc); if (rc) { Loading Loading @@ -1651,6 +2046,49 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) } else device_init_wakeup(vadc->dev, 1); vadc->state_copy->vadc_meas_int_enable = of_property_read_bool(node, "qcom,vadc-meas-int-mode"); if (vadc->state_copy->vadc_meas_int_enable) { vadc->adc->adc_high_thr_irq = spmi_get_irq_byname(spmi, NULL, "high-thr-en-set"); if (vadc->adc->adc_high_thr_irq < 0) { pr_err("Invalid irq\n"); rc = -ENXIO; goto err_setup; } vadc->adc->adc_low_thr_irq = spmi_get_irq_byname(spmi, NULL, "low-thr-en-set"); if (vadc->adc->adc_low_thr_irq < 0) { pr_err("Invalid irq\n"); rc = -ENXIO; goto err_setup; } rc = devm_request_irq(&spmi->dev, vadc->adc->adc_high_thr_irq, qpnp_vadc_high_thr_isr, IRQF_TRIGGER_RISING, "qpnp_vadc_high_interrupt", vadc); if (rc) { dev_err(&spmi->dev, "failed to request adc irq\n"); goto err_setup; } else { enable_irq_wake(vadc->adc->adc_high_thr_irq); } rc = devm_request_irq(&spmi->dev, vadc->adc->adc_low_thr_irq, qpnp_vadc_low_thr_isr, IRQF_TRIGGER_RISING, "qpnp_vadc_low_interrupt", vadc); if (rc) { dev_err(&spmi->dev, "failed to request adc irq\n"); goto err_setup; } else { enable_irq_wake(vadc->adc->adc_low_thr_irq); } INIT_WORK(&vadc->trigger_high_thr_work, qpnp_vadc_high_thr_fn); INIT_WORK(&vadc->trigger_low_thr_work, qpnp_vadc_low_thr_fn); } vadc->vadc_iadc_sync_lock = false; dev_set_drvdata(&spmi->dev, vadc); list_add(&vadc->list, &qpnp_vadc_device_list); Loading Loading @@ -1698,8 +2136,11 @@ static int qpnp_vadc_suspend_noirq(struct device *dev) struct qpnp_vadc_chip *vadc = dev_get_drvdata(dev); u8 status = 0; if (vadc->vadc_poll_eoc) { qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status); if (((status & QPNP_VADC_STATUS1_OP_MODE_MASK) >> QPNP_VADC_OP_MODE_SHIFT) == QPNP_VADC_MEAS_INT_MODE) { pr_debug("Meas interval in progress\n"); } else if (vadc->vadc_poll_eoc) { status &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK; pr_debug("vadc conversion status=%d\n", status); if (status != QPNP_VADC_STATUS1_EOC) { Loading @@ -1708,6 +2149,7 @@ static int qpnp_vadc_suspend_noirq(struct device *dev) return -EBUSY; } } return 0; } Loading
include/linux/qpnp/qpnp-adc.h +37 −1 Original line number Diff line number Diff line Loading @@ -251,7 +251,6 @@ enum qpnp_adc_scale_fn_type { SCALE_NONE, }; /** * enum qpnp_adc_tm_rscale_fn_type - Scaling function used to convert the * channels input voltage/temperature to corresponding ADC code that is Loading @@ -274,6 +273,20 @@ enum qpnp_adc_tm_rscale_fn_type { SCALE_RSCALE_NONE, }; /** * enum qpnp_vadc_rscale_fn_type - Scaling function used to convert the * channels input voltage/temperature to corresponding ADC code that is * applied for thresholds. Check the corresponding channels scaling to * determine the appropriate temperature/voltage units that are passed * to the scaling function. The order below should match the one in the * driver for qpnp_adc_scale_fn[]. */ enum qpnp_vadc_rscale_fn_type { SCALE_RVADC_ABSOLUTE = 0, SCALE_RVADC_PMIC_THERM = 3, SCALE_RVADC_SCALE_NONE, }; /** * enum qpnp_adc_fast_avg_ctl - Provides ability to obtain single result * from the ADC that is an average of multiple measurement Loading Loading @@ -1511,6 +1524,24 @@ int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev, * returns internal mapped PMIC number and revision id. */ int qpnp_adc_get_revid_version(struct device *dev); /** * qpnp_vadc_channel_monitor() - Configures kernel clients a channel to * monitor the corresponding ADC channel for threshold detection. * Driver passes the high/low voltage threshold along * with the notification callback once the set thresholds * are crossed. * @param: Structure pointer of qpnp_adc_tm_btm_param type. * Clients pass the low/high temperature along with the threshold * notification callback. */ int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param); /** * qpnp_vadc_end_channel_monitor() - Disables recurring measurement mode for * VADC_USR and disables the bank. * @param: device instance for the VADC */ int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip); #else static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev, uint32_t channel, Loading Loading @@ -1656,6 +1687,11 @@ static inline int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev, { return -ENXIO; } static inline int qpnp_adc_get_revid_version(struct device *dev) { return -ENXIO; } static inline int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param) { return -ENXIO; } static inline int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip) { return -ENXIO; } #endif /* Public API */ Loading