diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 14 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/hwmon.c | 350 | ||||
-rw-r--r-- | drivers/hwmon/ntc_thermistor.c | 452 | ||||
-rw-r--r-- | drivers/hwmon/sht21.c | 1 |
5 files changed, 818 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6030f20..ca2b79c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -768,6 +768,20 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support" + depends on EXPERIMENTAL + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and + send notifications about the temperature. + + Currently, this driver supports + NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333. + + This driver can also be built as a module. If so, the module + will be called ntc-thermistor. + config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" select HWMON_VID diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 28061cf..e4ab597 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a61e781..68f4925 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -21,6 +21,9 @@ #include <linux/gfp.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/slab.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -30,6 +33,17 @@ static struct class *hwmon_class; static DEFINE_IDR(hwmon_idr); static DEFINE_SPINLOCK(idr_lock); +struct hwmon_property { + struct list_head node; + const struct attribute *attr; + struct hwmon_property_head *head; +}; + +struct hwmon_property_head { + struct mutex lock; + struct list_head head; +}; + /** * hwmon_device_register - register w/ hwmon * @dev: the device to register @@ -43,6 +57,7 @@ struct device *hwmon_device_register(struct device *dev) { struct device *hwdev; int id, err; + struct hwmon_property_head *data; again: if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) @@ -65,11 +80,45 @@ again: spin_lock(&idr_lock); idr_remove(&hwmon_idr, id); spin_unlock(&idr_lock); + goto out; } + data = kzalloc(sizeof(struct hwmon_property_head), GFP_KERNEL); + if (data == NULL) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&data->head); + mutex_init(&data->lock); + dev_set_drvdata(hwdev, data); +out: return hwdev; } +static inline void _hwmon_free_prop(struct hwmon_property *prop) +{ + list_del(&prop->node); + prop->attr = NULL; + prop->head = NULL; + kfree(prop); +} + +/** + * hwmon_unregister_all_properties - removes every property registered + * + * @hwmon: the class device to unregister sysfs properties. + */ +void hwmon_unregister_all_properties(struct device *hwmon) +{ + struct hwmon_property_head *data = dev_get_drvdata(hwmon); + struct hwmon_property *pos, *tmp; + + mutex_lock(&data->lock); + list_for_each_entry_safe(pos, tmp, &data->head, node) { + _hwmon_free_prop(pos); + } + mutex_unlock(&data->lock); +} + /** * hwmon_device_unregister - removes the previously registered class device * @@ -78,6 +127,11 @@ again: void hwmon_device_unregister(struct device *dev) { int id; + struct hwmon_property_head *data = dev_get_drvdata(dev); + + hwmon_unregister_all_properties(dev); + mutex_destroy(&data->lock); + kfree(data); if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); @@ -89,6 +143,302 @@ void hwmon_device_unregister(struct device *dev) "hwmon_device_unregister() failed: bad class ID!\n"); } +/** + * hwmon_register_property - register one sysfs entry for hwmon framework + * + * @hwmon: the class device + * @attr: a sysfs entry to be registered. + */ +struct hwmon_property *hwmon_register_property(struct device *hwmon, + const struct device_attribute *attr) +{ + struct hwmon_property_head *data = dev_get_drvdata(hwmon); + struct hwmon_property *entry; + + entry = kzalloc(sizeof(struct hwmon_property), GFP_KERNEL); + if (!entry) + return ERR_PTR(-ENOMEM); + + mutex_lock(&data->lock); + entry->head = data; + entry->attr = &attr->attr; + list_add_tail(&entry->node, &data->head); + mutex_unlock(&data->lock); + + return entry; +} + +/** + * hwmon_unregister_property - unregister the sysfs entry registered to hwmon + * + * @hwmon: the class device + * @prop: hwmon_property entry to be unregistered + * + * Note that hwmon_device_unregister automatically unregister every property. + */ +int hwmon_unregister_property(struct device *hwmon, + struct hwmon_property *prop) +{ + struct hwmon_property_head *data = dev_get_drvdata(hwmon); + struct hwmon_property *pos, *tmp; + int err = -EINVAL; + + if (prop == NULL) + return -EINVAL; + if (!prop->attr || !prop->node.next || !prop->node.prev) + return -EINVAL; + + mutex_lock(&data->lock); + list_for_each_entry_safe(pos, tmp, &data->head, node) { + if (prop == pos) { + _hwmon_free_prop(pos); + err = 0; + break; + } + } + mutex_unlock(&data->lock); + + return err; +} + +/** + * hwmon_register_properties - register a group of sysfs attributes + * + * @hwmon: hwmon class device to register sysfs entries. + * @attrs: a sysfs attribute group to be registered. + */ +int hwmon_register_properties(struct device *hwmon, + const struct attribute_group *attrs) +{ + int i = 0, j, err = 0; + struct attribute **_attrs; + struct hwmon_property *prop; + struct hwmon_property_head *data = dev_get_drvdata(hwmon); + + if (!attrs) + return -EINVAL; + _attrs = attrs->attrs; + if (!_attrs) + return -EINVAL; + + mutex_lock(&data->lock); + + while (_attrs[i]) { + prop = kzalloc(sizeof(struct hwmon_property), GFP_KERNEL); + if (!prop) { + err = -ENOMEM; + break; + } + prop->head = data; + prop->attr = _attrs[i]; + list_add_tail(&prop->node, &data->head); + i++; + } + if (err && i > 0) { + struct hwmon_property *pos, *tmp; + + /* nodes are added to tail. remove from head */ + j = 0; + list_for_each_entry_safe(pos, tmp, &data->head, node) { + if (pos->attr == _attrs[j]) { + _hwmon_free_prop(pos); + + j++; + if (j >= i) + break; + } + } + } + + mutex_unlock(&data->lock); + + return err; +} + +/** + * hwmon_unregister_properties - unregister a group of attributes registered + * + * @hwmon: hwmon class device to register sysfs entries. + * @attrs: a sysfs attribute group to be unregistered. + * + * Note that hwmon_device_unregister automatically unregister every property. + */ +int hwmon_unregister_properties(struct device *hwmon, + const struct attribute_group *attrs) +{ + struct hwmon_property_head *data = dev_get_drvdata(hwmon); + struct attribute **_attrs; + struct hwmon_property *pos, *tmp; + int i = 0; + + if (!attrs) + return -EINVAL; + _attrs = attrs->attrs; + if (!_attrs) + return -EINVAL; + + mutex_lock(&data->lock); + + /* + * Assuming that hwmon_register_properties was used, try to + * remove in the inserted order first. + */ + list_for_each_entry_safe(pos, tmp, &data->head, node) { + if (_attrs[i] == NULL) + break; + if (pos->attr == _attrs[i]) { + _hwmon_free_prop(pos); + i++; + } + } + + /* If it wasn't inserted in the order of attrs */ + while (_attrs[i]) { + list_for_each_entry_safe(pos, tmp, &data->head, node) { + if (pos->attr == _attrs[i]) { + _hwmon_free_prop(pos); + break; + } + } + i++; + } + + mutex_unlock(&data->lock); + return 0; +} + +/** + * hwmon_get_property - get hwmon property based on an LMSENSOR sysfs name. + * + * @hwmon - hwmon class device + * @name - LMSENSOR sysfs name (e.g., "temp1_input") + */ +struct hwmon_property *hwmon_get_property(struct device *hwmon, + const char *name) +{ + struct hwmon_property_head *data = dev_get_drvdata(hwmon); + struct hwmon_property *pos; + + mutex_lock(&data->lock); + list_for_each_entry(pos, &data->head, node) { + if (!strcmp(name, pos->attr->name)) + goto out; + } + pos = ERR_PTR(-EINVAL); +out: + mutex_unlock(&data->lock); + return pos; +} + +/** + * hwmon_get_value - get the sysfs entry value of the hwmon device. + * + * @hwmon - hwmon class device + * @prop - hwmon property (use hwmon_get_property() to get one) + * @value - integer value from prop. + */ +int hwmon_get_value(struct device *hwmon, struct hwmon_property *prop, + int *value) +{ + int err = -EINVAL; + struct device_attribute *devattr; + char buf[13]; /* 32b int max string length = 12 */ + + if (!prop || !prop->attr || !prop->head) + return -EINVAL; + + mutex_lock(&prop->head->lock); + + devattr = container_of(prop->attr, struct device_attribute, attr); + + if (!devattr->show) + goto out; + + err = devattr->show(hwmon->parent, devattr, buf); + if (strnlen(buf, 13) >= 13) { + err = -EINVAL; + goto out; + } + + err = sscanf(buf, "%d", value); + if (err >= 0) + err = 0; +out: + mutex_unlock(&prop->head->lock); + return err; +} +EXPORT_SYMBOL_GPL(hwmon_get_value); + +/** + * hwmon_set_value - set the sysfs entry value of the hwmon device. + * + * @hwmon - hwmon class device + * @prop - hwmon property (use hwmon_get_property() to get one) + * @value - integer value to set prop + */ +int hwmon_set_value(struct device *hwmon, struct hwmon_property *prop, + int value) +{ + int err = -EINVAL, count; + struct device_attribute *devattr; + char buf[13]; /* 32b int max string length = 12 */ + + if (!prop || !prop->attr || !prop->head) + return -EINVAL; + + mutex_lock(&prop->head->lock); + + devattr = container_of(prop->attr, struct device_attribute, attr); + + if (!devattr->store) + goto out; + + count = snprintf(buf, 13, "%d\n", value); + + err = devattr->store(hwmon->parent, devattr, buf, count); +out: + mutex_unlock(&prop->head->lock); + return err; +} +EXPORT_SYMBOL_GPL(hwmon_set_value); + +static int hwmon_dev_match(struct device *dev, void *data) +{ + if (dev->class == hwmon_class) + return 1; + return 0; +} + +/** + * hwmon_find_device - find the hwmon device of the given device + * + * @dev - a parent device of a hwmon class device + */ +struct device *hwmon_find_device(struct device *dev) +{ + return device_find_child(dev, NULL, hwmon_dev_match); +} + +static int hwmon_parent_name_match(struct device *dev, void *data) +{ + char *devname = data; + + if (!strcmp(dev_name(dev->parent), devname)) + return 1; + return 0; +} + +/** + * hwmon_find_device_name - find the hwmon device with device name + * + * @name - device name of the parent device of a hwmon class device + */ +struct device *hwmon_find_device_name(char *devname) +{ + return class_find_device(hwmon_class, NULL, devname, + hwmon_parent_name_match); +} + static void __init hwmon_pci_quirks(void) { #if defined CONFIG_X86 && defined CONFIG_PCI diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c new file mode 100644 index 0000000..7a33593 --- /dev/null +++ b/drivers/hwmon/ntc_thermistor.c @@ -0,0 +1,452 @@ +/* + * ntc_thermistor.c - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/math64.h> +#include <linux/platform_device.h> +#include <linux/err.h> + +#include <linux/platform_data/ntc_thermistor.h> + +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +struct ntc_compensation { + int temp_C; + unsigned int ohm; +}; + +/* + * A compensation table should be sorted by the values of .ohm + * in descending order. + * The following compensation tables are from the specification of Murata NTC + * Thermistors Datasheet + */ +const struct ntc_compensation ncpXXwb473[] = { + { .temp_C = -40, .ohm = 1747920 }, + { .temp_C = -35, .ohm = 1245428 }, + { .temp_C = -30, .ohm = 898485 }, + { .temp_C = -25, .ohm = 655802 }, + { .temp_C = -20, .ohm = 483954 }, + { .temp_C = -15, .ohm = 360850 }, + { .temp_C = -10, .ohm = 271697 }, + { .temp_C = -5, .ohm = 206463 }, + { .temp_C = 0, .ohm = 158214 }, + { .temp_C = 5, .ohm = 122259 }, + { .temp_C = 10, .ohm = 95227 }, + { .temp_C = 15, .ohm = 74730 }, + { .temp_C = 20, .ohm = 59065 }, + { .temp_C = 25, .ohm = 47000 }, + { .temp_C = 30, .ohm = 37643 }, + { .temp_C = 35, .ohm = 30334 }, + { .temp_C = 40, .ohm = 24591 }, + { .temp_C = 45, .ohm = 20048 }, + { .temp_C = 50, .ohm = 16433 }, + { .temp_C = 55, .ohm = 13539 }, + { .temp_C = 60, .ohm = 11209 }, + { .temp_C = 65, .ohm = 9328 }, + { .temp_C = 70, .ohm = 7798 }, + { .temp_C = 75, .ohm = 6544 }, + { .temp_C = 80, .ohm = 5518 }, + { .temp_C = 85, .ohm = 4674 }, + { .temp_C = 90, .ohm = 3972 }, + { .temp_C = 95, .ohm = 3388 }, + { .temp_C = 100, .ohm = 2902 }, + { .temp_C = 105, .ohm = 2494 }, + { .temp_C = 110, .ohm = 2150 }, + { .temp_C = 115, .ohm = 1860 }, + { .temp_C = 120, .ohm = 1615 }, + { .temp_C = 125, .ohm = 1406 }, +}; +const struct ntc_compensation ncpXXwl333[] = { + { .temp_C = -40, .ohm = 1610154 }, + { .temp_C = -35, .ohm = 1130850 }, + { .temp_C = -30, .ohm = 802609 }, + { .temp_C = -25, .ohm = 575385 }, + { .temp_C = -20, .ohm = 416464 }, + { .temp_C = -15, .ohm = 304219 }, + { .temp_C = -10, .ohm = 224193 }, + { .temp_C = -5, .ohm = 166623 }, + { .temp_C = 0, .ohm = 124850 }, + { .temp_C = 5, .ohm = 94287 }, + { .temp_C = 10, .ohm = 71747 }, + { .temp_C = 15, .ohm = 54996 }, + { .temp_C = 20, .ohm = 42455 }, + { .temp_C = 25, .ohm = 33000 }, + { .temp_C = 30, .ohm = 25822 }, + { .temp_C = 35, .ohm = 20335 }, + { .temp_C = 40, .ohm = 16115 }, + { .temp_C = 45, .ohm = 12849 }, + { .temp_C = 50, .ohm = 10306 }, + { .temp_C = 55, .ohm = 8314 }, + { .temp_C = 60, .ohm = 6746 }, + { .temp_C = 65, .ohm = 5503 }, + { .temp_C = 70, .ohm = 4513 }, + { .temp_C = 75, .ohm = 3721 }, + { .temp_C = 80, .ohm = 3084 }, + { .temp_C = 85, .ohm = 2569 }, + { .temp_C = 90, .ohm = 2151 }, + { .temp_C = 95, .ohm = 1809 }, + { .temp_C = 100, .ohm = 1529 }, + { .temp_C = 105, .ohm = 1299 }, + { .temp_C = 110, .ohm = 1108 }, + { .temp_C = 115, .ohm = 949 }, + { .temp_C = 120, .ohm = 817 }, + { .temp_C = 125, .ohm = 707 }, +}; + +struct ntc_data { + struct device *hwmon_dev; + struct ntc_thermistor_platform_data *pdata; + const struct ntc_compensation *comp; + struct device *dev; + int n_comp; + char name[PLATFORM_NAME_SIZE]; +}; + +static inline u64 div64_u64_safe(u64 dividend, u64 divisor) +{ + if (divisor == 0 && dividend == 0) + return 0; + if (divisor == 0) + return UINT_MAX; + return div64_u64(dividend, divisor); +} + +static unsigned int get_ohm_of_thermistor(struct ntc_data *data, + unsigned int uV) +{ + struct ntc_thermistor_platform_data *pdata = data->pdata; + u64 mV = uV / 1000; + u64 pmV = pdata->pullup_uV / 1000; + u64 N, puO, pdO; + puO = pdata->pullup_ohm; + pdO = pdata->pulldown_ohm; + + if (mV == 0) { + if (pdata->connect == NTC_CONNECTED_POSITIVE) + return UINT_MAX; + return 0; + } + if (mV >= pmV) + return (pdata->connect == NTC_CONNECTED_POSITIVE) ? + 0 : UINT_MAX; + + if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) + N = div64_u64_safe(pdO * (pmV - mV), mV); + else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0) + N = div64_u64_safe(puO * mV, pmV - mV); + else if (pdata->connect == NTC_CONNECTED_POSITIVE) + N = div64_u64_safe(pdO * puO * (pmV - mV), + puO * mV - pdO * (pmV - mV)); + else + N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); + + return (unsigned int) N; +} + +static int lookup_comp(struct ntc_data *data, + unsigned int ohm, int *i_low, int *i_high) +{ + int start, end, mid = -1; + + /* Do a binary search on compensation table */ + start = 0; + end = data->n_comp; + + while (end > start) { + mid = start + (end - start) / 2; + if (data->comp[mid].ohm < 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; + } + } + + if (data->comp[mid].ohm <= ohm) { + *i_low = mid; + *i_high = mid - 1; + } else { + *i_low = mid + 1; + *i_high = mid; + } + + return 0; +} + +static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp) +{ + int low, high; + int ret; + + ret = lookup_comp(data, ohm, &low, &high); + if (ret) { + /* 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; + } else { + *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; +} + +static int ntc_thermistor_read(struct ntc_data *data, int *temp) +{ + 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; + } + + 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 0; +} + +static ssize_t ntc_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t ntc_show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "4\n"); +} + +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; + + ret = ntc_thermistor_read(data, &temp); + if (ret) + return ret; + return sprintf(buf, "%d\n", temp); +} + +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); +static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL); + +static struct attribute *ntc_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ntc_attr_group = { + .attrs = ntc_attributes, +}; + +static int __devinit ntc_thermistor_probe(struct platform_device *pdev) +{ + struct ntc_data *data; + struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + + /* 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"); + 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"); + pdata->read_uV = NULL; + } + + if (pdata->read_uV && (pdata->pullup_uV == 0 || + (pdata->pullup_ohm == 0 && pdata->connect == + NTC_CONNECTED_GROUND) || + (pdata->pulldown_ohm == 0 && pdata->connect == + 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"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &pdev->dev; + data->pdata = pdata; + strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE); + + switch (pdev->id_entry->driver_data) { + case TYPE_NCPXXWB473: + data->comp = ncpXXwb473; + data->n_comp = ARRAY_SIZE(ncpXXwb473); + break; + case TYPE_NCPXXWL333: + data->comp = ncpXXwl333; + data->n_comp = ARRAY_SIZE(ncpXXwl333); + break; + default: + dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", + pdev->id_entry->driver_data, + pdev->id_entry->name); + ret = -EINVAL; + goto err; + } + + platform_set_drvdata(pdev, data); + + ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); + if (ret) { + dev_err(data->dev, "unable to create sysfs files\n"); + goto err; + } + + data->hwmon_dev = hwmon_device_register(data->dev); + if (IS_ERR_OR_NULL(data->hwmon_dev)) { + dev_err(data->dev, "unable to register as hwmon device.\n"); + ret = -EINVAL; + goto err_after_sysfs; + } + + hwmon_register_properties(data->hwmon_dev, &ntc_attr_group); + + dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n", + pdev->name, pdev->id, pdev->id_entry->name, + pdev->id_entry->driver_data); + return 0; +err_after_sysfs: + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); +err: + kfree(data); + return ret; +} + +static int __devexit ntc_thermistor_remove(struct platform_device *pdev) +{ + struct ntc_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + platform_set_drvdata(pdev, NULL); + + kfree(data); + + return 0; +} + +static const struct platform_device_id ntc_thermistor_id[] = { + { "ncp15wb473", TYPE_NCPXXWB473 }, + { "ncp18wb473", TYPE_NCPXXWB473 }, + { "ncp21wb473", TYPE_NCPXXWB473 }, + { "ncp03wb473", TYPE_NCPXXWB473 }, + { "ncp15wl333", TYPE_NCPXXWL333 }, + { }, +}; + +static struct platform_driver ntc_thermistor_driver = { + .driver = { + .name = "ntc-thermistor", + .owner = THIS_MODULE, + }, + .probe = ntc_thermistor_probe, + .remove = __devexit_p(ntc_thermistor_remove), + .id_table = ntc_thermistor_id, +}; + +static int __init ntc_thermistor_driver_init(void) +{ + return platform_driver_register(&ntc_thermistor_driver); +} +module_init(ntc_thermistor_driver_init); + +static void __exit ntc_thermistor_driver_exit(void) +{ + platform_driver_unregister(&ntc_thermistor_driver); +} +module_exit(ntc_thermistor_driver_exit); + +MODULE_DESCRIPTION("NTC Thermistor Driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntc-thermistor"); diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index 1c8c981..be1c9ff 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -210,6 +210,7 @@ static int __devinit sht21_probe(struct i2c_client *client, struct sht21 *sht21; int err; + pr_info("================\n%s\n================", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_err(&client->dev, |