diff options
Diffstat (limited to 'drivers/hwmon/ntc_thermistor.c')
-rw-r--r-- | drivers/hwmon/ntc_thermistor.c | 191 |
1 files changed, 91 insertions, 100 deletions
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 9b382ec2c3bd..6da9696e1827 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -134,8 +134,7 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor) return div64_u64(dividend, divisor); } -static unsigned int get_ohm_of_thermistor(struct ntc_data *data, - unsigned int uV) +static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV) { struct ntc_thermistor_platform_data *pdata = data->pdata; u64 mV = uV / 1000; @@ -146,12 +145,12 @@ static unsigned int get_ohm_of_thermistor(struct ntc_data *data, if (mV == 0) { if (pdata->connect == NTC_CONNECTED_POSITIVE) - return UINT_MAX; + return INT_MAX; return 0; } if (mV >= pmV) return (pdata->connect == NTC_CONNECTED_POSITIVE) ? - 0 : UINT_MAX; + 0 : INT_MAX; if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) N = div64_u64_safe(pdO * (pmV - mV), mV); @@ -163,113 +162,109 @@ static unsigned int get_ohm_of_thermistor(struct ntc_data *data, else N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); - return (unsigned int) N; + if (N > INT_MAX) + N = INT_MAX; + return N; } -static int lookup_comp(struct ntc_data *data, - unsigned int ohm, int *i_low, int *i_high) +static void lookup_comp(struct ntc_data *data, unsigned int ohm, + int *i_low, int *i_high) { - int start, end, mid = -1; + int start, end, mid; + + /* + * Handle special cases: Resistance is higher than or equal to + * resistance in first table entry, or resistance is lower or equal + * to resistance in last table entry. + * In these cases, return i_low == i_high, either pointing to the + * beginning or to the end of the table depending on the condition. + */ + if (ohm >= data->comp[0].ohm) { + *i_low = 0; + *i_high = 0; + return; + } + if (ohm <= data->comp[data->n_comp - 1].ohm) { + *i_low = data->n_comp - 1; + *i_high = data->n_comp - 1; + return; + } /* Do a binary search on compensation table */ start = 0; end = data->n_comp; - - while (end > start) { + while (start < end) { mid = start + (end - start) / 2; - if (data->comp[mid].ohm < ohm) + /* + * start <= mid < end + * data->comp[start].ohm > ohm >= data->comp[end].ohm + * + * We could check for "ohm == data->comp[mid].ohm" here, but + * that is a quite unlikely condition, and we would have to + * check again after updating start. Check it at the end instead + * for simplicity. + */ + if (ohm >= data->comp[mid].ohm) { end = mid; - else if (data->comp[mid].ohm > ohm) - start = mid + 1; - else - break; - } - - if (mid == 0) { - if (data->comp[mid].ohm > ohm) { - *i_high = mid; - *i_low = mid + 1; - return 0; - } else { - *i_low = mid; - *i_high = -1; - return -EINVAL; - } - } - if (mid == (data->n_comp - 1)) { - if (data->comp[mid].ohm <= ohm) { - *i_low = mid; - *i_high = mid - 1; - return 0; } else { - *i_low = -1; - *i_high = mid; - return -EINVAL; + start = mid + 1; + /* + * ohm >= data->comp[start].ohm might be true here, + * since we set start to mid + 1. In that case, we are + * done. We could keep going, but the condition is quite + * likely to occur, so it is worth checking for it. + */ + if (ohm >= data->comp[start].ohm) + end = start; } + /* + * start <= end + * data->comp[start].ohm >= ohm >= data->comp[end].ohm + */ } - - if (data->comp[mid].ohm <= ohm) { - *i_low = mid; - *i_high = mid - 1; - } else { - *i_low = mid + 1; - *i_high = mid; - } - - return 0; + /* + * start == end + * ohm >= data->comp[end].ohm + */ + *i_low = end; + if (ohm == data->comp[end].ohm) + *i_high = end; + else + *i_high = end - 1; } -static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp) +static int get_temp_mC(struct ntc_data *data, unsigned int ohm) { int low, high; - int ret; + int temp; - ret = lookup_comp(data, ohm, &low, &high); - if (ret) { + lookup_comp(data, ohm, &low, &high); + if (low == high) { /* Unable to use linear approximation */ - if (low != -1) - *temp = data->comp[low].temp_C * 1000; - else if (high != -1) - *temp = data->comp[high].temp_C * 1000; - else - return ret; + temp = data->comp[low].temp_C * 1000; } else { - *temp = data->comp[low].temp_C * 1000 + + temp = data->comp[low].temp_C * 1000 + ((data->comp[high].temp_C - data->comp[low].temp_C) * 1000 * ((int)ohm - (int)data->comp[low].ohm)) / ((int)data->comp[high].ohm - (int)data->comp[low].ohm); } - - return 0; + return temp; } -static int ntc_thermistor_read(struct ntc_data *data, int *temp) +static int ntc_thermistor_get_ohm(struct ntc_data *data) { - int ret; - int read_ohm, read_uV; - unsigned int ohm = 0; - - if (data->pdata->read_ohm) { - read_ohm = data->pdata->read_ohm(); - if (read_ohm < 0) - return read_ohm; - ohm = (unsigned int)read_ohm; - } + int read_uV; + + if (data->pdata->read_ohm) + return data->pdata->read_ohm(); if (data->pdata->read_uV) { read_uV = data->pdata->read_uV(); if (read_uV < 0) return read_uV; - ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV); - } - - ret = get_temp_mC(data, ohm, temp); - if (ret) { - dev_dbg(data->dev, "Sensor reading function not available.\n"); - return ret; + return get_ohm_of_thermistor(data, read_uV); } - - return 0; + return -EINVAL; } static ssize_t ntc_show_name(struct device *dev, @@ -290,12 +285,13 @@ static ssize_t ntc_show_temp(struct device *dev, struct device_attribute *attr, char *buf) { struct ntc_data *data = dev_get_drvdata(dev); - int temp, ret; + int ohm; - ret = ntc_thermistor_read(data, &temp); - if (ret) - return ret; - return sprintf(buf, "%d\n", temp); + ohm = ntc_thermistor_get_ohm(data); + if (ohm < 0) + return ohm; + + return sprintf(buf, "%d\n", get_temp_mC(data, ohm)); } static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); @@ -326,14 +322,14 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) /* Either one of the two is required. */ if (!pdata->read_uV && !pdata->read_ohm) { - dev_err(&pdev->dev, "Both read_uV and read_ohm missing." - "Need either one of the two.\n"); + dev_err(&pdev->dev, + "Both read_uV and read_ohm missing. Need either one of the two.\n"); return -EINVAL; } if (pdata->read_uV && pdata->read_ohm) { - dev_warn(&pdev->dev, "Only one of read_uV and read_ohm " - "is needed; ignoring read_uV.\n"); + dev_warn(&pdev->dev, + "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n"); pdata->read_uV = NULL; } @@ -344,12 +340,12 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) NTC_CONNECTED_POSITIVE) || (pdata->connect != NTC_CONNECTED_POSITIVE && pdata->connect != NTC_CONNECTED_GROUND))) { - dev_err(&pdev->dev, "Required data to use read_uV not " - "supplied.\n"); + dev_err(&pdev->dev, + "Required data to use read_uV not supplied.\n"); return -EINVAL; } - data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -370,8 +366,7 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", pdev->id_entry->driver_data, pdev->id_entry->name); - ret = -EINVAL; - goto err; + return -EINVAL; } platform_set_drvdata(pdev, data); @@ -379,13 +374,13 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); if (ret) { dev_err(data->dev, "unable to create sysfs files\n"); - goto err; + return ret; } data->hwmon_dev = hwmon_device_register(data->dev); - if (IS_ERR_OR_NULL(data->hwmon_dev)) { + if (IS_ERR(data->hwmon_dev)) { dev_err(data->dev, "unable to register as hwmon device.\n"); - ret = -EINVAL; + ret = PTR_ERR(data->hwmon_dev); goto err_after_sysfs; } @@ -395,8 +390,6 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) return 0; err_after_sysfs: sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); -err: - kfree(data); return ret; } @@ -408,8 +401,6 @@ static int __devexit ntc_thermistor_remove(struct platform_device *pdev) sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); platform_set_drvdata(pdev, NULL); - kfree(data); - return 0; } |