From 731b4cace10f105a81d622b70f9a35f577612d63 Mon Sep 17 00:00:00 2001 From: David George Date: Thu, 2 Jun 2011 08:43:45 -0700 Subject: hwmon: Driver for MAX1668 This patch adds support for MAX1668 and compatible temperature sensors. Signed-off-by: David George [guenter.roeck@ericsson.com: minor cleanup of probe error path] Signed-off-by: Guenter Roeck --- Documentation/hwmon/max1668 | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/hwmon/max1668 (limited to 'Documentation') diff --git a/Documentation/hwmon/max1668 b/Documentation/hwmon/max1668 new file mode 100644 index 000000000000..0616ed9758df --- /dev/null +++ b/Documentation/hwmon/max1668 @@ -0,0 +1,60 @@ +Kernel driver max1668 +===================== + +Supported chips: + * Maxim MAX1668, MAX1805 and MAX1989 + Prefix: 'max1668' + Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e + Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX1668-MAX1989.pdf + +Author: + David George + +Description +----------- + +This driver implements support for the Maxim MAX1668, MAX1805 and MAX1989 +chips. + +The three devices are very similar, but the MAX1805 has a reduced feature +set; only two remote temperature inputs vs the four avaible on the other +two ICs. + +The driver is able to distinguish between the devices and creates sysfs +entries as follows: + +MAX1805, MAX1668 and MAX1989: + +temp1_input ro local (ambient) temperature +temp1_max rw local temperature maximum threshold for alarm +temp1_max_alarm ro local temperature maximum threshold alarm +temp1_min rw local temperature minimum threshold for alarm +temp1_min_alarm ro local temperature minimum threshold alarm +temp2_input ro remote temperature 1 +temp2_max rw remote temperature 1 maximum threshold for alarm +temp2_max_alarm ro remote temperature 1 maximum threshold alarm +temp2_min rw remote temperature 1 minimum threshold for alarm +temp2_min_alarm ro remote temperature 1 minimum threshold alarm +temp3_input ro remote temperature 2 +temp3_max rw remote temperature 2 maximum threshold for alarm +temp3_max_alarm ro remote temperature 2 maximum threshold alarm +temp3_min rw remote temperature 2 minimum threshold for alarm +temp3_min_alarm ro remote temperature 2 minimum threshold alarm + +MAX1668 and MAX1989 only: +temp4_input ro remote temperature 3 +temp4_max rw remote temperature 3 maximum threshold for alarm +temp4_max_alarm ro remote temperature 3 maximum threshold alarm +temp4_min rw remote temperature 3 minimum threshold for alarm +temp4_min_alarm ro remote temperature 3 minimum threshold alarm +temp5_input ro remote temperature 4 +temp5_max rw remote temperature 4 maximum threshold for alarm +temp5_max_alarm ro remote temperature 4 maximum threshold alarm +temp5_min rw remote temperature 4 minimum threshold for alarm +temp5_min_alarm ro remote temperature 4 minimum threshold alarm + +Module Parameters +----------------- + +* read_only: int + Set to non-zero if you wish to prevent write access to alarm thresholds. -- cgit v1.2.3 From f22aaaa70d8c24e5dc7d23a219c4beace8354b65 Mon Sep 17 00:00:00 2001 From: Donggeun Kim Date: Mon, 20 Jun 2011 16:48:19 +0900 Subject: hwmon: Driver for NTC Thermistors Add support for NTC Thermistor series. In this release, the following thermistors are supported: NCP15WB473, NCP18WB473, NCP03WB473, and NCP15WL333. This driver is based on the datasheet of MURATA. The driver in the patch does conversion from the raw ADC value (either voltage or resistence) to temperature. In order to use voltage values as input, the circuit schematics should be provided with the platform data. A compensation table for each type of thermistor is provided for the conversion. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: KyungMin Park Reviewed-by: Shubhrajyoti D Signed-off-by: Guenter Roeck --- Documentation/hwmon/ntc_thermistor | 93 ++++++ drivers/hwmon/Kconfig | 14 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ntc_thermistor.c | 453 +++++++++++++++++++++++++++ include/linux/platform_data/ntc_thermistor.h | 53 ++++ 5 files changed, 614 insertions(+) create mode 100644 Documentation/hwmon/ntc_thermistor create mode 100644 drivers/hwmon/ntc_thermistor.c create mode 100644 include/linux/platform_data/ntc_thermistor.h (limited to 'Documentation') diff --git a/Documentation/hwmon/ntc_thermistor b/Documentation/hwmon/ntc_thermistor new file mode 100644 index 000000000000..3bfda94096fd --- /dev/null +++ b/Documentation/hwmon/ntc_thermistor @@ -0,0 +1,93 @@ +Kernel driver ntc_thermistor +================= + +Supported thermistors: +* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333 + Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473', 'ncp15wl333' + Datasheet: Publicly available at Murata + +Other NTC thermistors can be supported simply by adding compensation +tables; e.g., NCP15WL333 support is added by the table ncpXXwl333. + +Authors: + MyungJoo Ham + +Description +----------- + +The NTC thermistor is a simple thermistor that requires users to provide the +resistance and lookup the corresponding compensation table to get the +temperature input. + +The NTC driver provides lookup tables with a linear approximation function +and four circuit models with an option not to use any of the four models. + +The four circuit models provided are: + + $: resister, [TH]: the thermistor + + 1. connect = NTC_CONNECTED_POSITIVE, pullup_ohm > 0 + + [pullup_uV] + | | + [TH] $ (pullup_ohm) + | | + +----+-----------------------[read_uV] + | + $ (pulldown_ohm) + | + --- (ground) + + 2. connect = NTC_CONNECTED_POSITIVE, pullup_ohm = 0 (not-connected) + + [pullup_uV] + | + [TH] + | + +----------------------------[read_uV] + | + $ (pulldown_ohm) + | + --- (ground) + + 3. connect = NTC_CONNECTED_GROUND, pulldown_ohm > 0 + + [pullup_uV] + | + $ (pullup_ohm) + | + +----+-----------------------[read_uV] + | | + [TH] $ (pulldown_ohm) + | | + -------- (ground) + + 4. connect = NTC_CONNECTED_GROUND, pulldown_ohm = 0 (not-connected) + + [pullup_uV] + | + $ (pullup_ohm) + | + +----------------------------[read_uV] + | + [TH] + | + --- (ground) + +When one of the four circuit models is used, read_uV, pullup_uV, pullup_ohm, +pulldown_ohm, and connect should be provided. When none of the four models +are suitable or the user can get the resistance directly, the user should +provide read_ohm and _not_ provide the others. + +Sysfs Interface +--------------- +name the mandatory global attribute, the thermistor name. + +temp1_type always 4 (thermistor) + RO + +temp1_input measure the temperature and provide the measured value. + (reading this file initiates the reading procedure.) + RO + +Note that each NTC thermistor has only _one_ thermistor; thus, only temp1 exists. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index dec5eee3542f..770fc8195627 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -777,6 +777,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 a6d519e568c0..1b7dc770aa60 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -92,6 +92,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/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c new file mode 100644 index 000000000000..d7926f4336b5 --- /dev/null +++ b/drivers/hwmon/ntc_thermistor.c @@ -0,0 +1,453 @@ +/* + * ntc_thermistor.c - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +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; + } + if (data->comp[mid].ohm > ohm) { + *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; + } + + 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_init(void) +{ + return platform_driver_register(&ntc_thermistor_driver); +} + +module_init(ntc_thermistor_init); + +static void __exit ntc_thermistor_cleanup(void) +{ + platform_driver_unregister(&ntc_thermistor_driver); +} + +module_exit(ntc_thermistor_cleanup); + +MODULE_DESCRIPTION("NTC Thermistor Driver"); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntc-thermistor"); diff --git a/include/linux/platform_data/ntc_thermistor.h b/include/linux/platform_data/ntc_thermistor.h new file mode 100644 index 000000000000..abd286215279 --- /dev/null +++ b/include/linux/platform_data/ntc_thermistor.h @@ -0,0 +1,53 @@ +/* + * ntc_thermistor.h - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham + * + * 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 + */ +#ifndef _LINUX_NTC_H +#define _LINUX_NTC_H + +enum ntc_thermistor_type { + TYPE_NCPXXWB473, + TYPE_NCPXXWL333, +}; + +struct ntc_thermistor_platform_data { + /* + * One (not both) of read_uV and read_ohm should be provided and only + * one of the two should be provided. + * Both functions should return negative value for an error case. + * + * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required to use + * read_uV() + * + * How to setup pullup_ohm, pulldown_ohm, and connect is + * described at Documentation/hwmon/ntc + * + * pullup/down_ohm: 0 for infinite / not-connected + */ + int (*read_uV)(void); + unsigned int pullup_uV; + + unsigned int pullup_ohm; + unsigned int pulldown_ohm; + enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect; + + int (*read_ohm)(void); +}; + +#endif /* _LINUX_NTC_H */ -- cgit v1.2.3 From 2ef017935d698b1c7c7421a7ebe20579d8f904dd Mon Sep 17 00:00:00 2001 From: Stijn Devriendt Date: Mon, 6 Jun 2011 10:40:45 +0000 Subject: hwmon: (lm90) Add support for Philips SA56004 Add support for Philips SA56004, an LM86 compatible temperature sensor. Signed-off-by: Stijn Devriendt Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90 | 9 ++++++++- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index f3efd18e87f4..9cd14cfe6515 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -113,7 +113,11 @@ Supported chips: Prefix: 'w83l771' Addresses scanned: I2C 0x4c Datasheet: Not publicly available, can be requested from Nuvoton - + * Philips/NXP SA56004X + Prefix: 'sa56004' + Addresses scanned: I2C 0x48 through 0x4F + Datasheet: Publicly available at NXP website + http://ics.nxp.com/products/interface/datasheet/sa56004x.pdf Author: Jean Delvare @@ -193,6 +197,9 @@ W83L771AWG/ASG * The AWG and ASG variants only differ in package format. * Diode ideality factor configuration (remote sensor) at 0xE3 +SA56004X: + * Better local resolution + All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature, 0.125 degree for the remote temperature, except for the MAX6657, MAX6658 and MAX6659 which have a diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 770fc8195627..1396290a31a4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -623,7 +623,7 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips. + Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2f94f9504804..708bf0e7e4ac 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -54,6 +54,9 @@ * and extended mode. They are mostly compatible with LM90 except for a data * format difference for the temperature value registers. * + * This driver also supports the SA56004 from Philips. This device is + * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -96,13 +99,15 @@ * MAX6659 can have address 0x4c, 0x4d or 0x4e. * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. + * SA56004 can have address 0x48 through 0x4F. */ static const unsigned short normal_i2c[] = { - 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; + 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696 }; + max6646, w83l771, max6696, sa56004 }; /* * The LM90 registers @@ -152,6 +157,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define MAX6659_REG_R_LOCAL_EMERG 0x17 #define MAX6659_REG_W_LOCAL_EMERG 0x17 +/* SA56004 registers */ + +#define SA56004_REG_R_LOCAL_TEMPL 0x22 + #define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */ #define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ @@ -192,6 +201,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6696", max6696 }, { "nct1008", adt7461 }, { "w83l771", w83l771 }, + { "sa56004", sa56004 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -204,6 +214,8 @@ struct lm90_params { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate register value */ + u8 reg_local_ext; /* Local extension register if + LM90_HAVE_LOCAL_EXT is set*/ }; static const struct lm90_params lm90_params[] = { @@ -238,16 +250,19 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_LOCAL_EXT, .alert_alarms = 0x7c, .max_convrate = 6, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6657] = { .flags = LM90_HAVE_LOCAL_EXT, .alert_alarms = 0x7c, .max_convrate = 8, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6659] = { .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY, .alert_alarms = 0x7c, .max_convrate = 8, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6680] = { .flags = LM90_HAVE_OFFSET, @@ -259,12 +274,20 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, .alert_alarms = 0x187c, .max_convrate = 6, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7c, .max_convrate = 8, }, + [sa56004] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_LOCAL_EXT, + .alert_alarms = 0x7b, + .max_convrate = 9, + .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, + }, }; /* @@ -286,6 +309,7 @@ struct lm90_data { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate */ + u8 reg_local_ext; /* local extension register offset */ /* registers values */ s8 temp8[8]; /* 0: local low limit @@ -454,7 +478,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (data->flags & LM90_HAVE_LOCAL_EXT) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, - MAX6657_REG_R_LOCAL_TEMPL, + data->reg_local_ext, &data->temp11[4]); } else { if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, @@ -1263,6 +1287,11 @@ static int lm90_detect(struct i2c_client *new_client, name = "w83l771"; } } + } else + if (man_id == 0xA1) { /* NXP Semiconductor/Philips */ + if (chip_id == 0x00 && address >= 0x48 && address <= 0x4F) { + name = "sa56004"; + } } if (!name) { /* identification failed */ @@ -1372,6 +1401,11 @@ static int lm90_probe(struct i2c_client *new_client, /* Set maximum conversion rate */ data->max_convrate = lm90_params[data->kind].max_convrate; + if (data->flags & LM90_HAVE_LOCAL_EXT) { + data->reg_local_ext = lm90_params[data->kind].reg_local_ext; + WARN_ON(data->reg_local_ext == 0); + } + /* Initialize the LM90 chip */ lm90_init_client(new_client); -- cgit v1.2.3 From fffd80ccc1e6c7e5f13741e17a7d80582ae21fcc Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 28 Jun 2011 15:11:23 +0000 Subject: hwmon: LM95245 driver A hwmon driver for the National Semiconductor LM95245 dual temperature sensors chip. Signed-off-by: Alexander Stein Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm95245 | 33 +++ drivers/hwmon/Kconfig | 9 + drivers/hwmon/Makefile | 1 + drivers/hwmon/lm95245.c | 543 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 586 insertions(+) create mode 100644 Documentation/hwmon/lm95245 create mode 100644 drivers/hwmon/lm95245.c (limited to 'Documentation') diff --git a/Documentation/hwmon/lm95245 b/Documentation/hwmon/lm95245 new file mode 100644 index 000000000000..cbd8aeab7124 --- /dev/null +++ b/Documentation/hwmon/lm95245 @@ -0,0 +1,33 @@ +Kernel driver lm95245 +================== + +Supported chips: + * National Semiconductor LM95245 + Addresses scanned: I2C 0x18, 0x19, 0x29, 0x4c, 0x4d + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/mpf/LM/LM95245.html + + +Author: Alexander Stein + +Description +----------- + +The LM95245 is an 11-bit digital temperature sensor with a 2-wire System +Management Bus (SMBus) interface and TruTherm technology that can monitor +the temperature of a remote diode as well as its own temperature. +The LM95245 can be used to very accurately monitor the temperature of +external devices such as microprocessors. + +All temperature values are given in millidegrees Celsius. Local temperature +is given within a range of -127 to +127.875 degrees. Remote temperatures are +given within a range of -127 to +255 degrees. Resolution depends on +temperature input and range. + +Each sensor has its own critical limit, but the hysteresis is common to all +two channels. + +The lm95245 driver can change its update interval to a fixed set of values. +It will round up to the next selectable interval. See the datasheet for exact +values. Reading sensor values more often will do no harm, but will return +'old' values. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1396290a31a4..166889b326ff 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -702,6 +702,15 @@ config SENSORS_LM95241 This driver can also be built as a module. If so, the module will be called lm95241. +config SENSORS_LM95245 + tristate "National Semiconductor LM95245 sensor chip" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for LM95245 sensor chip. + + This driver can also be built as a module. If so, the module + will be called lm95245. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 1b7dc770aa60..9a4473993845 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o +obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c new file mode 100644 index 000000000000..dce9e68241e6 --- /dev/null +++ b/drivers/hwmon/lm95245.c @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2011 Alexander Stein + * + * The LM95245 is a sensor chip made by National Semiconductors. + * It reports up to two temperatures (its own plus an external one). + * Complete datasheet can be obtained from National's website at: + * http://www.national.com/ds.cgi/LM/LM95245.pdf + * + * This driver is based on lm95241.c + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVNAME "lm95245" + +static const unsigned short normal_i2c[] = { + 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; + +/* LM95245 registers */ +/* general registers */ +#define LM95245_REG_RW_CONFIG1 0x03 +#define LM95245_REG_RW_CONVERS_RATE 0x04 +#define LM95245_REG_W_ONE_SHOT 0x0F + +/* diode configuration */ +#define LM95245_REG_RW_CONFIG2 0xBF +#define LM95245_REG_RW_REMOTE_OFFH 0x11 +#define LM95245_REG_RW_REMOTE_OFFL 0x12 + +/* status registers */ +#define LM95245_REG_R_STATUS1 0x02 +#define LM95245_REG_R_STATUS2 0x33 + +/* limit registers */ +#define LM95245_REG_RW_REMOTE_OS_LIMIT 0x07 +#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT 0x20 +#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT 0x19 +#define LM95245_REG_RW_COMMON_HYSTERESIS 0x21 + +/* temperature signed */ +#define LM95245_REG_R_LOCAL_TEMPH_S 0x00 +#define LM95245_REG_R_LOCAL_TEMPL_S 0x30 +#define LM95245_REG_R_REMOTE_TEMPH_S 0x01 +#define LM95245_REG_R_REMOTE_TEMPL_S 0x10 +/* temperature unsigned */ +#define LM95245_REG_R_REMOTE_TEMPH_U 0x31 +#define LM95245_REG_R_REMOTE_TEMPL_U 0x32 + +/* id registers */ +#define LM95245_REG_R_MAN_ID 0xFE +#define LM95245_REG_R_CHIP_ID 0xFF + +/* LM95245 specific bitfields */ +#define CFG_STOP 0x40 +#define CFG_REMOTE_TCRIT_MASK 0x10 +#define CFG_REMOTE_OS_MASK 0x08 +#define CFG_LOCAL_TCRIT_MASK 0x04 +#define CFG_LOCAL_OS_MASK 0x02 + +#define CFG2_OS_A0 0x40 +#define CFG2_DIODE_FAULT_OS 0x20 +#define CFG2_DIODE_FAULT_TCRIT 0x10 +#define CFG2_REMOTE_TT 0x08 +#define CFG2_REMOTE_FILTER_DIS 0x00 +#define CFG2_REMOTE_FILTER_EN 0x06 + +/* conversation rate in ms */ +#define RATE_CR0063 0x00 +#define RATE_CR0364 0x01 +#define RATE_CR1000 0x02 +#define RATE_CR2500 0x03 + +#define STATUS1_DIODE_FAULT 0x04 +#define STATUS1_RTCRIT 0x02 +#define STATUS1_LOC 0x01 + +#define MANUFACTURER_ID 0x01 +#define DEFAULT_REVISION 0xB3 + +static const u8 lm95245_reg_address[] = { + LM95245_REG_R_LOCAL_TEMPH_S, + LM95245_REG_R_LOCAL_TEMPL_S, + LM95245_REG_R_REMOTE_TEMPH_S, + LM95245_REG_R_REMOTE_TEMPL_S, + LM95245_REG_R_REMOTE_TEMPH_U, + LM95245_REG_R_REMOTE_TEMPL_U, + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT, + LM95245_REG_RW_REMOTE_TCRIT_LIMIT, + LM95245_REG_RW_COMMON_HYSTERESIS, + LM95245_REG_R_STATUS1, +}; + +/* Client data (each client gets its own) */ +struct lm95245_data { + struct device *hwmon_dev; + struct mutex update_lock; + unsigned long last_updated; /* in jiffies */ + unsigned long interval; /* in msecs */ + bool valid; /* zero until following fields are valid */ + /* registers values */ + u8 regs[ARRAY_SIZE(lm95245_reg_address)]; + u8 config1, config2; +}; + +/* Conversions */ +static int temp_from_reg_unsigned(u8 val_h, u8 val_l) +{ + return val_h * 1000 + val_l * 1000 / 256; +} + +static int temp_from_reg_signed(u8 val_h, u8 val_l) +{ + if (val_h & 0x80) + return (val_h - 0x100) * 1000; + return temp_from_reg_unsigned(val_h, val_l); +} + +static struct lm95245_data *lm95245_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->interval)) || !data->valid) { + int i; + + dev_dbg(&client->dev, "Updating lm95245 data.\n"); + for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) + data->regs[i] + = i2c_smbus_read_byte_data(client, + lm95245_reg_address[i]); + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static unsigned long lm95245_read_conversion_rate(struct i2c_client *client) +{ + int rate; + unsigned long interval; + + rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE); + + switch (rate) { + case RATE_CR0063: + interval = 63; + break; + case RATE_CR0364: + interval = 364; + break; + case RATE_CR1000: + interval = 1000; + break; + case RATE_CR2500: + default: + interval = 2500; + break; + } + + return interval; +} + +static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, + unsigned long interval) +{ + int rate; + + if (interval <= 63) { + interval = 63; + rate = RATE_CR0063; + } else if (interval <= 364) { + interval = 364; + rate = RATE_CR0364; + } else if (interval <= 1000) { + interval = 1000; + rate = RATE_CR1000; + } else { + interval = 2500; + rate = RATE_CR2500; + } + + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate); + + return interval; +} + +/* Sysfs stuff */ +static ssize_t show_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int temp; + int index = to_sensor_dev_attr(attr)->index; + + /* + * Index 0 (Local temp) is always signed + * Index 2 (Remote temp) has both signed and unsigned data + * use signed calculation for remote if signed bit is set + */ + if (index == 0 || data->regs[index] & 0x80) + temp = temp_from_reg_signed(data->regs[index], + data->regs[index + 1]); + else + temp = temp_from_reg_unsigned(data->regs[index + 2], + data->regs[index + 3]); + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp); +} + +static ssize_t show_limit(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + data->regs[index] * 1000); +} + +static ssize_t set_limit(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + val /= 1000; + + val = SENSORS_LIMIT(val, 0, (index == 6 ? 127 : 255)); + + mutex_lock(&data->update_lock); + + data->valid = 0; + + i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + val /= 1000; + + val = SENSORS_LIMIT(val, 0, 31); + + mutex_lock(&data->update_lock); + + data->valid = 0; + + /* shared crit hysteresis */ + i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, + val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE - 1, + data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val == 1) + data->config2 |= CFG2_REMOTE_TT; + else + data->config2 &= ~CFG2_REMOTE_TT; + + data->valid = 0; + + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2, + data->config2); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + !!(data->regs[9] & index)); +} + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + + return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); +} + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->interval = lm95245_set_conversion_rate(client, val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, + set_limit, 6); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit, + set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + STATUS1_LOC); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, + set_limit, 7); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit, + set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + STATUS1_RTCRIT); +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, + set_type, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, + STATUS1_DIODE_FAULT); + +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, + set_interval); + +static struct attribute *lm95245_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static const struct attribute_group lm95245_group = { + .attrs = lm95245_attributes, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm95245_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID) + != MANUFACTURER_ID + || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID) + != DEFAULT_REVISION) + return -ENODEV; + + strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); + return 0; +} + +static void lm95245_init_client(struct i2c_client *client) +{ + struct lm95245_data *data = i2c_get_clientdata(client); + + data->valid = 0; + data->interval = lm95245_read_conversion_rate(client); + + data->config1 = i2c_smbus_read_byte_data(client, + LM95245_REG_RW_CONFIG1); + data->config2 = i2c_smbus_read_byte_data(client, + LM95245_REG_RW_CONFIG2); + + if (data->config1 & CFG_STOP) { + /* Clear the standby bit */ + data->config1 &= ~CFG_STOP; + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1, + data->config1); + } +} + +static int lm95245_probe(struct i2c_client *new_client, + const struct i2c_device_id *id) +{ + struct lm95245_data *data; + int err; + + data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(new_client, data); + mutex_init(&data->update_lock); + + /* Initialize the LM95245 chip */ + lm95245_init_client(new_client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm95245_group); +exit_free: + kfree(data); +exit: + return err; +} + +static int lm95245_remove(struct i2c_client *client) +{ + struct lm95245_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm95245_group); + + kfree(data); + return 0; +} + +/* Driver data (common to all clients) */ +static const struct i2c_device_id lm95245_id[] = { + { DEVNAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm95245_id); + +static struct i2c_driver lm95245_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DEVNAME, + }, + .probe = lm95245_probe, + .remove = lm95245_remove, + .id_table = lm95245_id, + .detect = lm95245_detect, + .address_list = normal_i2c, +}; + +static int __init sensors_lm95245_init(void) +{ + return i2c_add_driver(&lm95245_driver); +} + +static void __exit sensors_lm95245_exit(void) +{ + i2c_del_driver(&lm95245_driver); +} + +MODULE_AUTHOR("Alexander Stein "); +MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm95245_init); +module_exit(sensors_lm95245_exit); -- cgit v1.2.3 From c814a4c7c4aad795835583344353963a0a673eb0 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 12 Jul 2011 07:07:16 -0400 Subject: hwmon: (coretemp) Add core/pkg threshold support to Coretemp This patch adds the core and pkg support to coretemp. These thresholds can be configured via the sysfs interfaces tempX_max and tempX_max_hyst. An interrupt is generated when CPU temperature reaches or crosses above tempX_max OR drops below tempX_max_hyst. This patch is based on the documentation in IA Manual vol 3A, that can be downloaded from here: http://download.intel.com/design/processor/manuals/253668.pdf Signed-off-by: Durgadoss R Signed-off-by: Guenter Roeck --- Documentation/hwmon/coretemp | 7 ++ drivers/hwmon/coretemp.c | 177 ++++++++++++++++++++++++++++++++----------- 2 files changed, 139 insertions(+), 45 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index f85e913a3401..fa8776ab9b18 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -35,6 +35,13 @@ the Out-Of-Spec bit. Following table summarizes the exported sysfs files: All Sysfs entries are named with their core_id (represented here by 'X'). tempX_input - Core temperature (in millidegrees Celsius). tempX_max - All cooling devices should be turned on (on Core2). + Initialized with IA32_THERM_INTERRUPT. When the CPU + temperature reaches this temperature, an interrupt is + generated and tempX_max_alarm is set. +tempX_max_hyst - If the CPU temperature falls below than temperature, + an interrupt is generated and tempX_max_alarm is reset. +tempX_max_alarm - Set if the temperature reaches or exceeds tempX_max. + Reset if the temperature drops to or below tempX_max_hyst. tempX_crit - Maximum junction temperature (in millidegrees Celsius). tempX_crit_alarm - Set when Out-of-spec bit is set, never clears. Correct CPU operation is no longer guaranteed. diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 0070d5476dd0..59d83e83da7f 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -44,7 +44,9 @@ #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ #define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ -#define MAX_ATTRS 5 /* Maximum no of per-core attrs */ +#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ +#define MAX_THRESH_ATTRS 3 /* Maximum no of Threshold attrs */ +#define TOTAL_ATTRS (MAX_CORE_ATTRS + MAX_THRESH_ATTRS) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) #ifdef CONFIG_SMP @@ -67,6 +69,9 @@ * This value is passed as "id" field to rdmsr/wrmsr functions. * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, * from where the temperature values should be read. + * @intrpt_reg: One of IA32_THERM_INTERRUPT or IA32_PACKAGE_THERM_INTERRUPT, + * from where the thresholds are read. + * @attr_size: Total number of pre-core attrs displayed in the sysfs. * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. * Otherwise, temp_data holds coretemp data. * @valid: If this is 1, the current temperature is valid. @@ -74,15 +79,18 @@ struct temp_data { int temp; int ttarget; + int tmin; int tjmax; unsigned long last_updated; unsigned int cpu; u32 cpu_core_id; u32 status_reg; + u32 intrpt_reg; + int attr_size; bool is_pkg_data; bool valid; - struct sensor_device_attribute sd_attrs[MAX_ATTRS]; - char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH]; + struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; + char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; struct mutex update_lock; }; @@ -135,6 +143,19 @@ static ssize_t show_crit_alarm(struct device *dev, return sprintf(buf, "%d\n", (eax >> 5) & 1); } +static ssize_t show_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + + return sprintf(buf, "%d\n", !!(eax & THERM_STATUS_THRESHOLD1)); +} + static ssize_t show_tjmax(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -153,6 +174,83 @@ static ssize_t show_ttarget(struct device *dev, return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); } +static ssize_t store_ttarget(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct platform_data *pdata = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct temp_data *tdata = pdata->core_data[attr->index]; + u32 eax, edx; + unsigned long val; + int diff; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + /* + * THERM_MASK_THRESHOLD1 is 7 bits wide. Values are entered in terms + * of milli degree celsius. Hence don't accept val > (127 * 1000) + */ + if (val > tdata->tjmax || val > 127000) + return -EINVAL; + + diff = (tdata->tjmax - val) / 1000; + + mutex_lock(&tdata->update_lock); + rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); + eax = (eax & ~THERM_MASK_THRESHOLD1) | + (diff << THERM_SHIFT_THRESHOLD1); + wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); + tdata->ttarget = val; + mutex_unlock(&tdata->update_lock); + + return count; +} + +static ssize_t show_tmin(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tmin); +} + +static ssize_t store_tmin(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct platform_data *pdata = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct temp_data *tdata = pdata->core_data[attr->index]; + u32 eax, edx; + unsigned long val; + int diff; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + /* + * THERM_MASK_THRESHOLD0 is 7 bits wide. Values are entered in terms + * of milli degree celsius. Hence don't accept val > (127 * 1000) + */ + if (val > tdata->tjmax || val > 127000) + return -EINVAL; + + diff = (tdata->tjmax - val) / 1000; + + mutex_lock(&tdata->update_lock); + rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); + eax = (eax & ~THERM_MASK_THRESHOLD0) | + (diff << THERM_SHIFT_THRESHOLD0); + wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); + tdata->tmin = val; + mutex_unlock(&tdata->update_lock); + + return count; +} + static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -344,23 +442,31 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, int attr_no) { int err, i; - static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev, + static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { - show_label, show_crit_alarm, show_ttarget, - show_temp, show_tjmax }; - static const char *names[MAX_ATTRS] = { + show_label, show_crit_alarm, show_temp, show_tjmax, + show_max_alarm, show_ttarget, show_tmin }; + static ssize_t (*rw_ptr[TOTAL_ATTRS]) (struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) = { NULL, NULL, NULL, NULL, NULL, + store_ttarget, store_tmin }; + static const char *names[TOTAL_ATTRS] = { "temp%d_label", "temp%d_crit_alarm", - "temp%d_max", "temp%d_input", - "temp%d_crit" }; + "temp%d_input", "temp%d_crit", + "temp%d_max_alarm", "temp%d_max", + "temp%d_max_hyst" }; - for (i = 0; i < MAX_ATTRS; i++) { + for (i = 0; i < tdata->attr_size; i++) { snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], attr_no); sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; + if (rw_ptr[i]) { + tdata->sd_attrs[i].dev_attr.attr.mode |= S_IWUSR; + tdata->sd_attrs[i].dev_attr.store = rw_ptr[i]; + } tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; - tdata->sd_attrs[i].dev_attr.store = NULL; tdata->sd_attrs[i].index = attr_no; err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); if (err) @@ -374,38 +480,6 @@ exit_free: return err; } -static void update_ttarget(__u8 cpu_model, struct temp_data *tdata, - struct device *dev) -{ - int err; - u32 eax, edx; - - /* - * Initialize ttarget value. Eventually this will be - * initialized with the value from MSR_IA32_THERM_INTERRUPT - * register. If IA32_TEMPERATURE_TARGET is supported, this - * value will be over written below. - * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT - */ - tdata->ttarget = tdata->tjmax - 20000; - - /* - * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists - * on older CPUs but not in this register, - * Atoms don't have it either. - */ - if (cpu_model > 0xe && cpu_model != 0x1c) { - err = rdmsr_safe_on_cpu(tdata->cpu, - MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); - if (err) { - dev_warn(dev, - "Unable to read IA32_TEMPERATURE_TARGET MSR\n"); - } else { - tdata->ttarget = tdata->tjmax - - ((eax >> 8) & 0xff) * 1000; - } - } -} static int __devinit chk_ucode_version(struct platform_device *pdev) { @@ -464,9 +538,12 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS; + tdata->intrpt_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_INTERRUPT : + MSR_IA32_THERM_INTERRUPT; tdata->is_pkg_data = pkg_flag; tdata->cpu = cpu; tdata->cpu_core_id = TO_CORE_ID(cpu); + tdata->attr_size = MAX_CORE_ATTRS; mutex_init(&tdata->update_lock); return tdata; } @@ -516,7 +593,17 @@ static int create_core_data(struct platform_data *pdata, else tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); - update_ttarget(c->x86_model, tdata, &pdev->dev); + /* + * Test if we can access the intrpt register. If so, increase the + * 'size' enough to have ttarget/tmin/max_alarm interfaces. + * Initialize ttarget with bits 16:22 of MSR_IA32_THERM_INTERRUPT + */ + err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx); + if (!err) { + tdata->attr_size += MAX_THRESH_ATTRS; + tdata->ttarget = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; + } + pdata->core_data[attr_no] = tdata; /* Create sysfs interfaces */ @@ -553,7 +640,7 @@ static void coretemp_remove_core(struct platform_data *pdata, struct temp_data *tdata = pdata->core_data[indx]; /* Remove the sysfs attributes */ - for (i = 0; i < MAX_ATTRS; i++) + for (i = 0; i < tdata->attr_size; i++) device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); kfree(pdata->core_data[indx]); -- cgit v1.2.3 From e0455e380b5bdf8dff7c8e29423d2446d9f59040 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jun 2011 15:13:44 -0700 Subject: hwmon: (pmbus) Add ADP4000, NCP4200 and NCP4208 to list of supported devices Add ADP4000, NCP4200 and NCP4208 to the list of devices supported by the generic PMBus driver, and add device IDs to enable explicit instantiation. Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson Acked-by: Jean Delvare --- Documentation/hwmon/pmbus | 7 +++++++ drivers/hwmon/pmbus/Kconfig | 4 ++-- drivers/hwmon/pmbus/pmbus.c | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/pmbus b/Documentation/hwmon/pmbus index 5e462fc7f99b..c36c1c1a62bb 100644 --- a/Documentation/hwmon/pmbus +++ b/Documentation/hwmon/pmbus @@ -13,6 +13,13 @@ Supported chips: Prefix: 'ltc2978' Addresses scanned: - Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf + * ON Semiconductor ADP4000, NCP4200, NCP4208 + Prefixes: 'adp4000', 'ncp4200', 'ncp4208' + Addresses scanned: - + Datasheets: + http://www.onsemi.com/pub_link/Collateral/ADP4000-D.PDF + http://www.onsemi.com/pub_link/Collateral/NCP4200-D.PDF + http://www.onsemi.com/pub_link/Collateral/JUNE%202009-%20REV.%200.PDF * Generic PMBus devices Prefix: 'pmbus' Addresses scanned: - diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 794129f48594..0a822a45085d 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -19,8 +19,8 @@ config SENSORS_PMBUS default y help If you say yes here you get hardware monitoring support for generic - PMBus devices, including but not limited to BMR450, BMR451, BMR453, - BMR454, and LTC2978. + PMBus devices, including but not limited to ADP4000, BMR450, BMR451, + BMR453, BMR454, LTC2978, NCP4200, and NCP4208. This driver can also be built as a module. If so, the module will be called pmbus. diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 4d8e31bcd7a3..73de9f1f3194 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -200,11 +200,14 @@ static int pmbus_remove(struct i2c_client *client) * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { + {"adp4000", 1}, {"bmr450", 1}, {"bmr451", 1}, {"bmr453", 1}, {"bmr454", 1}, {"ltc2978", 8}, + {"ncp4200", 1}, + {"ncp4208", 1}, {"pmbus", 0}, {} }; -- cgit v1.2.3 From 0084e9faabbc1e228fda9c8a78ef1ea8ee97a9ae Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 9 Jul 2011 10:32:11 -0700 Subject: hwmon: Add new attributes to sysfs ABI Add the following attributes to the sysfs ABI. in[0-*]_average in[0-*]_lowest in[0-*]_highest in[0-*]_reset_history in_reset_history curr[1-*]_average curr[1-*]_lowest curr[1-*]_highest curr[1-*]_reset_history curr_reset_history Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson --- Documentation/hwmon/sysfs-interface | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 8f63c244f1aa..a4aa8f600e09 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -139,6 +139,29 @@ in[0-*]_input Voltage input value. thumb: drivers should report the voltage values at the "pins" of the chip. +in[0-*]_average + Average voltage + Unit: millivolt + RO + +in[0-*]_lowest + Historical minimum voltage + Unit: millivolt + RO + +in[0-*]_highest + Historical maximum voltage + Unit: millivolt + RO + +in[0-*]_reset_history + Reset inX_lowest and inX_highest + WO + +in_reset_history + Reset inX_lowest and inX_highest for all sensors + WO + in[0-*]_label Suggested voltage channel label. Text string Should only be created if the driver has hints about what @@ -407,6 +430,29 @@ curr[1-*]_input Current input value Unit: milliampere RO +curr[1-*]_average + Average current use + Unit: milliampere + RO + +curr[1-*]_lowest + Historical minimum current + Unit: milliampere + RO + +curr[1-*]_highest + Historical maximum current + Unit: milliampere + RO + +curr[1-*]_reset_history + Reset currX_lowest and currX_highest + WO + +curr_reset_history + Reset currX_lowest and currX_highest for all sensors + WO + Also see the Alarms section for status flags associated with currents. ********* -- cgit v1.2.3 From c576e30cd0c9814b3b45772d46924d796f538dee Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 9 Jul 2011 11:17:33 -0700 Subject: hwmon: (adm1275) Add support for peak attributes Add support for voltage and current peak (historic maximum) attributes. Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson --- Documentation/hwmon/adm1275 | 8 ++++-- drivers/hwmon/pmbus/adm1275.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/adm1275 b/Documentation/hwmon/adm1275 index 6a3a6476cf20..097b3ccc4be7 100644 --- a/Documentation/hwmon/adm1275 +++ b/Documentation/hwmon/adm1275 @@ -43,8 +43,8 @@ Documentation/hwmon/pmbus for details. Sysfs entries ------------- -The following attributes are supported. Limits are read-write; all other -attributes are read-only. +The following attributes are supported. Limits are read-write, history reset +attributes are write-only, all other attributes are read-only. in1_label "vin1" or "vout1" depending on chip variant and configuration. @@ -53,8 +53,12 @@ in1_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register. in1_max Maximum voltage. From VOUT_OV_WARN_LIMIT register. in1_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status. in1_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status. +in1_highest Historical maximum voltage. +in1_reset_history Write any value to reset history. curr1_label "iout1" curr1_input Measured current. From READ_IOUT register. curr1_max Maximum current. From IOUT_OC_WARN_LIMIT register. curr1_max_alarm Current high alarm. From IOUT_OC_WARN_LIMIT register. +curr1_highest Historical maximum current. +curr1_reset_history Write any value to reset history. diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 71770ffbdaf6..c936e2782309 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -23,11 +23,68 @@ #include #include "pmbus.h" +#define ADM1275_PEAK_IOUT 0xd0 +#define ADM1275_PEAK_VIN 0xd1 +#define ADM1275_PEAK_VOUT 0xd2 #define ADM1275_PMON_CONFIG 0xd4 #define ADM1275_VIN_VOUT_SELECT (1 << 6) #define ADM1275_VRANGE (1 << 5) +static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page) + return -EINVAL; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT); + break; + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + if (page) + return -EINVAL; + + switch (reg) { + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static int adm1275_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -58,6 +115,9 @@ static int adm1275_probe(struct i2c_client *client, info->R[PSC_CURRENT_OUT] = -1; info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + info->read_word_data = adm1275_read_word_data; + info->write_word_data = adm1275_write_word_data; + if (config & ADM1275_VRANGE) { info->m[PSC_VOLTAGE_IN] = 19199; info->b[PSC_VOLTAGE_IN] = 0; -- cgit v1.2.3 From 8ebed854506301f4f67e75c73cae967d4a45ab4d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 9 Jul 2011 13:10:19 -0700 Subject: hwmon: (max16064) Add support for peak attributes Add support for voltage and temperature peak (historic maximum) attributes. Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson --- Documentation/hwmon/max16064 | 4 ++++ drivers/hwmon/pmbus/max16064.c | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/max16064 b/Documentation/hwmon/max16064 index 41728999e142..f6e8bcbfaccf 100644 --- a/Documentation/hwmon/max16064 +++ b/Documentation/hwmon/max16064 @@ -50,6 +50,8 @@ in[1-4]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status. in[1-4]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status. in[1-4]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status. in[1-4]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status. +in[1-4]_highest Historical maximum voltage. +in[1-4]_reset_history Write any value to reset history. temp1_input Measured temperature. From READ_TEMPERATURE_1 register. temp1_max Maximum temperature. From OT_WARN_LIMIT register. @@ -60,3 +62,5 @@ temp1_max_alarm Chip temperature high alarm. Set by comparing temp1_crit_alarm Chip temperature critical high alarm. Set by comparing READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT status is set. +temp1_highest Historical maximum temperature. +temp1_reset_history Write any value to reset history. diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index 78e20bca53a9..e50b296e8db4 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -25,6 +25,55 @@ #include #include "pmbus.h" +#define MAX16064_MFR_VOUT_PEAK 0xd4 +#define MAX16064_MFR_TEMPERATURE_PEAK 0xd6 + +static int max16064_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX16064_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + MAX16064_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max16064_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX16064_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX16064_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static struct pmbus_driver_info max16064_info = { .pages = 4, .format[PSC_VOLTAGE_IN] = direct, @@ -44,6 +93,8 @@ static struct pmbus_driver_info max16064_info = { .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .read_word_data = max16064_read_word_data, + .write_word_data = max16064_write_word_data, }; static int max16064_probe(struct i2c_client *client, -- cgit v1.2.3 From 70e94b276c21638a0a37908f6696a6b122944dea Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 9 Jul 2011 13:11:44 -0700 Subject: hwmon: (max8688) Add support for peak attributes Add support for voltage, current, and temperature peak (historic maximum) attributes. Signed-off-by: Guenter Roeck --- Documentation/hwmon/max8688 | 6 +++++ drivers/hwmon/pmbus/max8688.c | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/max8688 b/Documentation/hwmon/max8688 index 0ddd3a412030..71ed10a3c94e 100644 --- a/Documentation/hwmon/max8688 +++ b/Documentation/hwmon/max8688 @@ -50,6 +50,8 @@ in1_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status. in1_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status. in1_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status. in1_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status. +in1_highest Historical maximum voltage. +in1_reset_history Write any value to reset history. curr1_label "iout1" curr1_input Measured current. From READ_IOUT register. @@ -57,6 +59,8 @@ curr1_max Maximum current. From IOUT_OC_WARN_LIMIT register. curr1_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register. curr1_max_alarm Current high alarm. From IOUT_OC_WARN_LIMIT register. curr1_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status. +curr1_highest Historical maximum current. +curr1_reset_history Write any value to reset history. temp1_input Measured temperature. From READ_TEMPERATURE_1 register. temp1_max Maximum temperature. From OT_WARN_LIMIT register. @@ -67,3 +71,5 @@ temp1_max_alarm Chip temperature high alarm. Set by comparing temp1_crit_alarm Chip temperature critical high alarm. Set by comparing READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT status is set. +temp1_highest Historical maximum temperature. +temp1_reset_history Write any value to reset history. diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index ddc8a64c2ba5..c3e72f1a3cfb 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -25,6 +25,9 @@ #include #include "pmbus.h" +#define MAX8688_MFR_VOUT_PEAK 0xd4 +#define MAX8688_MFR_IOUT_PEAK 0xd5 +#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6 #define MAX8688_MFG_STATUS 0xd8 #define MAX8688_STATUS_OC_FAULT (1 << 4) @@ -37,6 +40,62 @@ #define MAX8688_STATUS_OT_FAULT (1 << 13) #define MAX8688_STATUS_OT_WARNING (1 << 14) +static int max8688_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page) + return -EINVAL; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, 0, + MAX8688_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max8688_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK, + 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK, + 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, 0, + MAX8688_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) { int ret = 0; @@ -111,6 +170,8 @@ static struct pmbus_driver_info max8688_info = { | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP, .read_byte_data = max8688_read_byte_data, + .read_word_data = max8688_read_word_data, + .write_word_data = max8688_write_word_data, }; static int max8688_probe(struct i2c_client *client, -- cgit v1.2.3 From 98591dbe633eace43a20ffa2907861fbef97237b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 9 Jul 2011 13:17:43 -0700 Subject: hwmon: (max34440) Add support for peak attributes Add support for voltage, current, and temperature peak (historic maximum) attributes. Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson --- Documentation/hwmon/max34440 | 6 ++++ drivers/hwmon/pmbus/max34440.c | 63 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/max34440 b/Documentation/hwmon/max34440 index 6c525dd07d59..8ab51536a1eb 100644 --- a/Documentation/hwmon/max34440 +++ b/Documentation/hwmon/max34440 @@ -56,6 +56,8 @@ in[1-6]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status. in[1-6]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status. in[1-6]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status. in[1-6]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status. +in[1-6]_highest Historical maximum voltage. +in[1-6]_reset_history Write any value to reset history. curr[1-6]_label "iout[1-6]". curr[1-6]_input Measured current. From READ_IOUT register. @@ -63,6 +65,8 @@ curr[1-6]_max Maximum current. From IOUT_OC_WARN_LIMIT register. curr[1-6]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register. curr[1-6]_max_alarm Current high alarm. From IOUT_OC_WARNING status. curr[1-6]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status. +curr[1-6]_highest Historical maximum current. +curr[1-6]_reset_history Write any value to reset history. in6 and curr6 attributes only exist for MAX34440. @@ -75,5 +79,7 @@ temp[1-8]_max Maximum temperature. From OT_WARN_LIMIT register. temp[1-8]_crit Critical high temperature. From OT_FAULT_LIMIT register. temp[1-8]_max_alarm Temperature high alarm. temp[1-8]_crit_alarm Temperature critical high alarm. +temp[1-8]_highest Historical maximum temperature. +temp[1-8]_reset_history Write any value to reset history. temp7 and temp8 attributes only exist for MAX34440. diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 2e30046a116e..fda621d2e458 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -27,11 +27,70 @@ enum chips { max34440, max34441 }; +#define MAX34440_MFR_VOUT_PEAK 0xd4 +#define MAX34440_MFR_IOUT_PEAK 0xd5 +#define MAX34440_MFR_TEMPERATURE_PEAK 0xd6 + #define MAX34440_STATUS_OC_WARN (1 << 0) #define MAX34440_STATUS_OC_FAULT (1 << 1) #define MAX34440_STATUS_OT_FAULT (1 << 5) #define MAX34440_STATUS_OT_WARN (1 << 6) +static int max34440_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max34440_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_IOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) { int ret; @@ -109,6 +168,8 @@ static struct pmbus_driver_info max34440_info[] = { .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, }, [max34441] = { .pages = 12, @@ -150,6 +211,8 @@ static struct pmbus_driver_info max34440_info[] = { .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, }, }; -- cgit v1.2.3 From 03e9bd8dbcee60c2e22fd54f9f28f0d32da218c3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 8 Jul 2011 10:43:57 -0700 Subject: hwmon: (pmbus) Add client driver for LM25066, LM5064, and LM5066 PMBus client driver supporting National Semiconductor LM25066, LM5064, and LM5066. Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson --- Documentation/hwmon/lm25066 | 90 +++++++++++ drivers/hwmon/pmbus/Kconfig | 10 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/lm25066.c | 340 +++++++++++++++++++++++++++++++++++++++ drivers/hwmon/pmbus/pmbus.h | 1 + drivers/hwmon/pmbus/pmbus_core.c | 3 +- 6 files changed, 444 insertions(+), 1 deletion(-) create mode 100644 Documentation/hwmon/lm25066 create mode 100644 drivers/hwmon/pmbus/lm25066.c (limited to 'Documentation') diff --git a/Documentation/hwmon/lm25066 b/Documentation/hwmon/lm25066 new file mode 100644 index 000000000000..a21db81c4591 --- /dev/null +++ b/Documentation/hwmon/lm25066 @@ -0,0 +1,90 @@ +Kernel driver max8688 +===================== + +Supported chips: + * National Semiconductor LM25066 + Prefix: 'lm25066' + Addresses scanned: - + Datasheets: + http://www.national.com/pf/LM/LM25066.html + http://www.national.com/pf/LM/LM25066A.html + * National Semiconductor LM5064 + Prefix: 'lm5064' + Addresses scanned: - + Datasheet: + http://www.national.com/pf/LM/LM5064.html + * National Semiconductor LM5066 + Prefix: 'lm5066' + Addresses scanned: - + Datasheet: + http://www.national.com/pf/LM/LM5066.html + +Author: Guenter Roeck + + +Description +----------- + +This driver supports hardware montoring for National Semiconductor LM25066, +LM5064, and LM5064 Power Management, Monitoring, Control, and Protection ICs. + +The driver is a client driver to the core PMBus driver. Please see +Documentation/hwmon/pmbus for details on PMBus client drivers. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices for +details. + + +Platform data support +--------------------- + +The driver supports standard PMBus driver platform data. + + +Sysfs entries +------------- + +The following attributes are supported. Limits are read-write; all other +attributes are read-only. + +in1_label "vin" +in1_input Measured input voltage. +in1_average Average measured input voltage. +in1_min Minimum input voltage. +in1_max Maximum input voltage. +in1_min_alarm Input voltage low alarm. +in1_max_alarm Input voltage high alarm. + +in2_label "vout1" +in2_input Measured output voltage. +in2_average Average measured output voltage. +in2_min Minimum output voltage. +in2_min_alarm Output voltage low alarm. + +in3_label "vout2" +in3_input Measured voltage on vaux pin + +curr1_label "iin" +curr1_input Measured input current. +curr1_average Average measured input current. +curr1_max Maximum input current. +curr1_max_alarm Input current high alarm. + +power1_label "pin" +power1_input Measured input power. +power1_average Average measured input power. +power1_max Maximum input power limit. +power1_alarm Input power alarm +power1_input_highest Historical maximum power. +power1_reset_history Write any value to reset maximum power history. + +temp1_input Measured temperature. +temp1_max Maximum temperature. +temp1_crit Critical high temperature. +temp1_max_alarm Chip temperature high alarm. +temp1_crit_alarm Chip temperature critical high alarm. diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 0a822a45085d..c9237b9dcff2 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -35,6 +35,16 @@ config SENSORS_ADM1275 This driver can also be built as a module. If so, the module will be called adm1275. +config SENSORS_LM25066 + tristate "National Semiconductor LM25066 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for National + Semiconductor LM25066, LM5064, and LM5066. + + This driver can also be built as a module. If so, the module will + be called lm25066. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 0178f81829d0..623eedb1ed9a 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o +obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c new file mode 100644 index 000000000000..d4bc114572de --- /dev/null +++ b/drivers/hwmon/pmbus/lm25066.c @@ -0,0 +1,340 @@ +/* + * Hardware monitoring driver for LM25066 / LM5064 / LM5066 + * + * Copyright (c) 2011 Ericsson AB. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include "pmbus.h" + +enum chips { lm25066, lm5064, lm5066 }; + +#define LM25066_READ_VAUX 0xd0 +#define LM25066_MFR_READ_IIN 0xd1 +#define LM25066_MFR_READ_PIN 0xd2 +#define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3 +#define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4 +#define LM25066_READ_PIN_PEAK 0xd5 +#define LM25066_CLEAR_PIN_PEAK 0xd6 +#define LM25066_DEVICE_SETUP 0xd9 +#define LM25066_READ_AVG_VIN 0xdc +#define LM25066_READ_AVG_VOUT 0xdd +#define LM25066_READ_AVG_IIN 0xde +#define LM25066_READ_AVG_PIN 0xdf + +#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ + +struct lm25066_data { + int id; + struct pmbus_driver_info info; +}; + +#define to_lm25066_data(x) container_of(x, struct lm25066_data, info) + +static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); + int ret; + + if (page > 1) + return -EINVAL; + + /* Map READ_VAUX into READ_VOUT register on page 1 */ + if (page == 1) { + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, 0, + LM25066_READ_VAUX); + if (ret < 0) + break; + /* Adjust returned value to match VOUT coefficients */ + switch (data->id) { + case lm25066: + /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); + break; + case lm5064: + /* VOUT: 4.53 mV VAUX: 700 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 70, 453); + break; + case lm5066: + /* VOUT: 2.18 mV VAUX: 725 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 725, 2180); + break; + } + break; + default: + /* No other valid registers on page 1 */ + ret = -EINVAL; + break; + } + goto done; + } + + switch (reg) { + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); + break; + case PMBUS_READ_PIN: + ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25066_MFR_IIN_OC_WARN_LIMIT); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25066_MFR_PIN_OP_WARN_LIMIT); + break; + case PMBUS_VIRT_READ_VIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); + break; + case PMBUS_VIRT_READ_VOUT_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); + break; + case PMBUS_VIRT_READ_IIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); + break; + case PMBUS_VIRT_READ_PIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); + break; + case PMBUS_VIRT_READ_PIN_MAX: + ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } +done: + return ret; +} + +static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + if (page > 1) + return -EINVAL; + + switch (reg) { + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_write_word_data(client, 0, + LM25066_MFR_IIN_OC_WARN_LIMIT, + word); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + ret = pmbus_write_word_data(client, 0, + LM25066_MFR_PIN_OP_WARN_LIMIT, + word); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lm25066_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int config; + int ret; + struct lm25066_data *data; + struct pmbus_driver_info *info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -ENODEV; + + data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); + if (config < 0) { + ret = config; + goto err_mem; + } + + data->id = id->driver_data; + info = &data->info; + + info->pages = 2; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_POWER] = direct; + + info->m[PSC_TEMPERATURE] = 16; + info->b[PSC_TEMPERATURE] = 0; + info->R[PSC_TEMPERATURE] = 0; + + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT; + + info->read_word_data = lm25066_read_word_data; + info->write_word_data = lm25066_write_word_data; + + switch (id->driver_data) { + case lm25066: + info->m[PSC_VOLTAGE_IN] = 22070; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 22070; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = 6852; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 369; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } else { + info->m[PSC_CURRENT_IN] = 13661; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 736; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } + break; + case lm5064: + info->m[PSC_VOLTAGE_IN] = 22075; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 22075; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = 6713; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 3619; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } else { + info->m[PSC_CURRENT_IN] = 13426; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 7238; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } + break; + case lm5066: + info->m[PSC_VOLTAGE_IN] = 4587; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 4587; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = 10753; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 1204; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } else { + info->m[PSC_CURRENT_IN] = 5405; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 605; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } + break; + default: + ret = -ENODEV; + goto err_mem; + } + + ret = pmbus_do_probe(client, id, info); + if (ret) + goto err_mem; + return 0; + +err_mem: + kfree(data); + return ret; +} + +static int lm25066_remove(struct i2c_client *client) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); + int ret; + + ret = pmbus_do_remove(client); + kfree(data); + return ret; +} + +static const struct i2c_device_id lm25066_id[] = { + {"lm25066", lm25066}, + {"lm5064", lm5064}, + {"lm5066", lm5066}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lm25066_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver lm25066_driver = { + .driver = { + .name = "lm25066", + }, + .probe = lm25066_probe, + .remove = lm25066_remove, + .id_table = lm25066_id, +}; + +static int __init lm25066_init(void) +{ + return i2c_add_driver(&lm25066_driver); +} + +static void __exit lm25066_exit(void) +{ + i2c_del_driver(&lm25066_driver); +} + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); +MODULE_LICENSE("GPL"); +module_init(lm25066_init); +module_exit(lm25066_exit); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 9973d265b28f..0808d986d75b 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -340,6 +340,7 @@ int pmbus_set_page(struct i2c_client *client, u8 page); int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); void pmbus_clear_faults(struct i2c_client *client); bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 9baf119b64db..5c1b6cf31701 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -168,7 +168,7 @@ int pmbus_set_page(struct i2c_client *client, u8 page) } EXPORT_SYMBOL_GPL(pmbus_set_page); -static int pmbus_write_byte(struct i2c_client *client, int page, u8 value) +int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { int rv; @@ -180,6 +180,7 @@ static int pmbus_write_byte(struct i2c_client *client, int page, u8 value) return i2c_smbus_write_byte(client, value); } +EXPORT_SYMBOL_GPL(pmbus_write_byte); int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word) { -- cgit v1.2.3