Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: (ntc_thermistor) Add DT with IIO support to NTC thermistor driver

This patch adds DT support to NTC driver to parse the
platform data.

Also adds the support to work as an iio device client.

During the probe ntc driver gets the respective channels of ADC
and uses iio_raw_read calls to get the ADC converted value.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
[Guenter Roeck: fixed Kconfig dependencies; use ERR_CAST]
Tested-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Naveen Krishna Chatradhi and committed by
Guenter Roeck
9e8269de 4b5e536b

+163 -20
+29
Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt
··· 1 + NTC Thermistor hwmon sensors 2 + ------------------------------- 3 + 4 + Requires node properties: 5 + - "compatible" value : one of 6 + "ntc,ncp15wb473" 7 + "ntc,ncp18wb473" 8 + "ntc,ncp21wb473" 9 + "ntc,ncp03wb473" 10 + "ntc,ncp15wl333" 11 + - "pullup-uv" Pull up voltage in micro volts 12 + - "pullup-ohm" Pull up resistor value in ohms 13 + - "pulldown-ohm" Pull down resistor value in ohms 14 + - "connected-positive" Always ON, If not specified. 15 + Status change is possible. 16 + - "io-channels" Channel node of ADC to be used for 17 + conversion. 18 + 19 + Read more about iio bindings at 20 + Documentation/devicetree/bindings/iio/iio-bindings.txt 21 + 22 + Example: 23 + ncp15wb473@0 { 24 + compatible = "ntc,ncp15wb473"; 25 + pullup-uv = <1800000>; 26 + pullup-ohm = <47000>; 27 + pulldown-ohm = <0>; 28 + io-channels = <&adc 3>; 29 + };
+1
drivers/hwmon/Kconfig
··· 899 899 900 900 config SENSORS_NTC_THERMISTOR 901 901 tristate "NTC thermistor support" 902 + depends on (!OF && !IIO) || (OF && IIO) 902 903 help 903 904 This driver supports NTC thermistors sensor reading and its 904 905 interpretation. The driver can also monitor the temperature and
+126 -19
drivers/hwmon/ntc_thermistor.c
··· 26 26 #include <linux/math64.h> 27 27 #include <linux/platform_device.h> 28 28 #include <linux/err.h> 29 + #include <linux/of.h> 30 + #include <linux/of_device.h> 29 31 30 32 #include <linux/platform_data/ntc_thermistor.h> 33 + 34 + #include <linux/iio/iio.h> 35 + #include <linux/iio/machine.h> 36 + #include <linux/iio/driver.h> 37 + #include <linux/iio/consumer.h> 31 38 32 39 #include <linux/hwmon.h> 33 40 #include <linux/hwmon-sysfs.h> ··· 42 35 struct ntc_compensation { 43 36 int temp_C; 44 37 unsigned int ohm; 38 + }; 39 + 40 + static const struct platform_device_id ntc_thermistor_id[] = { 41 + { "ncp15wb473", TYPE_NCPXXWB473 }, 42 + { "ncp18wb473", TYPE_NCPXXWB473 }, 43 + { "ncp21wb473", TYPE_NCPXXWB473 }, 44 + { "ncp03wb473", TYPE_NCPXXWB473 }, 45 + { "ncp15wl333", TYPE_NCPXXWL333 }, 46 + { }, 45 47 }; 46 48 47 49 /* ··· 140 124 int n_comp; 141 125 char name[PLATFORM_NAME_SIZE]; 142 126 }; 127 + 128 + #ifdef CONFIG_OF 129 + static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata) 130 + { 131 + struct iio_channel *channel = pdata->chan; 132 + unsigned int result; 133 + int val, ret; 134 + 135 + ret = iio_read_channel_raw(channel, &val); 136 + if (ret < 0) { 137 + pr_err("read channel() error: %d\n", ret); 138 + return ret; 139 + } 140 + 141 + /* unit: mV */ 142 + result = pdata->pullup_uV * val; 143 + result >>= 12; 144 + 145 + return result; 146 + } 147 + 148 + static const struct of_device_id ntc_match[] = { 149 + { .compatible = "ntc,ncp15wb473", 150 + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, 151 + { .compatible = "ntc,ncp18wb473", 152 + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, 153 + { .compatible = "ntc,ncp21wb473", 154 + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, 155 + { .compatible = "ntc,ncp03wb473", 156 + .data = &ntc_thermistor_id[TYPE_NCPXXWB473] }, 157 + { .compatible = "ntc,ncp15wl333", 158 + .data = &ntc_thermistor_id[TYPE_NCPXXWL333] }, 159 + { }, 160 + }; 161 + MODULE_DEVICE_TABLE(of, ntc_match); 162 + 163 + static struct ntc_thermistor_platform_data * 164 + ntc_thermistor_parse_dt(struct platform_device *pdev) 165 + { 166 + struct iio_channel *chan; 167 + struct device_node *np = pdev->dev.of_node; 168 + struct ntc_thermistor_platform_data *pdata; 169 + 170 + if (!np) 171 + return NULL; 172 + 173 + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 174 + if (!pdata) 175 + return ERR_PTR(-ENOMEM); 176 + 177 + chan = iio_channel_get(&pdev->dev, NULL); 178 + if (IS_ERR(chan)) 179 + return ERR_CAST(chan); 180 + 181 + if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uV)) 182 + return ERR_PTR(-ENODEV); 183 + if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm)) 184 + return ERR_PTR(-ENODEV); 185 + if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm)) 186 + return ERR_PTR(-ENODEV); 187 + 188 + if (of_find_property(np, "connected-positive", NULL)) 189 + pdata->connect = NTC_CONNECTED_POSITIVE; 190 + else /* status change should be possible if not always on. */ 191 + pdata->connect = NTC_CONNECTED_GROUND; 192 + 193 + pdata->chan = chan; 194 + pdata->read_uV = ntc_adc_iio_read; 195 + 196 + return pdata; 197 + } 198 + static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) 199 + { 200 + if (pdata->chan) 201 + iio_channel_release(pdata->chan); 202 + } 203 + #else 204 + static struct ntc_thermistor_platform_data * 205 + ntc_thermistor_parse_dt(struct platform_device *pdev) 206 + { 207 + return NULL; 208 + } 209 + 210 + static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) 211 + { } 212 + #endif 143 213 144 214 static inline u64 div64_u64_safe(u64 dividend, u64 divisor) 145 215 { ··· 361 259 return data->pdata->read_ohm(); 362 260 363 261 if (data->pdata->read_uV) { 364 - read_uV = data->pdata->read_uV(); 262 + read_uV = data->pdata->read_uV(data->pdata); 365 263 if (read_uV < 0) 366 264 return read_uV; 367 265 return get_ohm_of_thermistor(data, read_uV); ··· 413 311 414 312 static int ntc_thermistor_probe(struct platform_device *pdev) 415 313 { 314 + const struct of_device_id *of_id = 315 + of_match_device(of_match_ptr(ntc_match), &pdev->dev); 316 + const struct platform_device_id *pdev_id; 317 + struct ntc_thermistor_platform_data *pdata; 416 318 struct ntc_data *data; 417 - struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data; 418 - int ret = 0; 319 + int ret; 320 + 321 + pdata = ntc_thermistor_parse_dt(pdev); 322 + if (IS_ERR(pdata)) 323 + return PTR_ERR(pdata); 324 + else if (pdata == NULL) 325 + pdata = pdev->dev.platform_data; 419 326 420 327 if (!pdata) { 421 328 dev_err(&pdev->dev, "No platform init data supplied.\n"); ··· 460 349 if (!data) 461 350 return -ENOMEM; 462 351 352 + pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); 353 + 463 354 data->dev = &pdev->dev; 464 355 data->pdata = pdata; 465 - strlcpy(data->name, pdev->id_entry->name, sizeof(data->name)); 356 + strlcpy(data->name, pdev_id->name, sizeof(data->name)); 466 357 467 - switch (pdev->id_entry->driver_data) { 358 + switch (pdev_id->driver_data) { 468 359 case TYPE_NCPXXWB473: 469 360 data->comp = ncpXXwb473; 470 361 data->n_comp = ARRAY_SIZE(ncpXXwb473); ··· 477 364 break; 478 365 default: 479 366 dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", 480 - pdev->id_entry->driver_data, 481 - pdev->id_entry->name); 367 + pdev_id->driver_data, pdev_id->name); 482 368 return -EINVAL; 483 369 } 484 370 ··· 496 384 goto err_after_sysfs; 497 385 } 498 386 499 - dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n", 500 - pdev->name, pdev->id, pdev->id_entry->name, 501 - pdev->id_entry->driver_data); 387 + dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", 388 + pdev->name); 389 + 502 390 return 0; 503 391 err_after_sysfs: 504 392 sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); 393 + ntc_iio_channel_release(pdata); 505 394 return ret; 506 395 } 507 396 508 397 static int ntc_thermistor_remove(struct platform_device *pdev) 509 398 { 510 399 struct ntc_data *data = platform_get_drvdata(pdev); 400 + struct ntc_thermistor_platform_data *pdata = data->pdata; 511 401 512 402 hwmon_device_unregister(data->hwmon_dev); 513 403 sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); 404 + ntc_iio_channel_release(pdata); 514 405 platform_set_drvdata(pdev, NULL); 515 406 516 407 return 0; 517 408 } 518 409 519 - static const struct platform_device_id ntc_thermistor_id[] = { 520 - { "ncp15wb473", TYPE_NCPXXWB473 }, 521 - { "ncp18wb473", TYPE_NCPXXWB473 }, 522 - { "ncp21wb473", TYPE_NCPXXWB473 }, 523 - { "ncp03wb473", TYPE_NCPXXWB473 }, 524 - { "ncp15wl333", TYPE_NCPXXWL333 }, 525 - { }, 526 - }; 527 - 528 410 static struct platform_driver ntc_thermistor_driver = { 529 411 .driver = { 530 412 .name = "ntc-thermistor", 531 413 .owner = THIS_MODULE, 414 + .of_match_table = of_match_ptr(ntc_match), 532 415 }, 533 416 .probe = ntc_thermistor_probe, 534 417 .remove = ntc_thermistor_remove,
+7 -1
include/linux/platform_data/ntc_thermistor.h
··· 21 21 #ifndef _LINUX_NTC_H 22 22 #define _LINUX_NTC_H 23 23 24 + struct iio_channel; 25 + 24 26 enum ntc_thermistor_type { 25 27 TYPE_NCPXXWB473, 26 28 TYPE_NCPXXWL333, ··· 41 39 * described at Documentation/hwmon/ntc_thermistor 42 40 * 43 41 * pullup/down_ohm: 0 for infinite / not-connected 42 + * 43 + * chan: iio_channel pointer to communicate with the ADC which the 44 + * thermistor is using for conversion of the analog values. 44 45 */ 45 - int (*read_uV)(void); 46 + int (*read_uV)(struct ntc_thermistor_platform_data *); 46 47 unsigned int pullup_uV; 47 48 48 49 unsigned int pullup_ohm; 49 50 unsigned int pulldown_ohm; 50 51 enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect; 52 + struct iio_channel *chan; 51 53 52 54 int (*read_ohm)(void); 53 55 };