Loading drivers/thermal/qcom/adc-tm-common.c +19 −0 Original line number Original line Diff line number Diff line Loading @@ -129,5 +129,24 @@ void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, } } EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k); EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k); int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data, struct adc_tm_config *tm_config) { int64_t low_thr = 0, high_thr = 0; low_thr = div_s64(tm_config->low_thr_voltage, tm_config->prescal); low_thr *= data->full_scale_code_volt; low_thr = div64_s64(low_thr, ADC_HC_VDD_REF); tm_config->low_thr_voltage = low_thr; high_thr = div_s64(tm_config->high_thr_voltage, tm_config->prescal); high_thr *= data->full_scale_code_volt; high_thr = div64_s64(high_thr, ADC_HC_VDD_REF); tm_config->high_thr_voltage = high_thr; return 0; } EXPORT_SYMBOL(adc_tm_absolute_rthr); MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver"); MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver"); MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2"); drivers/thermal/qcom/adc-tm.c +94 −22 Original line number Original line Diff line number Diff line Loading @@ -77,14 +77,15 @@ static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num, for (i = 0; i < dt_chan_num; i++) { for (i = 0; i < dt_chan_num; i++) { adc_tm->sensor[i].chip = adc_tm; adc_tm->sensor[i].chip = adc_tm; if (!adc_tm->sensor[i].non_thermal) { if (set_trips) if (set_trips) tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev, tzd = devm_thermal_zone_of_sensor_register( adc_tm->sensor[i].adc_ch, &adc_tm->sensor[i], adc_tm->dev, adc_tm->sensor[i].adc_ch, &adc_tm_ops); &adc_tm->sensor[i], &adc_tm_ops); else else tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev, tzd = devm_thermal_zone_of_sensor_register( adc_tm->sensor[i].adc_ch, &adc_tm->sensor[i], adc_tm->dev, adc_tm->sensor[i].adc_ch, &adc_tm_ops_iio); &adc_tm->sensor[i], &adc_tm_ops_iio); if (IS_ERR(tzd)) { if (IS_ERR(tzd)) { pr_err("Error registering TZ zone:%d for dt_ch:%d\n", pr_err("Error registering TZ zone:%d for dt_ch:%d\n", Loading @@ -92,6 +93,8 @@ static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num, continue; continue; } } adc_tm->sensor[i].tzd = tzd; adc_tm->sensor[i].tzd = tzd; } else adc_tm->sensor[i].tzd = NULL; } } return 0; return 0; Loading Loading @@ -130,6 +133,39 @@ static int adc_tm_decimation_from_dt(u32 value, const unsigned int *decimation) return -EINVAL; return -EINVAL; } } struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name) { struct platform_device *pdev; struct adc_tm_chip *chip; struct device_node *node = NULL; char prop_name[MAX_PROP_NAME_LEN]; snprintf(prop_name, MAX_PROP_NAME_LEN, "qcom,%s-adc_tm", name); node = of_parse_phandle(dev->of_node, prop_name, 0); if (node == NULL) return ERR_PTR(-ENODEV); list_for_each_entry(chip, &adc_tm_device_list, list) { pdev = to_platform_device(chip->dev); if (pdev->dev.of_node == node) return chip; } return ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(get_adc_tm); int adc_tm_is_valid(struct adc_tm_chip *chip) { struct adc_tm_chip *adc_tm_chip = NULL; list_for_each_entry(adc_tm_chip, &adc_tm_device_list, list) if (chip == adc_tm_chip) return 0; return -EINVAL; } static const struct of_device_id adc_tm_match_table[] = { static const struct of_device_id adc_tm_match_table[] = { { { .compatible = "qcom,adc-tm5", .compatible = "qcom,adc-tm5", Loading Loading @@ -195,9 +231,11 @@ static int adc_tm_get_dt_data(struct platform_device *pdev, adc_tm->prop.timer3 = ADC_TM_TIMER3; adc_tm->prop.timer3 = ADC_TM_TIMER3; for_each_child_of_node(node, child) { for_each_child_of_node(node, child) { int channel_num, i = 0; int channel_num, i = 0, adc_rscale_fn = 0; int calib_type = 0, ret, hw_settle_time = 0; int calib_type = 0, ret, hw_settle_time = 0; int prescal = 0; struct iio_channel *chan_adc; struct iio_channel *chan_adc; bool non_thermal = false; ret = of_property_read_u32(child, "reg", &channel_num); ret = of_property_read_u32(child, "reg", &channel_num); if (ret) { if (ret) { Loading @@ -224,12 +262,39 @@ static int adc_tm_get_dt_data(struct platform_device *pdev, else else calib_type = ADC_ABS_CAL; calib_type = ADC_ABS_CAL; if (of_property_read_bool(child, "qcom,kernel-client")) non_thermal = true; ret = of_property_read_u32(child, "qcom,scale-type", &adc_rscale_fn); if (ret) adc_rscale_fn = SCALE_RSCALE_NONE; ret = of_property_read_u32(child, "qcom,prescaling", &prescal); if (ret) prescal = 1; /* Individual channel properties */ /* Individual channel properties */ adc_tm->sensor[idx].adc_ch = channel_num; adc_tm->sensor[idx].adc_ch = channel_num; adc_tm->sensor[idx].cal_sel = calib_type; adc_tm->sensor[idx].cal_sel = calib_type; /* Default to 1 second timer select */ /* Default to 1 second timer select */ adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2; adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2; adc_tm->sensor[idx].hw_settle_time = hw_settle_time; adc_tm->sensor[idx].hw_settle_time = hw_settle_time; adc_tm->sensor[idx].adc_rscale_fn = adc_rscale_fn; adc_tm->sensor[idx].non_thermal = non_thermal; adc_tm->sensor[idx].prescaling = prescal; if (adc_tm->sensor[idx].non_thermal) { adc_tm->sensor[idx].req_wq = alloc_workqueue( "qpnp_adc_notify_wq", WQ_HIGHPRI, 0); if (!adc_tm->sensor[idx].req_wq) { pr_err("Requesting priority wq failed\n"); return -ENOMEM; } INIT_WORK(&adc_tm->sensor[idx].work, notify_adc_tm_fn); } INIT_LIST_HEAD(&adc_tm->sensor[idx].thr_list); while (i < dt_chan_num) { while (i < dt_chan_num) { chan_adc = &chan[i]; chan_adc = &chan[i]; if (chan_adc->channel->channel == channel_num) if (chan_adc->channel->channel == channel_num) Loading @@ -249,7 +314,7 @@ static int adc_tm_probe(struct platform_device *pdev) struct adc_tm_chip *adc_tm; struct adc_tm_chip *adc_tm; struct regmap *regmap; struct regmap *regmap; struct iio_channel *channels; struct iio_channel *channels; int ret, dt_chan_num = 0, indio_chan_count = 0; int ret = 0, dt_chan_num = 0, indio_chan_count = 0, i = 0; u32 reg; u32 reg; if (!node) if (!node) Loading Loading @@ -313,7 +378,7 @@ static int adc_tm_probe(struct platform_device *pdev) ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false); ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false); if (ret) { if (ret) { dev_err(dev, "adc-tm failed to register with of thermal\n"); dev_err(dev, "adc-tm failed to register with of thermal\n"); return ret; goto fail; } } return 0; return 0; } } Loading @@ -321,25 +386,32 @@ static int adc_tm_probe(struct platform_device *pdev) ret = adc_tm_init(adc_tm, dt_chan_num); ret = adc_tm_init(adc_tm, dt_chan_num); if (ret) { if (ret) { dev_err(dev, "adc-tm init failed\n"); dev_err(dev, "adc-tm init failed\n"); return ret; goto fail; } } ret = adc_tm_register_tzd(adc_tm, dt_chan_num, true); ret = adc_tm_register_tzd(adc_tm, dt_chan_num, true); if (ret) { if (ret) { dev_err(dev, "adc-tm failed to register with of thermal\n"); dev_err(dev, "adc-tm failed to register with of thermal\n"); return ret; goto fail; } } ret = adc_tm_register_interrupts(adc_tm); ret = adc_tm_register_interrupts(adc_tm); if (ret) { if (ret) { pr_err("adc-tm register interrupts failed:%d\n", ret); pr_err("adc-tm register interrupts failed:%d\n", ret); return ret; goto fail; } } list_add_tail(&adc_tm->list, &adc_tm_device_list); list_add_tail(&adc_tm->list, &adc_tm_device_list); platform_set_drvdata(pdev, adc_tm); platform_set_drvdata(pdev, adc_tm); return 0; return 0; fail: i = 0; while (i < dt_chan_num) { if (adc_tm->sensor[i].req_wq) destroy_workqueue(adc_tm->sensor[i].req_wq); i++; } return ret; } } static int adc_tm_remove(struct platform_device *pdev) static int adc_tm_remove(struct platform_device *pdev) Loading drivers/thermal/qcom/adc-tm.h +118 −0 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,8 @@ struct adc_tm_chip; #define ADC_TM_TIMER1 3 /* 3.9ms */ #define ADC_TM_TIMER1 3 /* 3.9ms */ #define ADC_TM_TIMER2 10 /* 1 second */ #define ADC_TM_TIMER2 10 /* 1 second */ #define ADC_TM_TIMER3 4 /* 4 second */ #define ADC_TM_TIMER3 4 /* 4 second */ #define ADC_HC_VDD_REF 1875000 #define MAX_PROP_NAME_LEN 32 enum adc_cal_method { enum adc_cal_method { ADC_NO_CAL = 0, ADC_NO_CAL = 0, Loading @@ -53,6 +55,72 @@ enum adc_timer_select { ADC_TIMER_SEL_NONE, ADC_TIMER_SEL_NONE, }; }; /** * enum adc_tm_state - This lets the client know whether the threshold * that was crossed was high/low. * %ADC_TM_HIGH_STATE: Client is notified of crossing the requested high * voltage threshold. * %ADC_TM_COOL_STATE: Client is notified of crossing the requested cool * temperature threshold. * %ADC_TM_LOW_STATE: Client is notified of crossing the requested low * voltage threshold. * %ADC_TM_WARM_STATE: Client is notified of crossing the requested high * temperature threshold. */ enum adc_tm_state { ADC_TM_HIGH_STATE = 0, ADC_TM_COOL_STATE = ADC_TM_HIGH_STATE, ADC_TM_LOW_STATE, ADC_TM_WARM_STATE = ADC_TM_LOW_STATE, ADC_TM_STATE_NUM, }; /** * enum adc_tm_state_request - Request to enable/disable the corresponding * high/low voltage/temperature thresholds. * %ADC_TM_HIGH_THR_ENABLE: Enable high voltage threshold. * %ADC_TM_COOL_THR_ENABLE = Enables cool temperature threshold. * %ADC_TM_LOW_THR_ENABLE: Enable low voltage/temperature threshold. * %ADC_TM_WARM_THR_ENABLE = Enables warm temperature threshold. * %ADC_TM_HIGH_LOW_THR_ENABLE: Enable high and low voltage/temperature * threshold. * %ADC_TM_HIGH_THR_DISABLE: Disable high voltage/temperature threshold. * %ADC_TM_COOL_THR_ENABLE = Disables cool temperature threshold. * %ADC_TM_LOW_THR_DISABLE: Disable low voltage/temperature threshold. * %ADC_TM_WARM_THR_ENABLE = Disables warm temperature threshold. * %ADC_TM_HIGH_THR_DISABLE: Disable high and low voltage/temperature * threshold. */ enum adc_tm_state_request { ADC_TM_HIGH_THR_ENABLE = 0, ADC_TM_COOL_THR_ENABLE = ADC_TM_HIGH_THR_ENABLE, ADC_TM_LOW_THR_ENABLE, ADC_TM_WARM_THR_ENABLE = ADC_TM_LOW_THR_ENABLE, ADC_TM_HIGH_LOW_THR_ENABLE, ADC_TM_HIGH_THR_DISABLE, ADC_TM_COOL_THR_DISABLE = ADC_TM_HIGH_THR_DISABLE, ADC_TM_LOW_THR_DISABLE, ADC_TM_WARM_THR_DISABLE = ADC_TM_LOW_THR_DISABLE, ADC_TM_HIGH_LOW_THR_DISABLE, ADC_TM_THR_NUM, }; /** * enum adc_tm_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. Example battery follows the power supply * framework that needs its units to be in decidegreesC so it passes * deci-degreesC. PA_THERM clients pass the temperature in degrees. * The order below should match the one in the driver for * adc_tm_rscale_fn[]. */ enum adc_tm_rscale_fn_type { SCALE_R_ABSOLUTE = 0, SCALE_RSCALE_NONE, }; struct adc_tm_sensor { struct adc_tm_sensor { struct adc_tm_chip *chip; struct adc_tm_chip *chip; struct thermal_zone_device *tzd; struct thermal_zone_device *tzd; Loading @@ -63,7 +131,36 @@ struct adc_tm_sensor { unsigned int btm_ch; unsigned int btm_ch; unsigned int prescaling; unsigned int prescaling; unsigned int timer_select; unsigned int timer_select; enum adc_tm_rscale_fn_type adc_rscale_fn; struct iio_channel *adc; struct iio_channel *adc; struct list_head thr_list; bool non_thermal; bool high_thr_triggered; bool low_thr_triggered; struct workqueue_struct *req_wq; struct work_struct work; }; struct adc_tm_param { int low_thr; int high_thr; uint32_t channel; enum adc_tm_state_request state_request; void *btm_ctx; void (*threshold_notification)(enum adc_tm_state state, void *ctx); }; struct adc_tm_client_info { struct list_head list; struct adc_tm_param *param; int32_t low_thr_requested; int32_t high_thr_requested; bool notify_low_thr; bool notify_high_thr; bool high_thr_set; bool low_thr_set; enum adc_tm_state_request state_request; }; }; struct adc_tm_cmn_prop { struct adc_tm_cmn_prop { Loading @@ -89,6 +186,7 @@ struct adc_tm_chip { u16 base; u16 base; struct adc_tm_cmn_prop prop; struct adc_tm_cmn_prop prop; spinlock_t adc_tm_lock; spinlock_t adc_tm_lock; struct mutex adc_mutex_lock; const struct adc_tm_ops *ops; const struct adc_tm_ops *ops; const struct adc_tm_data *data; const struct adc_tm_data *data; unsigned int dt_channels; unsigned int dt_channels; Loading Loading @@ -182,12 +280,23 @@ struct adc_tm_trip_reg_type { struct adc_tm_config { struct adc_tm_config { int channel; int channel; int adc_code; int adc_code; int prescal; int high_thr_temp; int high_thr_temp; int low_thr_temp; int low_thr_temp; int64_t high_thr_voltage; int64_t high_thr_voltage; int64_t low_thr_voltage; int64_t low_thr_voltage; }; }; /** * struct adc_tm_reverse_scale_fn - Reverse scaling prototype * @chan: Function pointer to one of the scaling functions * which takes the adc properties and returns the physical result */ struct adc_tm_reverse_scale_fn { int32_t (*chan)(const struct adc_tm_data *, struct adc_tm_config *); }; /** /** * struct adc_map_pt - Map the graph representation for ADC channel * struct adc_map_pt - Map the graph representation for ADC channel * @x: Represent the ADC digitized code. * @x: Represent the ADC digitized code. Loading Loading @@ -217,4 +326,13 @@ struct adc_tm_linear_graph { void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, const struct adc_tm_data *data); const struct adc_tm_data *data); int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data, struct adc_tm_config *tm_config); void notify_adc_tm_fn(struct work_struct *work); struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name); int adc_tm_is_valid(struct adc_tm_chip *chip); #endif /* __QCOM_ADC_TM_H__ */ #endif /* __QCOM_ADC_TM_H__ */ drivers/thermal/qcom/adc-tm5.c +496 −9 Original line number Original line Diff line number Diff line Loading @@ -76,6 +76,10 @@ static struct adc_tm_trip_reg_type adc_tm_ch_data[] = { [ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL}, [ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL}, }; }; static struct adc_tm_reverse_scale_fn adc_tm_rscale_fn[] = { [SCALE_R_ABSOLUTE] = {adc_tm_absolute_rthr}, }; static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp) static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp) { { int ret, milli_celsius; int ret, milli_celsius; Loading Loading @@ -219,6 +223,441 @@ static int adc_tm5_configure(struct adc_tm_sensor *sensor, return 0; return 0; } } static int32_t adc_tm_add_to_list(struct adc_tm_chip *chip, uint32_t dt_index, struct adc_tm_param *param) { struct adc_tm_client_info *client_info = NULL; bool client_info_exists = false; list_for_each_entry(client_info, &chip->sensor[dt_index].thr_list, list) { if (client_info->param == param) { client_info->low_thr_requested = param->low_thr; client_info->high_thr_requested = param->high_thr; client_info->state_request = param->state_request; client_info->notify_low_thr = false; client_info->notify_high_thr = false; client_info_exists = true; pr_debug("client found\n"); } } if (!client_info_exists) { client_info = devm_kzalloc(chip->dev, sizeof(struct adc_tm_client_info), GFP_KERNEL); if (!client_info) return -ENOMEM; pr_debug("new client\n"); client_info->param = param; client_info->low_thr_requested = param->low_thr; client_info->high_thr_requested = param->high_thr; client_info->state_request = param->state_request; list_add_tail(&client_info->list, &chip->sensor[dt_index].thr_list); } return 0; } static int32_t adc_tm5_thr_update(struct adc_tm_sensor *sensor, int32_t high_thr, int32_t low_thr) { int ret = 0; u8 trip_low_thr[2], trip_high_thr[2]; uint16_t reg_low_thr_lsb, reg_high_thr_lsb; uint32_t scale_type = 0, mask = 0, btm_chan_idx = 0; struct adc_tm_config tm_config; struct adc_tm_chip *chip; ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx\n"); return ret; } chip = sensor->chip; tm_config.high_thr_voltage = (int64_t)high_thr; tm_config.low_thr_voltage = (int64_t)low_thr; tm_config.prescal = sensor->prescaling; scale_type = sensor->adc_rscale_fn; if (scale_type >= SCALE_RSCALE_NONE) { ret = -EBADF; return ret; } adc_tm_rscale_fn[scale_type].chan(chip->data, &tm_config); mask = lower_32_bits(tm_config.high_thr_voltage); trip_high_thr[0] = ADC_TM_LOWER_MASK(mask); trip_high_thr[1] = ADC_TM_UPPER_MASK(mask); mask = lower_32_bits(tm_config.low_thr_voltage); trip_low_thr[0] = ADC_TM_LOWER_MASK(mask); trip_low_thr[1] = ADC_TM_UPPER_MASK(mask); pr_debug("high_thr:0x%llx, low_thr:0x%llx\n", tm_config.high_thr_voltage, tm_config.low_thr_voltage); reg_low_thr_lsb = ADC_TM_Mn_LOW_THR0(btm_chan_idx); reg_high_thr_lsb = ADC_TM_Mn_HIGH_THR0(btm_chan_idx); if (low_thr != INT_MIN) { ret = adc_tm5_write_reg(chip, reg_low_thr_lsb, trip_low_thr, 2); if (ret) { pr_err("Low set threshold err\n"); return ret; } } if (high_thr != INT_MAX) { ret = adc_tm5_write_reg(chip, reg_high_thr_lsb, trip_high_thr, 2); if (ret) { pr_err("High set threshold err\n"); return ret; } } return ret; } static int32_t adc_tm5_manage_thresholds(struct adc_tm_sensor *sensor) { int ret = 0, high_thr = INT_MAX, low_thr = INT_MIN; struct adc_tm_client_info *client_info = NULL; struct list_head *thr_list; uint32_t btm_chan_idx = 0; struct adc_tm_chip *chip = sensor->chip; ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx with %d\n", ret); return ret; } /* * Reset the high_thr_set and low_thr_set of all * clients since the thresholds will be recomputed. */ list_for_each(thr_list, &sensor->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); client_info->high_thr_set = false; client_info->low_thr_set = false; } /* Find the min of high_thr and max of low_thr */ list_for_each(thr_list, &sensor->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (client_info->high_thr_requested < high_thr) high_thr = client_info->high_thr_requested; if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (client_info->low_thr_requested > low_thr) low_thr = client_info->low_thr_requested; pr_debug("threshold compared is high:%d and low:%d\n", client_info->high_thr_requested, client_info->low_thr_requested); pr_debug("current threshold is high:%d and low:%d\n", high_thr, low_thr); } /* Check which of the high_thr and low_thr got set */ list_for_each(thr_list, &sensor->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (high_thr == client_info->high_thr_requested) client_info->high_thr_set = true; if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (low_thr == client_info->low_thr_requested) client_info->low_thr_set = true; } ret = adc_tm5_thr_update(sensor, high_thr, low_thr); if (ret < 0) pr_err("setting chan:%d threshold failed\n", btm_chan_idx); pr_debug("threshold written is high:%d and low:%d\n", high_thr, low_thr); return 0; } void notify_adc_tm_fn(struct work_struct *work) { struct adc_tm_client_info *client_info = NULL; struct adc_tm_chip *chip; struct list_head *thr_list; uint32_t btm_chan_num = 0, btm_chan_idx = 0; int ret = 0; struct adc_tm_sensor *adc_tm = container_of(work, struct adc_tm_sensor, work); chip = adc_tm->chip; btm_chan_num = adc_tm->btm_ch; ret = adc_tm5_get_btm_idx(chip, btm_chan_num, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx\n"); return; } mutex_lock(&chip->adc_mutex_lock); if (adc_tm->low_thr_triggered) { /* adjust thr, calling manage_thr */ list_for_each(thr_list, &adc_tm->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if (client_info->low_thr_set) { client_info->low_thr_set = false; client_info->notify_low_thr = true; if (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE) client_info->state_request = ADC_TM_HIGH_THR_ENABLE; else client_info->state_request = ADC_TM_LOW_THR_DISABLE; } } adc_tm5_manage_thresholds(adc_tm); adc_tm->low_thr_triggered = false; } if (adc_tm->high_thr_triggered) { /* adjust thr, calling manage_thr */ list_for_each(thr_list, &adc_tm->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if (client_info->high_thr_set) { client_info->high_thr_set = false; client_info->notify_high_thr = true; if (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE) client_info->state_request = ADC_TM_LOW_THR_ENABLE; else client_info->state_request = ADC_TM_HIGH_THR_DISABLE; } } adc_tm5_manage_thresholds(adc_tm); adc_tm->high_thr_triggered = false; } mutex_unlock(&chip->adc_mutex_lock); list_for_each_entry(client_info, &adc_tm->thr_list, list) { if (client_info->notify_low_thr) { if (client_info->param->threshold_notification != NULL) { pr_debug("notify kernel with low state\n"); client_info->param->threshold_notification( ADC_TM_LOW_STATE, client_info->param->btm_ctx); client_info->notify_low_thr = false; } } if (client_info->notify_high_thr) { if (client_info->param->threshold_notification != NULL) { pr_debug("notify kernel with high state\n"); client_info->param->threshold_notification( ADC_TM_HIGH_STATE, client_info->param->btm_ctx); client_info->notify_high_thr = false; } } } } int32_t adc_tm5_channel_measure(struct adc_tm_chip *chip, struct adc_tm_param *param) { int ret = 0, i = 0; uint32_t channel, dt_index = 0, btm_chan_idx = 0; bool chan_found = false, high_thr_set = false, low_thr_set = false; struct adc_tm_client_info *client_info = NULL; ret = adc_tm_is_valid(chip); if (ret || (param == NULL)) return -EINVAL; if (param->threshold_notification == NULL) { pr_debug("No notification for high/low temp\n"); return -EINVAL; } mutex_lock(&chip->adc_mutex_lock); channel = param->channel; while (i < chip->dt_channels) { if (chip->sensor[i].adc_ch == channel) { dt_index = i; chan_found = true; break; } i++; } if (!chan_found) { pr_err("not a valid ADC_TM channel\n"); ret = -EINVAL; goto fail_unlock; } ret = adc_tm5_get_btm_idx(chip, chip->sensor[dt_index].btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx with %d\n", ret); goto fail_unlock; } /* add channel client to channel list */ adc_tm_add_to_list(chip, dt_index, param); /* set right thresholds for the sensor */ adc_tm5_manage_thresholds(&chip->sensor[dt_index]); /* enable low/high irqs */ list_for_each_entry(client_info, &chip->sensor[dt_index].thr_list, list) { if (client_info->high_thr_set == true) high_thr_set = true; if (client_info->low_thr_set == true) low_thr_set = true; } if (low_thr_set) { /* Enable low threshold's interrupt */ pr_debug("low sensor:%x with state:%d\n", dt_index, param->state_request); ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_LOW_THR_INT_EN, true); if (ret < 0) { pr_err("low thr enable err:%d\n", chip->sensor[dt_index].btm_ch); goto fail_unlock; } } if (high_thr_set) { /* Enable high threshold's interrupt */ pr_debug("high sensor mask:%x with state:%d\n", dt_index, param->state_request); ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_HIGH_THR_INT_EN, true); if (ret < 0) { pr_err("high thr enable err:%d\n", chip->sensor[dt_index].btm_ch); goto fail_unlock; } } /* configure channel */ ret = adc_tm5_configure(&chip->sensor[dt_index], btm_chan_idx); if (ret < 0) { pr_err("Error during adc-tm configure:%d\n", ret); goto fail_unlock; } ret = adc_tm5_enable(chip); if (ret < 0) pr_err("Error enabling adc-tm with %d\n", ret); fail_unlock: mutex_unlock(&chip->adc_mutex_lock); return ret; } EXPORT_SYMBOL(adc_tm5_channel_measure); int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *chip, struct adc_tm_param *param) { int ret = 0, i = 0; uint32_t channel, dt_index = 0, btm_chan_idx = 0; unsigned long flags; ret = adc_tm_is_valid(chip); if (ret || (param == NULL)) return -EINVAL; channel = param->channel; while (i < chip->dt_channels) { if (chip->sensor[i].adc_ch == channel) { dt_index = i; break; } i++; } if (i == chip->dt_channels) { pr_err("not a valid ADC_TM channel\n"); return -EINVAL; } ret = adc_tm5_get_btm_idx(chip, chip->sensor[dt_index].btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx with %d\n", ret); return ret; } spin_lock_irqsave(&chip->adc_tm_lock, flags); ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_HIGH_THR_INT_EN, false); if (ret < 0) { pr_err("high thr disable err\n"); goto fail; } ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_LOW_THR_INT_EN, false); if (ret < 0) { pr_err("low thr disable err\n"); goto fail; } ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_MEAS_EN, false); if (ret < 0) pr_err("multi measurement disable failed\n"); fail: spin_unlock_irqrestore(&chip->adc_tm_lock, flags); return ret; } EXPORT_SYMBOL(adc_tm5_disable_chan_meas); static int adc_tm5_set_mode(struct adc_tm_sensor *sensor, static int adc_tm5_set_mode(struct adc_tm_sensor *sensor, enum thermal_device_mode mode) enum thermal_device_mode mode) { { Loading Loading @@ -424,7 +863,7 @@ static irqreturn_t adc_tm5_handler(int irq, void *data) { { struct adc_tm_chip *chip = data; struct adc_tm_chip *chip = data; u8 status_low, status_high, ctl; u8 status_low, status_high, ctl; int ret, i = 0; int ret = 0, i = 0; unsigned long flags; unsigned long flags; ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1); ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1); Loading @@ -443,12 +882,20 @@ static irqreturn_t adc_tm5_handler(int irq, void *data) bool upper_set = false, lower_set = false; bool upper_set = false, lower_set = false; int temp; int temp; if (IS_ERR(chip->sensor[i].tzd)) if (!chip->sensor[i].non_thermal && IS_ERR(chip->sensor[i].tzd)) { pr_err("thermal device not found\n"); i++; continue; continue; } if (!chip->sensor[i].non_thermal) { ret = adc_tm5_get_temp(&chip->sensor[i], &temp); ret = adc_tm5_get_temp(&chip->sensor[i], &temp); if (ret < 0) if (ret < 0) { i++; continue; continue; } } spin_lock_irqsave(&chip->adc_tm_lock, flags); spin_lock_irqsave(&chip->adc_tm_lock, flags); Loading @@ -469,14 +916,53 @@ static irqreturn_t adc_tm5_handler(int irq, void *data) status_low >>= 1; status_low >>= 1; status_high >>= 1; status_high >>= 1; spin_unlock_irqrestore(&chip->adc_tm_lock, flags); spin_unlock_irqrestore(&chip->adc_tm_lock, flags); if (upper_set || lower_set) { if (!(upper_set || lower_set)) { i++; continue; } if (!chip->sensor[i].non_thermal) { /* /* * Expected behavior is while notifying of_thermal, * Expected behavior is while notifying * thermal core will call set_trips with new thresholds * of_thermal, thermal core will call set_trips * and activate/disable the appropriate trips. * with new thresholds and activate/disable * the appropriate trips. */ */ pr_debug("notifying of_thermal\n"); pr_debug("notifying of_thermal\n"); of_thermal_handle_trip(chip->sensor[i].tzd); of_thermal_handle_trip(chip->sensor[i].tzd); } else { if (lower_set) { ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(i), ADC_TM_Mn_LOW_THR_INT_EN, false); if (ret < 0) { pr_err("low thr disable failed\n"); return IRQ_HANDLED; } chip->sensor[i].low_thr_triggered = true; queue_work(chip->sensor[i].req_wq, &chip->sensor[i].work); } if (upper_set) { ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(i), ADC_TM_Mn_HIGH_THR_INT_EN, false); if (ret < 0) { pr_err("high thr disable failed\n"); return IRQ_HANDLED; } chip->sensor[i].high_thr_triggered = true; queue_work(chip->sensor[i].req_wq, &chip->sensor[i].work); } } } i++; i++; } } Loading Loading @@ -561,6 +1047,7 @@ static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans) pr_err("adc-tm block write failed with %d\n", ret); pr_err("adc-tm block write failed with %d\n", ret); spin_lock_init(&chip->adc_tm_lock); spin_lock_init(&chip->adc_tm_lock); mutex_init(&chip->adc_mutex_lock); if (chip->pmic_rev_id) { if (chip->pmic_rev_id) { switch (chip->pmic_rev_id->pmic_subtype) switch (chip->pmic_rev_id->pmic_subtype) Loading Loading
drivers/thermal/qcom/adc-tm-common.c +19 −0 Original line number Original line Diff line number Diff line Loading @@ -129,5 +129,24 @@ void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, } } EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k); EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k); int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data, struct adc_tm_config *tm_config) { int64_t low_thr = 0, high_thr = 0; low_thr = div_s64(tm_config->low_thr_voltage, tm_config->prescal); low_thr *= data->full_scale_code_volt; low_thr = div64_s64(low_thr, ADC_HC_VDD_REF); tm_config->low_thr_voltage = low_thr; high_thr = div_s64(tm_config->high_thr_voltage, tm_config->prescal); high_thr *= data->full_scale_code_volt; high_thr = div64_s64(high_thr, ADC_HC_VDD_REF); tm_config->high_thr_voltage = high_thr; return 0; } EXPORT_SYMBOL(adc_tm_absolute_rthr); MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver"); MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver"); MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
drivers/thermal/qcom/adc-tm.c +94 −22 Original line number Original line Diff line number Diff line Loading @@ -77,14 +77,15 @@ static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num, for (i = 0; i < dt_chan_num; i++) { for (i = 0; i < dt_chan_num; i++) { adc_tm->sensor[i].chip = adc_tm; adc_tm->sensor[i].chip = adc_tm; if (!adc_tm->sensor[i].non_thermal) { if (set_trips) if (set_trips) tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev, tzd = devm_thermal_zone_of_sensor_register( adc_tm->sensor[i].adc_ch, &adc_tm->sensor[i], adc_tm->dev, adc_tm->sensor[i].adc_ch, &adc_tm_ops); &adc_tm->sensor[i], &adc_tm_ops); else else tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev, tzd = devm_thermal_zone_of_sensor_register( adc_tm->sensor[i].adc_ch, &adc_tm->sensor[i], adc_tm->dev, adc_tm->sensor[i].adc_ch, &adc_tm_ops_iio); &adc_tm->sensor[i], &adc_tm_ops_iio); if (IS_ERR(tzd)) { if (IS_ERR(tzd)) { pr_err("Error registering TZ zone:%d for dt_ch:%d\n", pr_err("Error registering TZ zone:%d for dt_ch:%d\n", Loading @@ -92,6 +93,8 @@ static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num, continue; continue; } } adc_tm->sensor[i].tzd = tzd; adc_tm->sensor[i].tzd = tzd; } else adc_tm->sensor[i].tzd = NULL; } } return 0; return 0; Loading Loading @@ -130,6 +133,39 @@ static int adc_tm_decimation_from_dt(u32 value, const unsigned int *decimation) return -EINVAL; return -EINVAL; } } struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name) { struct platform_device *pdev; struct adc_tm_chip *chip; struct device_node *node = NULL; char prop_name[MAX_PROP_NAME_LEN]; snprintf(prop_name, MAX_PROP_NAME_LEN, "qcom,%s-adc_tm", name); node = of_parse_phandle(dev->of_node, prop_name, 0); if (node == NULL) return ERR_PTR(-ENODEV); list_for_each_entry(chip, &adc_tm_device_list, list) { pdev = to_platform_device(chip->dev); if (pdev->dev.of_node == node) return chip; } return ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(get_adc_tm); int adc_tm_is_valid(struct adc_tm_chip *chip) { struct adc_tm_chip *adc_tm_chip = NULL; list_for_each_entry(adc_tm_chip, &adc_tm_device_list, list) if (chip == adc_tm_chip) return 0; return -EINVAL; } static const struct of_device_id adc_tm_match_table[] = { static const struct of_device_id adc_tm_match_table[] = { { { .compatible = "qcom,adc-tm5", .compatible = "qcom,adc-tm5", Loading Loading @@ -195,9 +231,11 @@ static int adc_tm_get_dt_data(struct platform_device *pdev, adc_tm->prop.timer3 = ADC_TM_TIMER3; adc_tm->prop.timer3 = ADC_TM_TIMER3; for_each_child_of_node(node, child) { for_each_child_of_node(node, child) { int channel_num, i = 0; int channel_num, i = 0, adc_rscale_fn = 0; int calib_type = 0, ret, hw_settle_time = 0; int calib_type = 0, ret, hw_settle_time = 0; int prescal = 0; struct iio_channel *chan_adc; struct iio_channel *chan_adc; bool non_thermal = false; ret = of_property_read_u32(child, "reg", &channel_num); ret = of_property_read_u32(child, "reg", &channel_num); if (ret) { if (ret) { Loading @@ -224,12 +262,39 @@ static int adc_tm_get_dt_data(struct platform_device *pdev, else else calib_type = ADC_ABS_CAL; calib_type = ADC_ABS_CAL; if (of_property_read_bool(child, "qcom,kernel-client")) non_thermal = true; ret = of_property_read_u32(child, "qcom,scale-type", &adc_rscale_fn); if (ret) adc_rscale_fn = SCALE_RSCALE_NONE; ret = of_property_read_u32(child, "qcom,prescaling", &prescal); if (ret) prescal = 1; /* Individual channel properties */ /* Individual channel properties */ adc_tm->sensor[idx].adc_ch = channel_num; adc_tm->sensor[idx].adc_ch = channel_num; adc_tm->sensor[idx].cal_sel = calib_type; adc_tm->sensor[idx].cal_sel = calib_type; /* Default to 1 second timer select */ /* Default to 1 second timer select */ adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2; adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2; adc_tm->sensor[idx].hw_settle_time = hw_settle_time; adc_tm->sensor[idx].hw_settle_time = hw_settle_time; adc_tm->sensor[idx].adc_rscale_fn = adc_rscale_fn; adc_tm->sensor[idx].non_thermal = non_thermal; adc_tm->sensor[idx].prescaling = prescal; if (adc_tm->sensor[idx].non_thermal) { adc_tm->sensor[idx].req_wq = alloc_workqueue( "qpnp_adc_notify_wq", WQ_HIGHPRI, 0); if (!adc_tm->sensor[idx].req_wq) { pr_err("Requesting priority wq failed\n"); return -ENOMEM; } INIT_WORK(&adc_tm->sensor[idx].work, notify_adc_tm_fn); } INIT_LIST_HEAD(&adc_tm->sensor[idx].thr_list); while (i < dt_chan_num) { while (i < dt_chan_num) { chan_adc = &chan[i]; chan_adc = &chan[i]; if (chan_adc->channel->channel == channel_num) if (chan_adc->channel->channel == channel_num) Loading @@ -249,7 +314,7 @@ static int adc_tm_probe(struct platform_device *pdev) struct adc_tm_chip *adc_tm; struct adc_tm_chip *adc_tm; struct regmap *regmap; struct regmap *regmap; struct iio_channel *channels; struct iio_channel *channels; int ret, dt_chan_num = 0, indio_chan_count = 0; int ret = 0, dt_chan_num = 0, indio_chan_count = 0, i = 0; u32 reg; u32 reg; if (!node) if (!node) Loading Loading @@ -313,7 +378,7 @@ static int adc_tm_probe(struct platform_device *pdev) ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false); ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false); if (ret) { if (ret) { dev_err(dev, "adc-tm failed to register with of thermal\n"); dev_err(dev, "adc-tm failed to register with of thermal\n"); return ret; goto fail; } } return 0; return 0; } } Loading @@ -321,25 +386,32 @@ static int adc_tm_probe(struct platform_device *pdev) ret = adc_tm_init(adc_tm, dt_chan_num); ret = adc_tm_init(adc_tm, dt_chan_num); if (ret) { if (ret) { dev_err(dev, "adc-tm init failed\n"); dev_err(dev, "adc-tm init failed\n"); return ret; goto fail; } } ret = adc_tm_register_tzd(adc_tm, dt_chan_num, true); ret = adc_tm_register_tzd(adc_tm, dt_chan_num, true); if (ret) { if (ret) { dev_err(dev, "adc-tm failed to register with of thermal\n"); dev_err(dev, "adc-tm failed to register with of thermal\n"); return ret; goto fail; } } ret = adc_tm_register_interrupts(adc_tm); ret = adc_tm_register_interrupts(adc_tm); if (ret) { if (ret) { pr_err("adc-tm register interrupts failed:%d\n", ret); pr_err("adc-tm register interrupts failed:%d\n", ret); return ret; goto fail; } } list_add_tail(&adc_tm->list, &adc_tm_device_list); list_add_tail(&adc_tm->list, &adc_tm_device_list); platform_set_drvdata(pdev, adc_tm); platform_set_drvdata(pdev, adc_tm); return 0; return 0; fail: i = 0; while (i < dt_chan_num) { if (adc_tm->sensor[i].req_wq) destroy_workqueue(adc_tm->sensor[i].req_wq); i++; } return ret; } } static int adc_tm_remove(struct platform_device *pdev) static int adc_tm_remove(struct platform_device *pdev) Loading
drivers/thermal/qcom/adc-tm.h +118 −0 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,8 @@ struct adc_tm_chip; #define ADC_TM_TIMER1 3 /* 3.9ms */ #define ADC_TM_TIMER1 3 /* 3.9ms */ #define ADC_TM_TIMER2 10 /* 1 second */ #define ADC_TM_TIMER2 10 /* 1 second */ #define ADC_TM_TIMER3 4 /* 4 second */ #define ADC_TM_TIMER3 4 /* 4 second */ #define ADC_HC_VDD_REF 1875000 #define MAX_PROP_NAME_LEN 32 enum adc_cal_method { enum adc_cal_method { ADC_NO_CAL = 0, ADC_NO_CAL = 0, Loading @@ -53,6 +55,72 @@ enum adc_timer_select { ADC_TIMER_SEL_NONE, ADC_TIMER_SEL_NONE, }; }; /** * enum adc_tm_state - This lets the client know whether the threshold * that was crossed was high/low. * %ADC_TM_HIGH_STATE: Client is notified of crossing the requested high * voltage threshold. * %ADC_TM_COOL_STATE: Client is notified of crossing the requested cool * temperature threshold. * %ADC_TM_LOW_STATE: Client is notified of crossing the requested low * voltage threshold. * %ADC_TM_WARM_STATE: Client is notified of crossing the requested high * temperature threshold. */ enum adc_tm_state { ADC_TM_HIGH_STATE = 0, ADC_TM_COOL_STATE = ADC_TM_HIGH_STATE, ADC_TM_LOW_STATE, ADC_TM_WARM_STATE = ADC_TM_LOW_STATE, ADC_TM_STATE_NUM, }; /** * enum adc_tm_state_request - Request to enable/disable the corresponding * high/low voltage/temperature thresholds. * %ADC_TM_HIGH_THR_ENABLE: Enable high voltage threshold. * %ADC_TM_COOL_THR_ENABLE = Enables cool temperature threshold. * %ADC_TM_LOW_THR_ENABLE: Enable low voltage/temperature threshold. * %ADC_TM_WARM_THR_ENABLE = Enables warm temperature threshold. * %ADC_TM_HIGH_LOW_THR_ENABLE: Enable high and low voltage/temperature * threshold. * %ADC_TM_HIGH_THR_DISABLE: Disable high voltage/temperature threshold. * %ADC_TM_COOL_THR_ENABLE = Disables cool temperature threshold. * %ADC_TM_LOW_THR_DISABLE: Disable low voltage/temperature threshold. * %ADC_TM_WARM_THR_ENABLE = Disables warm temperature threshold. * %ADC_TM_HIGH_THR_DISABLE: Disable high and low voltage/temperature * threshold. */ enum adc_tm_state_request { ADC_TM_HIGH_THR_ENABLE = 0, ADC_TM_COOL_THR_ENABLE = ADC_TM_HIGH_THR_ENABLE, ADC_TM_LOW_THR_ENABLE, ADC_TM_WARM_THR_ENABLE = ADC_TM_LOW_THR_ENABLE, ADC_TM_HIGH_LOW_THR_ENABLE, ADC_TM_HIGH_THR_DISABLE, ADC_TM_COOL_THR_DISABLE = ADC_TM_HIGH_THR_DISABLE, ADC_TM_LOW_THR_DISABLE, ADC_TM_WARM_THR_DISABLE = ADC_TM_LOW_THR_DISABLE, ADC_TM_HIGH_LOW_THR_DISABLE, ADC_TM_THR_NUM, }; /** * enum adc_tm_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. Example battery follows the power supply * framework that needs its units to be in decidegreesC so it passes * deci-degreesC. PA_THERM clients pass the temperature in degrees. * The order below should match the one in the driver for * adc_tm_rscale_fn[]. */ enum adc_tm_rscale_fn_type { SCALE_R_ABSOLUTE = 0, SCALE_RSCALE_NONE, }; struct adc_tm_sensor { struct adc_tm_sensor { struct adc_tm_chip *chip; struct adc_tm_chip *chip; struct thermal_zone_device *tzd; struct thermal_zone_device *tzd; Loading @@ -63,7 +131,36 @@ struct adc_tm_sensor { unsigned int btm_ch; unsigned int btm_ch; unsigned int prescaling; unsigned int prescaling; unsigned int timer_select; unsigned int timer_select; enum adc_tm_rscale_fn_type adc_rscale_fn; struct iio_channel *adc; struct iio_channel *adc; struct list_head thr_list; bool non_thermal; bool high_thr_triggered; bool low_thr_triggered; struct workqueue_struct *req_wq; struct work_struct work; }; struct adc_tm_param { int low_thr; int high_thr; uint32_t channel; enum adc_tm_state_request state_request; void *btm_ctx; void (*threshold_notification)(enum adc_tm_state state, void *ctx); }; struct adc_tm_client_info { struct list_head list; struct adc_tm_param *param; int32_t low_thr_requested; int32_t high_thr_requested; bool notify_low_thr; bool notify_high_thr; bool high_thr_set; bool low_thr_set; enum adc_tm_state_request state_request; }; }; struct adc_tm_cmn_prop { struct adc_tm_cmn_prop { Loading @@ -89,6 +186,7 @@ struct adc_tm_chip { u16 base; u16 base; struct adc_tm_cmn_prop prop; struct adc_tm_cmn_prop prop; spinlock_t adc_tm_lock; spinlock_t adc_tm_lock; struct mutex adc_mutex_lock; const struct adc_tm_ops *ops; const struct adc_tm_ops *ops; const struct adc_tm_data *data; const struct adc_tm_data *data; unsigned int dt_channels; unsigned int dt_channels; Loading Loading @@ -182,12 +280,23 @@ struct adc_tm_trip_reg_type { struct adc_tm_config { struct adc_tm_config { int channel; int channel; int adc_code; int adc_code; int prescal; int high_thr_temp; int high_thr_temp; int low_thr_temp; int low_thr_temp; int64_t high_thr_voltage; int64_t high_thr_voltage; int64_t low_thr_voltage; int64_t low_thr_voltage; }; }; /** * struct adc_tm_reverse_scale_fn - Reverse scaling prototype * @chan: Function pointer to one of the scaling functions * which takes the adc properties and returns the physical result */ struct adc_tm_reverse_scale_fn { int32_t (*chan)(const struct adc_tm_data *, struct adc_tm_config *); }; /** /** * struct adc_map_pt - Map the graph representation for ADC channel * struct adc_map_pt - Map the graph representation for ADC channel * @x: Represent the ADC digitized code. * @x: Represent the ADC digitized code. Loading Loading @@ -217,4 +326,13 @@ struct adc_tm_linear_graph { void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, const struct adc_tm_data *data); const struct adc_tm_data *data); int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data, struct adc_tm_config *tm_config); void notify_adc_tm_fn(struct work_struct *work); struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name); int adc_tm_is_valid(struct adc_tm_chip *chip); #endif /* __QCOM_ADC_TM_H__ */ #endif /* __QCOM_ADC_TM_H__ */
drivers/thermal/qcom/adc-tm5.c +496 −9 Original line number Original line Diff line number Diff line Loading @@ -76,6 +76,10 @@ static struct adc_tm_trip_reg_type adc_tm_ch_data[] = { [ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL}, [ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL}, }; }; static struct adc_tm_reverse_scale_fn adc_tm_rscale_fn[] = { [SCALE_R_ABSOLUTE] = {adc_tm_absolute_rthr}, }; static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp) static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp) { { int ret, milli_celsius; int ret, milli_celsius; Loading Loading @@ -219,6 +223,441 @@ static int adc_tm5_configure(struct adc_tm_sensor *sensor, return 0; return 0; } } static int32_t adc_tm_add_to_list(struct adc_tm_chip *chip, uint32_t dt_index, struct adc_tm_param *param) { struct adc_tm_client_info *client_info = NULL; bool client_info_exists = false; list_for_each_entry(client_info, &chip->sensor[dt_index].thr_list, list) { if (client_info->param == param) { client_info->low_thr_requested = param->low_thr; client_info->high_thr_requested = param->high_thr; client_info->state_request = param->state_request; client_info->notify_low_thr = false; client_info->notify_high_thr = false; client_info_exists = true; pr_debug("client found\n"); } } if (!client_info_exists) { client_info = devm_kzalloc(chip->dev, sizeof(struct adc_tm_client_info), GFP_KERNEL); if (!client_info) return -ENOMEM; pr_debug("new client\n"); client_info->param = param; client_info->low_thr_requested = param->low_thr; client_info->high_thr_requested = param->high_thr; client_info->state_request = param->state_request; list_add_tail(&client_info->list, &chip->sensor[dt_index].thr_list); } return 0; } static int32_t adc_tm5_thr_update(struct adc_tm_sensor *sensor, int32_t high_thr, int32_t low_thr) { int ret = 0; u8 trip_low_thr[2], trip_high_thr[2]; uint16_t reg_low_thr_lsb, reg_high_thr_lsb; uint32_t scale_type = 0, mask = 0, btm_chan_idx = 0; struct adc_tm_config tm_config; struct adc_tm_chip *chip; ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx\n"); return ret; } chip = sensor->chip; tm_config.high_thr_voltage = (int64_t)high_thr; tm_config.low_thr_voltage = (int64_t)low_thr; tm_config.prescal = sensor->prescaling; scale_type = sensor->adc_rscale_fn; if (scale_type >= SCALE_RSCALE_NONE) { ret = -EBADF; return ret; } adc_tm_rscale_fn[scale_type].chan(chip->data, &tm_config); mask = lower_32_bits(tm_config.high_thr_voltage); trip_high_thr[0] = ADC_TM_LOWER_MASK(mask); trip_high_thr[1] = ADC_TM_UPPER_MASK(mask); mask = lower_32_bits(tm_config.low_thr_voltage); trip_low_thr[0] = ADC_TM_LOWER_MASK(mask); trip_low_thr[1] = ADC_TM_UPPER_MASK(mask); pr_debug("high_thr:0x%llx, low_thr:0x%llx\n", tm_config.high_thr_voltage, tm_config.low_thr_voltage); reg_low_thr_lsb = ADC_TM_Mn_LOW_THR0(btm_chan_idx); reg_high_thr_lsb = ADC_TM_Mn_HIGH_THR0(btm_chan_idx); if (low_thr != INT_MIN) { ret = adc_tm5_write_reg(chip, reg_low_thr_lsb, trip_low_thr, 2); if (ret) { pr_err("Low set threshold err\n"); return ret; } } if (high_thr != INT_MAX) { ret = adc_tm5_write_reg(chip, reg_high_thr_lsb, trip_high_thr, 2); if (ret) { pr_err("High set threshold err\n"); return ret; } } return ret; } static int32_t adc_tm5_manage_thresholds(struct adc_tm_sensor *sensor) { int ret = 0, high_thr = INT_MAX, low_thr = INT_MIN; struct adc_tm_client_info *client_info = NULL; struct list_head *thr_list; uint32_t btm_chan_idx = 0; struct adc_tm_chip *chip = sensor->chip; ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx with %d\n", ret); return ret; } /* * Reset the high_thr_set and low_thr_set of all * clients since the thresholds will be recomputed. */ list_for_each(thr_list, &sensor->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); client_info->high_thr_set = false; client_info->low_thr_set = false; } /* Find the min of high_thr and max of low_thr */ list_for_each(thr_list, &sensor->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (client_info->high_thr_requested < high_thr) high_thr = client_info->high_thr_requested; if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (client_info->low_thr_requested > low_thr) low_thr = client_info->low_thr_requested; pr_debug("threshold compared is high:%d and low:%d\n", client_info->high_thr_requested, client_info->low_thr_requested); pr_debug("current threshold is high:%d and low:%d\n", high_thr, low_thr); } /* Check which of the high_thr and low_thr got set */ list_for_each(thr_list, &sensor->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (high_thr == client_info->high_thr_requested) client_info->high_thr_set = true; if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) || (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE)) if (low_thr == client_info->low_thr_requested) client_info->low_thr_set = true; } ret = adc_tm5_thr_update(sensor, high_thr, low_thr); if (ret < 0) pr_err("setting chan:%d threshold failed\n", btm_chan_idx); pr_debug("threshold written is high:%d and low:%d\n", high_thr, low_thr); return 0; } void notify_adc_tm_fn(struct work_struct *work) { struct adc_tm_client_info *client_info = NULL; struct adc_tm_chip *chip; struct list_head *thr_list; uint32_t btm_chan_num = 0, btm_chan_idx = 0; int ret = 0; struct adc_tm_sensor *adc_tm = container_of(work, struct adc_tm_sensor, work); chip = adc_tm->chip; btm_chan_num = adc_tm->btm_ch; ret = adc_tm5_get_btm_idx(chip, btm_chan_num, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx\n"); return; } mutex_lock(&chip->adc_mutex_lock); if (adc_tm->low_thr_triggered) { /* adjust thr, calling manage_thr */ list_for_each(thr_list, &adc_tm->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if (client_info->low_thr_set) { client_info->low_thr_set = false; client_info->notify_low_thr = true; if (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE) client_info->state_request = ADC_TM_HIGH_THR_ENABLE; else client_info->state_request = ADC_TM_LOW_THR_DISABLE; } } adc_tm5_manage_thresholds(adc_tm); adc_tm->low_thr_triggered = false; } if (adc_tm->high_thr_triggered) { /* adjust thr, calling manage_thr */ list_for_each(thr_list, &adc_tm->thr_list) { client_info = list_entry(thr_list, struct adc_tm_client_info, list); if (client_info->high_thr_set) { client_info->high_thr_set = false; client_info->notify_high_thr = true; if (client_info->state_request == ADC_TM_HIGH_LOW_THR_ENABLE) client_info->state_request = ADC_TM_LOW_THR_ENABLE; else client_info->state_request = ADC_TM_HIGH_THR_DISABLE; } } adc_tm5_manage_thresholds(adc_tm); adc_tm->high_thr_triggered = false; } mutex_unlock(&chip->adc_mutex_lock); list_for_each_entry(client_info, &adc_tm->thr_list, list) { if (client_info->notify_low_thr) { if (client_info->param->threshold_notification != NULL) { pr_debug("notify kernel with low state\n"); client_info->param->threshold_notification( ADC_TM_LOW_STATE, client_info->param->btm_ctx); client_info->notify_low_thr = false; } } if (client_info->notify_high_thr) { if (client_info->param->threshold_notification != NULL) { pr_debug("notify kernel with high state\n"); client_info->param->threshold_notification( ADC_TM_HIGH_STATE, client_info->param->btm_ctx); client_info->notify_high_thr = false; } } } } int32_t adc_tm5_channel_measure(struct adc_tm_chip *chip, struct adc_tm_param *param) { int ret = 0, i = 0; uint32_t channel, dt_index = 0, btm_chan_idx = 0; bool chan_found = false, high_thr_set = false, low_thr_set = false; struct adc_tm_client_info *client_info = NULL; ret = adc_tm_is_valid(chip); if (ret || (param == NULL)) return -EINVAL; if (param->threshold_notification == NULL) { pr_debug("No notification for high/low temp\n"); return -EINVAL; } mutex_lock(&chip->adc_mutex_lock); channel = param->channel; while (i < chip->dt_channels) { if (chip->sensor[i].adc_ch == channel) { dt_index = i; chan_found = true; break; } i++; } if (!chan_found) { pr_err("not a valid ADC_TM channel\n"); ret = -EINVAL; goto fail_unlock; } ret = adc_tm5_get_btm_idx(chip, chip->sensor[dt_index].btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx with %d\n", ret); goto fail_unlock; } /* add channel client to channel list */ adc_tm_add_to_list(chip, dt_index, param); /* set right thresholds for the sensor */ adc_tm5_manage_thresholds(&chip->sensor[dt_index]); /* enable low/high irqs */ list_for_each_entry(client_info, &chip->sensor[dt_index].thr_list, list) { if (client_info->high_thr_set == true) high_thr_set = true; if (client_info->low_thr_set == true) low_thr_set = true; } if (low_thr_set) { /* Enable low threshold's interrupt */ pr_debug("low sensor:%x with state:%d\n", dt_index, param->state_request); ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_LOW_THR_INT_EN, true); if (ret < 0) { pr_err("low thr enable err:%d\n", chip->sensor[dt_index].btm_ch); goto fail_unlock; } } if (high_thr_set) { /* Enable high threshold's interrupt */ pr_debug("high sensor mask:%x with state:%d\n", dt_index, param->state_request); ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_HIGH_THR_INT_EN, true); if (ret < 0) { pr_err("high thr enable err:%d\n", chip->sensor[dt_index].btm_ch); goto fail_unlock; } } /* configure channel */ ret = adc_tm5_configure(&chip->sensor[dt_index], btm_chan_idx); if (ret < 0) { pr_err("Error during adc-tm configure:%d\n", ret); goto fail_unlock; } ret = adc_tm5_enable(chip); if (ret < 0) pr_err("Error enabling adc-tm with %d\n", ret); fail_unlock: mutex_unlock(&chip->adc_mutex_lock); return ret; } EXPORT_SYMBOL(adc_tm5_channel_measure); int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *chip, struct adc_tm_param *param) { int ret = 0, i = 0; uint32_t channel, dt_index = 0, btm_chan_idx = 0; unsigned long flags; ret = adc_tm_is_valid(chip); if (ret || (param == NULL)) return -EINVAL; channel = param->channel; while (i < chip->dt_channels) { if (chip->sensor[i].adc_ch == channel) { dt_index = i; break; } i++; } if (i == chip->dt_channels) { pr_err("not a valid ADC_TM channel\n"); return -EINVAL; } ret = adc_tm5_get_btm_idx(chip, chip->sensor[dt_index].btm_ch, &btm_chan_idx); if (ret < 0) { pr_err("Invalid btm channel idx with %d\n", ret); return ret; } spin_lock_irqsave(&chip->adc_tm_lock, flags); ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_HIGH_THR_INT_EN, false); if (ret < 0) { pr_err("high thr disable err\n"); goto fail; } ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_LOW_THR_INT_EN, false); if (ret < 0) { pr_err("low thr disable err\n"); goto fail; } ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx), ADC_TM_Mn_MEAS_EN, false); if (ret < 0) pr_err("multi measurement disable failed\n"); fail: spin_unlock_irqrestore(&chip->adc_tm_lock, flags); return ret; } EXPORT_SYMBOL(adc_tm5_disable_chan_meas); static int adc_tm5_set_mode(struct adc_tm_sensor *sensor, static int adc_tm5_set_mode(struct adc_tm_sensor *sensor, enum thermal_device_mode mode) enum thermal_device_mode mode) { { Loading Loading @@ -424,7 +863,7 @@ static irqreturn_t adc_tm5_handler(int irq, void *data) { { struct adc_tm_chip *chip = data; struct adc_tm_chip *chip = data; u8 status_low, status_high, ctl; u8 status_low, status_high, ctl; int ret, i = 0; int ret = 0, i = 0; unsigned long flags; unsigned long flags; ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1); ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1); Loading @@ -443,12 +882,20 @@ static irqreturn_t adc_tm5_handler(int irq, void *data) bool upper_set = false, lower_set = false; bool upper_set = false, lower_set = false; int temp; int temp; if (IS_ERR(chip->sensor[i].tzd)) if (!chip->sensor[i].non_thermal && IS_ERR(chip->sensor[i].tzd)) { pr_err("thermal device not found\n"); i++; continue; continue; } if (!chip->sensor[i].non_thermal) { ret = adc_tm5_get_temp(&chip->sensor[i], &temp); ret = adc_tm5_get_temp(&chip->sensor[i], &temp); if (ret < 0) if (ret < 0) { i++; continue; continue; } } spin_lock_irqsave(&chip->adc_tm_lock, flags); spin_lock_irqsave(&chip->adc_tm_lock, flags); Loading @@ -469,14 +916,53 @@ static irqreturn_t adc_tm5_handler(int irq, void *data) status_low >>= 1; status_low >>= 1; status_high >>= 1; status_high >>= 1; spin_unlock_irqrestore(&chip->adc_tm_lock, flags); spin_unlock_irqrestore(&chip->adc_tm_lock, flags); if (upper_set || lower_set) { if (!(upper_set || lower_set)) { i++; continue; } if (!chip->sensor[i].non_thermal) { /* /* * Expected behavior is while notifying of_thermal, * Expected behavior is while notifying * thermal core will call set_trips with new thresholds * of_thermal, thermal core will call set_trips * and activate/disable the appropriate trips. * with new thresholds and activate/disable * the appropriate trips. */ */ pr_debug("notifying of_thermal\n"); pr_debug("notifying of_thermal\n"); of_thermal_handle_trip(chip->sensor[i].tzd); of_thermal_handle_trip(chip->sensor[i].tzd); } else { if (lower_set) { ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(i), ADC_TM_Mn_LOW_THR_INT_EN, false); if (ret < 0) { pr_err("low thr disable failed\n"); return IRQ_HANDLED; } chip->sensor[i].low_thr_triggered = true; queue_work(chip->sensor[i].req_wq, &chip->sensor[i].work); } if (upper_set) { ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(i), ADC_TM_Mn_HIGH_THR_INT_EN, false); if (ret < 0) { pr_err("high thr disable failed\n"); return IRQ_HANDLED; } chip->sensor[i].high_thr_triggered = true; queue_work(chip->sensor[i].req_wq, &chip->sensor[i].work); } } } i++; i++; } } Loading Loading @@ -561,6 +1047,7 @@ static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans) pr_err("adc-tm block write failed with %d\n", ret); pr_err("adc-tm block write failed with %d\n", ret); spin_lock_init(&chip->adc_tm_lock); spin_lock_init(&chip->adc_tm_lock); mutex_init(&chip->adc_mutex_lock); if (chip->pmic_rev_id) { if (chip->pmic_rev_id) { switch (chip->pmic_rev_id->pmic_subtype) switch (chip->pmic_rev_id->pmic_subtype) Loading