Loading Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt +6 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,12 @@ Optional properties: for any one supported channel along with supporting single conversion requests. - qcom,vadc-recalib-check: Add this property to check if recalibration required due to inaccuracy. - qcom,vadc-thermal-node : If present a thermal node is created and the channel is registered as part of the thermal sysfs which allows clients to use the thermal framework to set temperature thresholds and receive notification when the temperature crosses a set threshold, read temperature and enable/set trip types supported by the thermal framework. Client required property: - qcom,<consumer name>-vadc : The phandle to the corresponding vadc device. Loading drivers/hwmon/qpnp-adc-voltage.c +97 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/qpnp/qpnp-adc.h> #include <linux/platform_device.h> #include <linux/thermal.h> /* QPNP VADC register definition */ #define QPNP_VADC_REVISION1 0x0 Loading Loading @@ -123,6 +124,14 @@ struct qpnp_vadc_mode_state { struct qpnp_adc_amux vadc_meas_amux; }; struct qpnp_vadc_thermal_data { bool thermal_node; int thermal_chan; enum qpnp_vadc_channels vadc_channel; struct thermal_zone_device *tz_dev; struct qpnp_vadc_chip *vadc_dev; }; struct qpnp_vadc_chip { struct device *dev; struct qpnp_adc_drv *adc; Loading @@ -143,6 +152,7 @@ struct qpnp_vadc_chip { struct work_struct trigger_high_thr_work; struct work_struct trigger_low_thr_work; struct qpnp_vadc_mode_state *state_copy; struct qpnp_vadc_thermal_data *vadc_therm_chan; struct sensor_device_attribute sens_attr[0]; }; Loading Loading @@ -2123,10 +2133,77 @@ hwmon_err_sens: return rc; } static int qpnp_vadc_get_temp(struct thermal_zone_device *thermal, unsigned long *temp) { struct qpnp_vadc_thermal_data *vadc_therm = thermal->devdata; struct qpnp_vadc_chip *vadc = vadc_therm->vadc_dev; struct qpnp_vadc_result result; int rc = 0; rc = qpnp_vadc_read(vadc, vadc_therm->vadc_channel, &result); if (rc) { pr_err("VADC read error with %d\n", rc); return rc; } *temp = result.physical; return rc; } static struct thermal_zone_device_ops qpnp_vadc_thermal_ops = { .get_temp = qpnp_vadc_get_temp, }; static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc, struct spmi_device *spmi) { struct device_node *child; struct device_node *node = spmi->dev.of_node; int rc = 0, i = 0; bool thermal_node = false; if (node == NULL) goto thermal_err_sens; for_each_child_of_node(node, child) { char name[QPNP_THERMALNODE_NAME_LENGTH]; vadc->vadc_therm_chan[i].vadc_channel = vadc->adc->adc_channels[i].channel_num; vadc->vadc_therm_chan[i].thermal_chan = i; thermal_node = of_property_read_bool(child, "qcom,vadc-thermal-node"); if (thermal_node) { /* Register with the thermal zone */ vadc->vadc_therm_chan[i].thermal_node = true; snprintf(name, sizeof(name), "%s", vadc->adc->adc_channels[i].name); vadc->vadc_therm_chan[i].tz_dev = thermal_zone_device_register(name, 0, 0, &vadc->vadc_therm_chan[i], &qpnp_vadc_thermal_ops, NULL, 0, 0); if (IS_ERR(vadc->vadc_therm_chan[i].tz_dev)) { pr_err("thermal device register failed.\n"); goto thermal_err_sens; } vadc->vadc_therm_chan[i].vadc_dev = vadc; } i++; thermal_node = false; } return 0; thermal_err_sens: pr_err("Init HWMON failed for qpnp_adc with %d\n", rc); return rc; } static int qpnp_vadc_probe(struct spmi_device *spmi) { struct qpnp_vadc_chip *vadc; struct qpnp_adc_drv *adc_qpnp; struct qpnp_vadc_thermal_data *adc_thermal; struct device_node *node = spmi->dev.of_node; struct device_node *child; int rc, count_adc_channel_list = 0, i = 0; Loading Loading @@ -2164,6 +2241,15 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) } vadc->adc = adc_qpnp; adc_thermal = devm_kzalloc(&spmi->dev, (sizeof(struct qpnp_vadc_thermal_data) * count_adc_channel_list), GFP_KERNEL); if (!adc_thermal) { dev_err(&spmi->dev, "Unable to allocate memory\n"); return -ENOMEM; } vadc->vadc_therm_chan = adc_thermal; rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc); if (rc) { dev_err(&spmi->dev, "failed to read device tree\n"); Loading @@ -2177,6 +2263,11 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) return rc; } vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev); rc = qpnp_vadc_init_thermal(vadc, spmi); if (rc) { dev_err(&spmi->dev, "failed to initialize qpnp thermal adc\n"); return rc; } vadc->vadc_init_calib = false; vadc->max_channels_available = count_adc_channel_list; rc = qpnp_vadc_read_reg(vadc, QPNP_INT_TEST_VAL, &fab_id); Loading Loading @@ -2281,6 +2372,9 @@ err_setup: for_each_child_of_node(node, child) { device_remove_file(&spmi->dev, &vadc->sens_attr[i].dev_attr); if (vadc->vadc_therm_chan[i].thermal_node) thermal_zone_device_unregister( vadc->vadc_therm_chan[i].tz_dev); i++; } hwmon_device_unregister(vadc->vadc_hwmon); Loading @@ -2298,6 +2392,9 @@ static int qpnp_vadc_remove(struct spmi_device *spmi) for_each_child_of_node(node, child) { device_remove_file(&spmi->dev, &vadc->sens_attr[i].dev_attr); if (vadc->vadc_therm_chan[i].thermal_node) thermal_zone_device_unregister( vadc->vadc_therm_chan[i].tz_dev); i++; } hwmon_device_unregister(vadc->vadc_hwmon); Loading include/linux/qpnp/qpnp-adc.h +2 −1 Original line number Diff line number Diff line /* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -140,6 +140,7 @@ enum qpnp_iadc_channels { #define QPNP_ADC_625_UV 625000 #define QPNP_ADC_HWMON_NAME_LENGTH 64 #define QPNP_MAX_PROP_NAME_LEN 32 #define QPNP_THERMALNODE_NAME_LENGTH 25 /* Structure device for qpnp vadc */ struct qpnp_vadc_chip; Loading Loading
Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt +6 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,12 @@ Optional properties: for any one supported channel along with supporting single conversion requests. - qcom,vadc-recalib-check: Add this property to check if recalibration required due to inaccuracy. - qcom,vadc-thermal-node : If present a thermal node is created and the channel is registered as part of the thermal sysfs which allows clients to use the thermal framework to set temperature thresholds and receive notification when the temperature crosses a set threshold, read temperature and enable/set trip types supported by the thermal framework. Client required property: - qcom,<consumer name>-vadc : The phandle to the corresponding vadc device. Loading
drivers/hwmon/qpnp-adc-voltage.c +97 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/qpnp/qpnp-adc.h> #include <linux/platform_device.h> #include <linux/thermal.h> /* QPNP VADC register definition */ #define QPNP_VADC_REVISION1 0x0 Loading Loading @@ -123,6 +124,14 @@ struct qpnp_vadc_mode_state { struct qpnp_adc_amux vadc_meas_amux; }; struct qpnp_vadc_thermal_data { bool thermal_node; int thermal_chan; enum qpnp_vadc_channels vadc_channel; struct thermal_zone_device *tz_dev; struct qpnp_vadc_chip *vadc_dev; }; struct qpnp_vadc_chip { struct device *dev; struct qpnp_adc_drv *adc; Loading @@ -143,6 +152,7 @@ struct qpnp_vadc_chip { struct work_struct trigger_high_thr_work; struct work_struct trigger_low_thr_work; struct qpnp_vadc_mode_state *state_copy; struct qpnp_vadc_thermal_data *vadc_therm_chan; struct sensor_device_attribute sens_attr[0]; }; Loading Loading @@ -2123,10 +2133,77 @@ hwmon_err_sens: return rc; } static int qpnp_vadc_get_temp(struct thermal_zone_device *thermal, unsigned long *temp) { struct qpnp_vadc_thermal_data *vadc_therm = thermal->devdata; struct qpnp_vadc_chip *vadc = vadc_therm->vadc_dev; struct qpnp_vadc_result result; int rc = 0; rc = qpnp_vadc_read(vadc, vadc_therm->vadc_channel, &result); if (rc) { pr_err("VADC read error with %d\n", rc); return rc; } *temp = result.physical; return rc; } static struct thermal_zone_device_ops qpnp_vadc_thermal_ops = { .get_temp = qpnp_vadc_get_temp, }; static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc, struct spmi_device *spmi) { struct device_node *child; struct device_node *node = spmi->dev.of_node; int rc = 0, i = 0; bool thermal_node = false; if (node == NULL) goto thermal_err_sens; for_each_child_of_node(node, child) { char name[QPNP_THERMALNODE_NAME_LENGTH]; vadc->vadc_therm_chan[i].vadc_channel = vadc->adc->adc_channels[i].channel_num; vadc->vadc_therm_chan[i].thermal_chan = i; thermal_node = of_property_read_bool(child, "qcom,vadc-thermal-node"); if (thermal_node) { /* Register with the thermal zone */ vadc->vadc_therm_chan[i].thermal_node = true; snprintf(name, sizeof(name), "%s", vadc->adc->adc_channels[i].name); vadc->vadc_therm_chan[i].tz_dev = thermal_zone_device_register(name, 0, 0, &vadc->vadc_therm_chan[i], &qpnp_vadc_thermal_ops, NULL, 0, 0); if (IS_ERR(vadc->vadc_therm_chan[i].tz_dev)) { pr_err("thermal device register failed.\n"); goto thermal_err_sens; } vadc->vadc_therm_chan[i].vadc_dev = vadc; } i++; thermal_node = false; } return 0; thermal_err_sens: pr_err("Init HWMON failed for qpnp_adc with %d\n", rc); return rc; } static int qpnp_vadc_probe(struct spmi_device *spmi) { struct qpnp_vadc_chip *vadc; struct qpnp_adc_drv *adc_qpnp; struct qpnp_vadc_thermal_data *adc_thermal; struct device_node *node = spmi->dev.of_node; struct device_node *child; int rc, count_adc_channel_list = 0, i = 0; Loading Loading @@ -2164,6 +2241,15 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) } vadc->adc = adc_qpnp; adc_thermal = devm_kzalloc(&spmi->dev, (sizeof(struct qpnp_vadc_thermal_data) * count_adc_channel_list), GFP_KERNEL); if (!adc_thermal) { dev_err(&spmi->dev, "Unable to allocate memory\n"); return -ENOMEM; } vadc->vadc_therm_chan = adc_thermal; rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc); if (rc) { dev_err(&spmi->dev, "failed to read device tree\n"); Loading @@ -2177,6 +2263,11 @@ static int qpnp_vadc_probe(struct spmi_device *spmi) return rc; } vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev); rc = qpnp_vadc_init_thermal(vadc, spmi); if (rc) { dev_err(&spmi->dev, "failed to initialize qpnp thermal adc\n"); return rc; } vadc->vadc_init_calib = false; vadc->max_channels_available = count_adc_channel_list; rc = qpnp_vadc_read_reg(vadc, QPNP_INT_TEST_VAL, &fab_id); Loading Loading @@ -2281,6 +2372,9 @@ err_setup: for_each_child_of_node(node, child) { device_remove_file(&spmi->dev, &vadc->sens_attr[i].dev_attr); if (vadc->vadc_therm_chan[i].thermal_node) thermal_zone_device_unregister( vadc->vadc_therm_chan[i].tz_dev); i++; } hwmon_device_unregister(vadc->vadc_hwmon); Loading @@ -2298,6 +2392,9 @@ static int qpnp_vadc_remove(struct spmi_device *spmi) for_each_child_of_node(node, child) { device_remove_file(&spmi->dev, &vadc->sens_attr[i].dev_attr); if (vadc->vadc_therm_chan[i].thermal_node) thermal_zone_device_unregister( vadc->vadc_therm_chan[i].tz_dev); i++; } hwmon_device_unregister(vadc->vadc_hwmon); Loading
include/linux/qpnp/qpnp-adc.h +2 −1 Original line number Diff line number Diff line /* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -140,6 +140,7 @@ enum qpnp_iadc_channels { #define QPNP_ADC_625_UV 625000 #define QPNP_ADC_HWMON_NAME_LENGTH 64 #define QPNP_MAX_PROP_NAME_LEN 32 #define QPNP_THERMALNODE_NAME_LENGTH 25 /* Structure device for qpnp vadc */ struct qpnp_vadc_chip; Loading