From 11c119986f2700e9daca0d795fbe84a988939655 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 15 Oct 2014 13:55:08 -0500 Subject: hwmon: (pmbus) add helpers for byte write and read modify write Add two helper functions: * pmbus_write_byte_data = paged byte write * pmbus_update_byte_data = paged byte read/modify/write Signed-off-by: Alan Tull Cc: Mark Brown Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 4 ++++ drivers/hwmon/pmbus/pmbus_core.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index fa9beb3eb60c..3ae79a7d1b00 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -375,6 +375,10 @@ 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); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + u8 value); +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, 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 291d11fe93e7..d6c3701eb7f9 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -253,6 +253,37 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) } EXPORT_SYMBOL_GPL(pmbus_read_byte_data); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_write_byte_data(client, reg, value); +} +EXPORT_SYMBOL_GPL(pmbus_write_byte_data); + +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value) +{ + unsigned int tmp; + int rv; + + rv = pmbus_read_byte_data(client, page, reg); + if (rv < 0) + return rv; + + tmp = (rv & ~mask) | (value & mask); + + if (tmp != rv) + rv = pmbus_write_byte_data(client, page, reg, tmp); + + return rv; +} +EXPORT_SYMBOL_GPL(pmbus_update_byte_data); + /* * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if * a device specific mapping function exists and calls it if necessary. -- cgit v1.2.3 From ddbb4db4ced1ba784fcd3500179a7291b6c5d7b7 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 15 Oct 2014 13:55:09 -0500 Subject: hwmon: (pmbus) Add regulator support Add support for simple on/off control of each channel. To add regulator support, the pmbus part driver needs to add regulator_desc information and number of regulators to its pmbus_driver_info struct. regulator_desc can be declared using default macro for a regulator (PMBUS_REGULATOR) that is in pmbus.h The regulator_init_data can be initialized from either platform data or the device tree. Signed-off-by: Alan Tull Reviewed-by: Mark Brown Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 26 ++++++++++++ drivers/hwmon/pmbus/pmbus_core.c | 87 ++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pmbus.h | 4 ++ 3 files changed, 117 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 3ae79a7d1b00..89a23ff836e7 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -19,6 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + #ifndef PMBUS_H #define PMBUS_H @@ -185,6 +187,11 @@ #define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) #define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) +/* + * OPERATION + */ +#define PB_OPERATION_CONTROL_ON (1<<7) + /* * CAPABILITY */ @@ -365,8 +372,27 @@ struct pmbus_driver_info { */ int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info); + + /* Regulator functionality, if supported by this chip driver. */ + int num_regulators; + const struct regulator_desc *reg_desc; }; +/* Regulator ops */ + +extern struct regulator_ops pmbus_regulator_ops; + +/* Macro for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR(_name, _id) \ + [_id] = { \ + .name = (_name # _id), \ + .id = (_id), \ + .of_match = of_match_ptr(_name # _id), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pmbus_regulator_ops, \ + .owner = THIS_MODULE, \ + } + /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index d6c3701eb7f9..f2e47c7dd808 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "pmbus.h" /* @@ -1758,6 +1760,84 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return 0; } +#if IS_ENABLED(CONFIG_REGULATOR) +static int pmbus_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + u8 page = rdev_get_id(rdev); + int ret; + + ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + if (ret < 0) + return ret; + + return !!(ret & PB_OPERATION_CONTROL_ON); +} + +static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + u8 page = rdev_get_id(rdev); + + return pmbus_update_byte_data(client, page, PMBUS_OPERATION, + PB_OPERATION_CONTROL_ON, + enable ? PB_OPERATION_CONTROL_ON : 0); +} + +static int pmbus_regulator_enable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 1); +} + +static int pmbus_regulator_disable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 0); +} + +struct regulator_ops pmbus_regulator_ops = { + .enable = pmbus_regulator_enable, + .disable = pmbus_regulator_disable, + .is_enabled = pmbus_regulator_is_enabled, +}; +EXPORT_SYMBOL_GPL(pmbus_regulator_ops); + +static int pmbus_regulator_register(struct pmbus_data *data) +{ + struct device *dev = data->dev; + const struct pmbus_driver_info *info = data->info; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct regulator_dev *rdev; + int i; + + for (i = 0; i < info->num_regulators; i++) { + struct regulator_config config = { }; + + config.dev = dev; + config.driver_data = data; + + if (pdata && pdata->reg_init_data) + config.init_data = &pdata->reg_init_data[i]; + + rdev = devm_regulator_register(dev, &info->reg_desc[i], + &config); + if (IS_ERR(rdev)) { + dev_err(dev, "Failed to register %s regulator\n", + info->reg_desc[i].name); + return PTR_ERR(rdev); + } + } + + return 0; +} +#else +static int pmbus_regulator_register(struct pmbus_data *data) +{ + return 0; +} +#endif + int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info) { @@ -1812,8 +1892,15 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, dev_err(dev, "Failed to register hwmon device\n"); goto out_kfree; } + + ret = pmbus_regulator_register(data); + if (ret) + goto out_unregister; + return 0; +out_unregister: + hwmon_device_unregister(data->hwmon_dev); out_kfree: kfree(data->group.attrs); return ret; diff --git a/include/linux/i2c/pmbus.h b/include/linux/i2c/pmbus.h index 69280db02c41..ee3c2aba2a8e 100644 --- a/include/linux/i2c/pmbus.h +++ b/include/linux/i2c/pmbus.h @@ -40,6 +40,10 @@ struct pmbus_platform_data { u32 flags; /* Device specific flags */ + + /* regulator support */ + int num_regulators; + struct regulator_init_data *reg_init_data; }; #endif /* _PMBUS_H_ */ -- cgit v1.2.3 From 77aa3585805920d871e09b9a60d9635e51b03d21 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 15 Oct 2014 13:55:10 -0500 Subject: hwmon: (ltc2978) Add regulator support Add simple on/off regulator support for ltc2978 and other pmbus parts supported by the ltc2978 driver. Signed-off-by: Alan Tull Cc: Guenter Roeck Cc: Mark Brown Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 11 +++++++++-- drivers/hwmon/pmbus/ltc2978.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 6e1e4935fc62..a674cd83a4e2 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -47,15 +47,22 @@ config SENSORS_LM25066 be called lm25066. config SENSORS_LTC2978 - tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883" + tristate "Linear Technologies LTC2978 and compatibles" default n help If you say yes here you get hardware monitoring support for Linear - Technology LTC2974, LTC2978, LTC3880, and LTC3883. + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. This driver can also be built as a module. If so, the module will be called ltc2978. +config SENSORS_LTC2978_REGULATOR + boolean "Regulator support for LTC2978 and compatibles" + depends on SENSORS_LTC2978 && REGULATOR + help + If you say yes here you get regulator support for Linear + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index e24ed521051a..0835050ec245 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "pmbus.h" enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; @@ -374,6 +375,19 @@ static const struct i2c_device_id ltc2978_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); +#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) +static const struct regulator_desc ltc2978_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), + PMBUS_REGULATOR("vout", 1), + PMBUS_REGULATOR("vout", 2), + PMBUS_REGULATOR("vout", 3), + PMBUS_REGULATOR("vout", 4), + PMBUS_REGULATOR("vout", 5), + PMBUS_REGULATOR("vout", 6), + PMBUS_REGULATOR("vout", 7), +}; +#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */ + static int ltc2978_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -487,13 +501,36 @@ static int ltc2978_probe(struct i2c_client *client, default: return -ENODEV; } + +#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) + info->num_regulators = info->pages; + info->reg_desc = ltc2978_reg_desc; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { + dev_err(&client->dev, "num_regulators too large!"); + info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + } +#endif + return pmbus_do_probe(client, id, info); } -/* This is the driver that will be inserted */ +#ifdef CONFIG_OF +static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,ltc2974" }, + { .compatible = "lltc,ltc2977" }, + { .compatible = "lltc,ltc2978" }, + { .compatible = "lltc,ltc3880" }, + { .compatible = "lltc,ltc3883" }, + { .compatible = "lltc,ltm4676" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2978_of_match); +#endif + static struct i2c_driver ltc2978_driver = { .driver = { .name = "ltc2978", + .of_match_table = of_match_ptr(ltc2978_of_match), }, .probe = ltc2978_probe, .remove = pmbus_do_remove, -- cgit v1.2.3 From 61bb53bcbdd86e0c25fbf517c48a38f66c6fc0bc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 27 Sep 2014 08:31:12 -0700 Subject: hwmon: (iio_hwmon) Add support for humidity sensors The iio subsystem supports humidity sensors, so it makes sense to support it in the iio-hwmon bridge as well. Cc: Jonathan Cameron Acked-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/iio_hwmon.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 14c82daab019..980175628563 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -63,7 +63,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) struct iio_hwmon_state *st; struct sensor_device_attribute *a; int ret, i; - int in_i = 1, temp_i = 1, curr_i = 1; + int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; enum iio_chan_type type; struct iio_channel *channels; const char *name = "iio_hwmon"; @@ -123,6 +123,11 @@ static int iio_hwmon_probe(struct platform_device *pdev) "curr%d_input", curr_i++); break; + case IIO_HUMIDITYRELATIVE: + a->dev_attr.attr.name = kasprintf(GFP_KERNEL, + "humidity%d_input", + humidity_i++); + break; default: ret = -EINVAL; goto error_release_channels; -- cgit v1.2.3 From 8de303bae48bf907138e11042268834b1227e1e7 Mon Sep 17 00:00:00 2001 From: Neelesh Gupta Date: Wed, 5 Nov 2014 16:45:14 +0530 Subject: hwmon: (ibmpowernv) Use platform 'id_table' to probe the device The current driver probe() function assumes the sensor device to be always present and gets executed every time if the driver is loaded, but the appropriate hardware could not be present. So, move the platform device creation as part of platform init code and use the 'id_table' to check if the device is present or not. Signed-off-by: Neelesh Gupta Acked-by: Michael Ellerman Signed-off-by: Guenter Roeck --- arch/powerpc/platforms/powernv/opal-sensor.c | 20 +++++++++ drivers/hwmon/ibmpowernv.c | 67 ++++++++-------------------- 2 files changed, 39 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/powernv/opal-sensor.c b/arch/powerpc/platforms/powernv/opal-sensor.c index 10271ad1fac4..4ab67ef7abc9 100644 --- a/arch/powerpc/platforms/powernv/opal-sensor.c +++ b/arch/powerpc/platforms/powernv/opal-sensor.c @@ -20,7 +20,9 @@ #include #include +#include #include +#include static DEFINE_MUTEX(opal_sensor_mutex); @@ -64,3 +66,21 @@ out: return ret; } EXPORT_SYMBOL_GPL(opal_get_sensor_data); + +static __init int opal_sensor_init(void) +{ + struct platform_device *pdev; + struct device_node *sensor; + + sensor = of_find_node_by_path("/ibm,opal/sensors"); + if (!sensor) { + pr_err("Opal node 'sensors' not found\n"); + return -ENODEV; + } + + pdev = of_platform_device_create(sensor, "opal-sensor", NULL); + of_node_put(sensor); + + return PTR_ERR_OR_ZERO(pdev); +} +machine_subsys_initcall(powernv, opal_sensor_init); diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 6a30eeea94be..c7577b8f17a8 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -74,9 +74,6 @@ struct platform_data { u32 sensors_count; /* Total count of sensors from each group */ }; -/* Platform device representing all the ibmpowernv sensors */ -static struct platform_device *pdevice; - static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -99,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%u\n", x); } -static int __init get_sensor_index_attr(const char *name, u32 *index, +static int get_sensor_index_attr(const char *name, u32 *index, char *attr) { char *hash_pos = strchr(name, '#'); @@ -136,7 +133,7 @@ static int __init get_sensor_index_attr(const char *name, u32 *index, * which need to be mapped as fan2_input, temp1_max respectively before * populating them inside hwmon device class. */ -static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, +static int create_hwmon_attr_name(struct device *dev, enum sensors type, const char *node_name, char *hwmon_attr_name) { @@ -172,7 +169,7 @@ static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, return 0; } -static int __init populate_attr_groups(struct platform_device *pdev) +static int populate_attr_groups(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; @@ -180,11 +177,6 @@ static int __init populate_attr_groups(struct platform_device *pdev) enum sensors type; opal = of_find_node_by_path("/ibm,opal/sensors"); - if (!opal) { - dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n"); - return -ENODEV; - } - for_each_child_of_node(opal, np) { if (np->name == NULL) continue; @@ -221,7 +213,7 @@ static int __init populate_attr_groups(struct platform_device *pdev) * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max * etc.. */ -static int __init create_device_attrs(struct platform_device *pdev) +static int create_device_attrs(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; @@ -280,7 +272,7 @@ exit_put_node: return err; } -static int __init ibmpowernv_probe(struct platform_device *pdev) +static int ibmpowernv_probe(struct platform_device *pdev) { struct platform_data *pdata; struct device *hwmon_dev; @@ -309,52 +301,31 @@ static int __init ibmpowernv_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } +static const struct platform_device_id opal_sensor_driver_ids[] = { + { + .name = "opal-sensor", + }, + { } +}; +MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); + static struct platform_driver ibmpowernv_driver = { - .driver = { - .owner = THIS_MODULE, - .name = DRVNAME, + .probe = ibmpowernv_probe, + .id_table = opal_sensor_driver_ids, + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, }, }; static int __init ibmpowernv_init(void) { - int err; - - pdevice = platform_device_alloc(DRVNAME, 0); - if (!pdevice) { - pr_err("Device allocation failed\n"); - err = -ENOMEM; - goto exit; - } - - err = platform_device_add(pdevice); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_put; - } - - err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe); - if (err) { - if (err != -ENODEV) - pr_err("Platform driver probe failed (%d)\n", err); - - goto exit_device_del; - } - - return 0; - -exit_device_del: - platform_device_del(pdevice); -exit_device_put: - platform_device_put(pdevice); -exit: - return err; + return platform_driver_register(&ibmpowernv_driver); } static void __exit ibmpowernv_exit(void) { platform_driver_unregister(&ibmpowernv_driver); - platform_device_unregister(pdevice); } MODULE_AUTHOR("Neelesh Gupta "); -- cgit v1.2.3 From 3bdec670df30d515b51ab41452a9cf86a956f7e8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 12 Nov 2014 16:36:47 +0800 Subject: hwmon: (ibmpowernv) Convert to module_platform_driver Use module_platform_driver to simplify the code a bit. Signed-off-by: Axel Lin Signed-off-by: Guenter Roeck --- drivers/hwmon/ibmpowernv.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index c7577b8f17a8..7c2c7be182f2 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -318,19 +318,8 @@ static struct platform_driver ibmpowernv_driver = { }, }; -static int __init ibmpowernv_init(void) -{ - return platform_driver_register(&ibmpowernv_driver); -} - -static void __exit ibmpowernv_exit(void) -{ - platform_driver_unregister(&ibmpowernv_driver); -} +module_platform_driver(ibmpowernv_driver); MODULE_AUTHOR("Neelesh Gupta "); MODULE_DESCRIPTION("IBM POWERNV platform sensors"); MODULE_LICENSE("GPL"); - -module_init(ibmpowernv_init); -module_exit(ibmpowernv_exit); -- cgit v1.2.3 From 3434f37835804331dd505722cd0010d708305837 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 29 Jun 2014 19:38:45 -0700 Subject: hwmon: Driver for Nuvoton NCT7802Y NCT7802Y is an I2C based hardware monitoring chip from Nuvoton. Signed-off-by: Guenter Roeck --- Documentation/hwmon/nct7802 | 32 ++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/nct7802.c | 860 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 904 insertions(+) create mode 100644 Documentation/hwmon/nct7802 create mode 100644 drivers/hwmon/nct7802.c (limited to 'drivers') diff --git a/Documentation/hwmon/nct7802 b/Documentation/hwmon/nct7802 new file mode 100644 index 000000000000..2e00f5e344bc --- /dev/null +++ b/Documentation/hwmon/nct7802 @@ -0,0 +1,32 @@ +Kernel driver nct7802 +===================== + +Supported chips: + * Nuvoton NCT7802Y + Prefix: 'nct7802' + Addresses scanned: I2C 0x28..0x2f + Datasheet: Available from Nuvoton web site + +Authors: + Guenter Roeck + +Description +----------- + +This driver implements support for the Nuvoton NCT7802Y hardware monitoring +chip. NCT7802Y supports 6 temperature sensors, 5 voltage sensors, and 3 fan +speed sensors. + +The chip also supports intelligent fan speed control. This functionality is +not currently supported by the driver. + +Tested Boards and BIOS Versions +------------------------------- + +The driver has been reported to work with the following boards and +BIOS versions. + +Board BIOS version +--------------------------------------------------------------- +Kontron COMe-bSC2 CHR2E934.001.GGO +Kontron COMe-bIP2 CCR2E212 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5286d7ce1f9e..f893e0ff0081 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1123,6 +1123,17 @@ config SENSORS_NCT6775 This driver can also be built as a module. If so, the module will be called nct6775. +config SENSORS_NCT7802 + tristate "Nuvoton NCT7802Y" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7802Y + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called nct7802. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c90a7611efaa..67280643bcf0 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o +obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c new file mode 100644 index 000000000000..ec5678289e4a --- /dev/null +++ b/drivers/hwmon/nct7802.c @@ -0,0 +1,860 @@ +/* + * nct7802 - Driver for Nuvoton NCT7802Y + * + * Copyright (C) 2014 Guenter Roeck + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "nct7802" + +static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e }; + +static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = { + { 0x40, 0x00, 0x42, 0x44, 0x46 }, + { 0x3f, 0x00, 0x41, 0x43, 0x45 }, +}; + +static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 }; + +static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { + { 0, 0, 4, 0, 4 }, + { 2, 0, 6, 2, 6 }, +}; + +#define REG_BANK 0x00 +#define REG_TEMP_LSB 0x05 +#define REG_TEMP_PECI_LSB 0x08 +#define REG_VOLTAGE_LOW 0x0f +#define REG_FANCOUNT_LOW 0x13 +#define REG_START 0x21 +#define REG_MODE 0x22 +#define REG_PECI_ENABLE 0x23 +#define REG_FAN_ENABLE 0x24 +#define REG_VMON_ENABLE 0x25 +#define REG_VENDOR_ID 0xfd +#define REG_CHIP_ID 0xfe +#define REG_VERSION_ID 0xff + +/* + * Data structures and manipulation thereof + */ + +struct nct7802_data { + struct regmap *regmap; + struct mutex access_lock; /* for multi-byte read and write operations */ +}; + +static int nct7802_read_temp(struct nct7802_data *data, + u8 reg_temp, u8 reg_temp_low, int *temp) +{ + unsigned int t1, t2 = 0; + int err; + + *temp = 0; + + mutex_lock(&data->access_lock); + err = regmap_read(data->regmap, reg_temp, &t1); + if (err < 0) + goto abort; + t1 <<= 8; + if (reg_temp_low) { /* 11 bit data */ + err = regmap_read(data->regmap, reg_temp_low, &t2); + if (err < 0) + goto abort; + } + t1 |= t2 & 0xe0; + *temp = (s16)t1 / 32 * 125; +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2); + if (ret < 0) + goto abort; + ret = (f1 << 5) | (f2 >> 3); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume fan is stopped */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, + u8 reg_fan_high) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan_low, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, reg_fan_high, &f2); + if (ret < 0) + goto abort; + ret = f1 | ((f2 & 0xf8) << 5); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume no limit */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, + u8 reg_fan_high, unsigned int limit) +{ + int err; + + if (limit) + limit = DIV_ROUND_CLOSEST(1350000U, limit); + else + limit = 0x1fff; + limit = clamp_val(limit, 0, 0x1fff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, reg_fan_low, limit & 0xff); + if (err < 0) + goto abort; + + err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 }; + +static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) +{ + unsigned int v1, v2; + int ret; + + mutex_lock(&data->access_lock); + if (index == 0) { /* voltage */ + ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); + if (ret < 0) + goto abort; + ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr]; + } else { /* limit */ + int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + + ret = regmap_read(data->regmap, + REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], + &v2); + if (ret < 0) + goto abort; + ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr]; + } +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, + unsigned int voltage) +{ + int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + int err; + + voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); + voltage = clamp_val(voltage, 0, 0x3ff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, + REG_VOLTAGE_LIMIT_LSB[index - 1][nr], + voltage & 0xff); + if (err < 0) + goto abort; + + err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], + 0x0300 >> shift, (voltage & 0x0300) >> shift); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int voltage; + + voltage = nct7802_read_voltage(data, sattr->nr, sattr->index); + if (voltage < 0) + return voltage; + + return sprintf(buf, "%d\n", voltage); +} + +static ssize_t store_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int index = sattr->index; + int nr = sattr->nr; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7802_write_voltage(data, nr, index, val); + return err ? : count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int err, temp; + + err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t store_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int nr = sattr->nr; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + + err = regmap_write(data->regmap, nr, val & 0xff); + return err ? : count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7802_read_fan(data, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7802_read_fan_min(data, sattr->nr, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val); + return err ? : count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int bit = sattr->index; + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, sattr->nr, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", !!(val & (1 << bit))); +} + +static ssize_t +show_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + err = regmap_read(data->regmap, sattr->nr, ®val); + if (err) + return err; + + return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index))); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index, + val ? 1 << sattr->index : 0); + return err ? : count; +} + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x31, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x30, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3a, 0); + +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x33, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x32, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3b, 0); + +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x35, 0); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x34, 0); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3c, 0); + +static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 0x04, 0); +static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x37, 0); +static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x36, 0); +static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3d, 0); + +static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 0x06, + REG_TEMP_PECI_LSB); +static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x39, 0); +static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x38, 0); +static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3e, 0); + +static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 0x07, + REG_TEMP_PECI_LSB); + +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 0); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 1); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 2); +static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 3); +static SENSOR_DEVICE_ATTR_2(temp5_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 1); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 2); +static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 3); +static SENSOR_DEVICE_ATTR_2(temp5_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 1); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 2); +static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 3); +static SENSOR_DEVICE_ATTR_2(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, 0x17, 0); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, 0x17, 1); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, 0x17, 2); + +static SENSOR_DEVICE_ATTR_2(temp1_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 0); +static SENSOR_DEVICE_ATTR_2(temp2_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 1); +static SENSOR_DEVICE_ATTR_2(temp3_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 2); +static SENSOR_DEVICE_ATTR_2(temp4_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 3); +static SENSOR_DEVICE_ATTR_2(temp5_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 4); +static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 5); + +static struct attribute *nct7802_temp_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp1_beep.dev_attr.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */ + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + + &sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */ + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, + + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */ + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_beep.dev_attr.attr, + + &sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */ + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_beep.dev_attr.attr, + + &sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */ + &sensor_dev_attr_temp6_beep.dev_attr.attr, + + NULL +}; + +static umode_t nct7802_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index < 9 && + (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */ + return 0; + if (index >= 9 && index < 18 && + (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */ + return 0; + if (index >= 18 && index < 27 && (reg & 0x30) != 0x10) /* RD3 */ + return 0; + if (index >= 27 && index < 35) /* local */ + return attr->mode; + + err = regmap_read(data->regmap, REG_PECI_ENABLE, ®); + if (err < 0) + return 0; + + if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */ + return 0; + + if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */ + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_temp_group = { + .attrs = nct7802_temp_attrs, + .is_visible = nct7802_temp_is_visible, +}; + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, store_in, + 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, store_in, + 0, 2); +static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 3); +static SENSOR_DEVICE_ATTR_2(in0_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 3); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, store_in, + 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, store_in, + 2, 2); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 0); +static SENSOR_DEVICE_ATTR_2(in2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 0); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, store_in, + 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, store_in, + 3, 2); +static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 1); +static SENSOR_DEVICE_ATTR_2(in3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 1); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, store_in, + 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, store_in, + 4, 2); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 2); +static SENSOR_DEVICE_ATTR_2(in4_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 2); + +static struct attribute *nct7802_in_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in0_beep.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */ + + &sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */ + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in2_beep.dev_attr.attr, + + &sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */ + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in3_beep.dev_attr.attr, + + &sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */ + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in4_beep.dev_attr.attr, + + NULL, +}; + +static umode_t nct7802_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + if (index < 6) /* VCC, VCORE */ + return attr->mode; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */ + return 0; + if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */ + return 0; + if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */ + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_in_group = { + .attrs = nct7802_in_attrs, + .is_visible = nct7802_in_is_visible, +}; + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0x10); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x49, 0x4c); +static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 0); +static SENSOR_DEVICE_ATTR_2(fan1_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 0x11); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x4a, 0x4d); +static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 1); +static SENSOR_DEVICE_ATTR_2(fan2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 0x12); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x4b, 0x4e); +static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2); +static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 2); + +static struct attribute *nct7802_fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_beep.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_beep.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_beep.dev_attr.attr, + + NULL +}; + +static umode_t nct7802_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + int fan = index / 4; /* 4 attributes per fan */ + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_FAN_ENABLE, ®); + if (err < 0 || !(reg & (1 << fan))) + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_fan_group = { + .attrs = nct7802_fan_attrs, + .is_visible = nct7802_fan_is_visible, +}; + +static const struct attribute_group *nct7802_groups[] = { + &nct7802_temp_group, + &nct7802_in_group, + &nct7802_fan_group, + NULL +}; + +static int nct7802_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int reg; + + /* + * Chip identification registers are only available in bank 0, + * so only attempt chip detection if bank 0 is selected + */ + reg = i2c_smbus_read_byte_data(client, REG_BANK); + if (reg != 0x00) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID); + if (reg != 0x50) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID); + if (reg != 0xc3) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID); + if (reg < 0 || (reg & 0xf0) != 0x20) + return -ENODEV; + + /* Also validate lower bits of voltage and temperature registers */ + reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB); + if (reg < 0 || (reg & 0x1f)) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB); + if (reg < 0 || (reg & 0x3f)) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW); + if (reg < 0 || (reg & 0x3f)) + return -ENODEV; + + strlcpy(info->type, "nct7802", I2C_NAME_SIZE); + return 0; +} + +static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg != REG_BANK && reg <= 0x20; +} + +static struct regmap_config nct7802_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = nct7802_regmap_is_volatile, +}; + +static int nct7802_init_chip(struct nct7802_data *data) +{ + int err; + + /* Enable ADC */ + err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01); + if (err) + return err; + + /* Enable local temperature sensor */ + err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40); + if (err) + return err; + + /* Enable Vcore and VCC voltage monitoring */ + return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03); +} + +static int nct7802_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct nct7802_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + mutex_init(&data->access_lock); + + ret = nct7802_init_chip(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + nct7802_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const unsigned short nct7802_address_list[] = { + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; + +static const struct i2c_device_id nct7802_idtable[] = { + { "nct7802", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct7802_idtable); + +static struct i2c_driver nct7802_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .detect = nct7802_detect, + .probe = nct7802_probe, + .id_table = nct7802_idtable, + .address_list = nct7802_address_list, +}; + +module_i2c_driver(nct7802_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 799fc6021430243592ea8390aa4865713a12fd5f Mon Sep 17 00:00:00 2001 From: Michael Thalmeier Date: Tue, 18 Nov 2014 17:08:04 +0100 Subject: hwmon: (lm75) Add support for the NXP LM75B It is basically a faster lm75 with improved (11 bit) resolution. Signed-off-by: Michael Thalmeier Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm75 | 5 +++++ drivers/hwmon/lm75.c | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75 index c6a5ff1b4641..67691a0aa41d 100644 --- a/Documentation/hwmon/lm75 +++ b/Documentation/hwmon/lm75 @@ -53,6 +53,11 @@ Supported chips: http://www.ti.com/product/tmp75 http://www.ti.com/product/tmp175 http://www.ti.com/product/tmp275 + * NXP LM75B + Prefix: 'lm75b' + Addresses scanned: none + Datasheet: Publicly available at the NXP website + http://www.nxp.com/documents/data_sheet/LM75B.pdf Author: Frodo Looijaard diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index d16dbb33a531..f58439b817b5 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -44,6 +44,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ g751, lm75, lm75a, + lm75b, max6625, max6626, mcp980x, @@ -233,6 +234,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->resolution = 9; data->sample_time = HZ / 2; break; + case lm75b: + data->resolution = 11; + data->sample_time = HZ / 4; + break; case max6625: data->resolution = 9; data->sample_time = HZ / 4; @@ -322,6 +327,7 @@ static const struct i2c_device_id lm75_ids[] = { { "g751", g751, }, { "lm75", lm75, }, { "lm75a", lm75a, }, + { "lm75b", lm75b, }, { "max6625", max6625, }, { "max6626", max6626, }, { "mcp980x", mcp980x, }, -- cgit v1.2.3 From 8aefb93f09bf4464f6da8ee071edcede9517d4bf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Nov 2014 09:50:04 -0800 Subject: hwmon: (nct6775) Add support for NCT6792D NCT6792D is similar to NCT6791D. Only beep control and temperature monitoring registers are different. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- Documentation/hwmon/nct6775 | 4 ++++ drivers/hwmon/Kconfig | 4 ++-- drivers/hwmon/nct6775.c | 47 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 43 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 index 345d11a2b526..f0dd3d2fec96 100644 --- a/Documentation/hwmon/nct6775 +++ b/Documentation/hwmon/nct6775 @@ -28,6 +28,10 @@ Supported chips: Prefix: 'nct6791' Addresses scanned: ISA address retrieved from Super I/O registers Datasheet: Available from Nuvoton upon request + * Nuvoton NCT6792D + Prefix: 'nct6792' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: Available from Nuvoton upon request Authors: Guenter Roeck diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f893e0ff0081..fcca19b53bd2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1117,8 +1117,8 @@ config SENSORS_NCT6775 help If you say yes here you get support for the hardware monitoring functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, - NCT6791D and compatible Super-I/O chips. This driver replaces the - w83627ehf driver for NCT6775F and NCT6776F. + NCT6791D, NCT6792D and compatible Super-I/O chips. This driver + replaces the w83627ehf driver for NCT6775F and NCT6776F. This driver can also be built as a module. If so, the module will be called nct6775. diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 504cbddbdd90..c0dd307001c9 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -38,6 +38,7 @@ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 + * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -61,7 +62,7 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -70,6 +71,7 @@ static const char * const nct6775_device_names[] = { "nct6776", "nct6779", "nct6791", + "nct6792", }; static unsigned short force_id; @@ -100,6 +102,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 #define SIO_NCT6791_ID 0xc800 +#define SIO_NCT6792_ID 0xc910 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -529,6 +532,12 @@ static const s8 NCT6791_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ +/* NCT6792 specific data */ + +static const u16 NCT6792_REG_TEMP_MON[] = { + 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d }; +static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = { + 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; /* NCT6102D/NCT6106D specific data */ @@ -1043,13 +1052,14 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) reg == 0x73 || reg == 0x75 || reg == 0x77; case nct6779: case nct6791: + case nct6792: return reg == 0x150 || reg == 0x153 || reg == 0x155 || ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || reg == 0x63a || reg == 0x63c || reg == 0x63e || reg == 0x640 || reg == 0x642 || reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || - reg == 0x7b; + reg == 0x7b || reg == 0x7d; } return false; } @@ -1391,6 +1401,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6106: case nct6779: case nct6791: + case nct6792: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -2790,6 +2801,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6106: case nct6779: case nct6791: + case nct6792: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -3202,7 +3214,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = false; pwm5pin = false; pwm6pin = false; - } else { /* NCT6779D or NCT6791D */ + } else { /* NCT6779D, NCT6791D, or NCT6792D */ regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); @@ -3215,7 +3227,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792) { regval = superio_inb(sioreg, 0x2d); fan6pin = (regval & (1 << 1)); pwm6pin = (regval & (1 << 0)); @@ -3588,6 +3600,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: + case nct6792: data->in_num = 15; data->pwm_num = 6; data->auto_pwm_num = 4; @@ -3650,12 +3663,20 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL; data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6791_REG_ALARM; - data->REG_BEEP = NCT6776_REG_BEEP; + if (data->kind == nct6791) + data->REG_BEEP = NCT6776_REG_BEEP; + else + data->REG_BEEP = NCT6792_REG_BEEP; reg_temp = NCT6779_REG_TEMP; - reg_temp_mon = NCT6779_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); - num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + if (data->kind == nct6791) { + reg_temp_mon = NCT6779_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + } else { + reg_temp_mon = NCT6792_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON); + } reg_temp_over = NCT6779_REG_TEMP_OVER; reg_temp_hyst = NCT6779_REG_TEMP_HYST; reg_temp_config = NCT6779_REG_TEMP_CONFIG; @@ -3854,6 +3875,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6106: case nct6779: case nct6791: + case nct6792: break; } @@ -3885,6 +3907,7 @@ static int nct6775_probe(struct platform_device *pdev) tmp |= 0x3e; break; case nct6791: + case nct6792: tmp |= 0x7e; break; } @@ -3972,7 +3995,7 @@ static int nct6775_resume(struct device *dev) mutex_lock(&data->update_lock); data->bank = 0xff; /* Force initial bank selection */ - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792) { err = superio_enter(data->sioreg); if (err) goto abort; @@ -4052,6 +4075,7 @@ static const char * const nct6775_sio_names[] __initconst = { "NCT6776D/F", "NCT6779D", "NCT6791D", + "NCT6792D", }; /* nct6775_find() looks for a '627 in the Super-I/O config space */ @@ -4086,6 +4110,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6791_ID: sio_data->kind = nct6791; break; + case SIO_NCT6792_ID: + sio_data->kind = nct6792; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4111,7 +4138,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } - if (sio_data->kind == nct6791) + if (sio_data->kind == nct6791 || sio_data->kind == nct6792) nct6791_enable_io_mapping(sioaddr); superio_exit(sioaddr); @@ -4221,7 +4248,7 @@ static void __exit sensors_nct6775_exit(void) } MODULE_AUTHOR("Guenter Roeck "); -MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D driver"); +MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver"); MODULE_LICENSE("GPL"); module_init(sensors_nct6775_init); -- cgit v1.2.3 From 9cd892bcbe32fed1b78fe1b742d887ed2be1791d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Nov 2014 10:00:06 -0800 Subject: hwmon: (nct6775) Add blank lines after declarations checkpatch complains about WARNING: Missing a blank line after declarations Add missing blank lines. Also reorder variables length-wise where appropriate if a function header is touched anyway. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index c0dd307001c9..dc0df57200cd 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -1073,6 +1073,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) { u8 bank = reg >> 8; + if (data->bank != bank) { outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); outb_p(bank, data->addr + DATA_REG_OFFSET); @@ -1310,6 +1311,7 @@ static void nct6775_update_pwm(struct device *dev) if (!data->target_speed_tolerance[i] || data->pwm_enable[i] == speed_cruise) { u8 t = fanmodecfg & 0x0f; + if (data->REG_TOLERANCE_H) { t |= (nct6775_read_value(data, data->REG_TOLERANCE_H[i]) & 0x70) >> 1; @@ -1484,6 +1486,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->alarms = 0; for (i = 0; i < NUM_REG_ALARM; i++) { u8 alarm; + if (!data->REG_ALARM[i]) continue; alarm = nct6775_read_value(data, data->REG_ALARM[i]); @@ -1493,6 +1496,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->beeps = 0; for (i = 0; i < NUM_REG_BEEP; i++) { u8 beep; + if (!data->REG_BEEP[i]) continue; beep = nct6775_read_value(data, data->REG_BEEP[i]); @@ -1515,8 +1519,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); } @@ -1526,10 +1531,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, { struct nct6775_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; unsigned long val; - int err = kstrtoul(buf, 10, &val); + int err; + + err = kstrtoul(buf, 10, &val); if (err < 0) return err; mutex_lock(&data->update_lock); @@ -1546,6 +1553,7 @@ show_alarm(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = data->ALARM_BITS[sattr->index]; + return sprintf(buf, "%u\n", (unsigned int)((data->alarms >> nr) & 0x01)); } @@ -1581,6 +1589,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_alarms); if (nr >= 0) { int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE]; + alarm = (data->alarms >> bit) & 0x01; } return sprintf(buf, "%u\n", alarm); @@ -1606,8 +1615,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf, int nr = data->BEEP_BITS[sattr->index]; int regindex = nr >> 3; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1640,6 +1650,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_beeps); if (nr >= 0) { int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + beep = (data->beeps >> bit) & 0x01; } return sprintf(buf, "%u\n", beep); @@ -1653,8 +1664,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, struct nct6775_data *data = dev_get_drvdata(dev); int nr, bit, regindex; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1726,6 +1738,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); } @@ -1735,6 +1748,7 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->fan_from_reg_min(data->fan_min[nr], data->fan_div[nr])); @@ -1746,6 +1760,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); } @@ -1757,9 +1772,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; unsigned long val; - int err; unsigned int reg; u8 new_div; + int err; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -1943,6 +1958,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } @@ -2019,6 +2035,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); } @@ -3009,6 +3026,7 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } -- cgit v1.2.3 From f975b3399c0a35439c48b89d1afffc4d86276d0f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 27 Nov 2014 10:59:06 +0100 Subject: hwmon: (ina2xx) bail-out from ina2xx_probe() in case of configuration errors The return value of i2c_smbus_write_word_swapped() isn't checked in ina2xx_probe(). This leads to devices being registered even if they cannot be physically detected (e.g. device is not powered-up at boot-time). Even after restoring power to such device, it is left unconfigured as the configuration has never been actually written to the register. Error out in case of write errors in probe and notify the user. Signed-off-by: Bartosz Golaszewski [Guenter Roeck: Fixed multi-line comment style] Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index bfd3f3eeabcd..e01feba909c3 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -223,6 +223,7 @@ static int ina2xx_probe(struct i2c_client *client, struct device *hwmon_dev; long shunt = 10000; /* default shunt value 10mOhms */ u32 val; + int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -247,12 +248,25 @@ static int ina2xx_probe(struct i2c_client *client, data->config = &ina2xx_config[data->kind]; /* device configuration */ - i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->config->config_default); - /* set current LSB to 1mA, shunt is in uOhms */ - /* (equation 13 in datasheet) */ - i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, - data->config->calibration_factor / shunt); + ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, + data->config->config_default); + if (ret < 0) { + dev_err(dev, + "error writing to the config register: %d", ret); + return -ENODEV; + } + + /* + * Set current LSB to 1mA, shunt is in uOhms + * (equation 13 in datasheet). + */ + ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, + data->config->calibration_factor / shunt); + if (ret < 0) { + dev_err(dev, + "error writing to the calibration register: %d", ret); + return -ENODEV; + } data->client = client; mutex_init(&data->update_lock); -- cgit v1.2.3 From 162a8dfe73df95e59265e350b2f247b8b35490d1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 22 Apr 2014 08:48:57 -0700 Subject: hwmon: (lm95245) Add support for LM95235 LM95235 is register compatible to LM95245. Also update link to LM95245 data sheet, and drop the link to the datasheet from the driver source to simplify code maintenance. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm95245 | 14 +++++++++----- drivers/hwmon/Kconfig | 5 +++-- drivers/hwmon/lm95245.c | 41 +++++++++++++++++++++++++++-------------- 3 files changed, 39 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/lm95245 b/Documentation/hwmon/lm95245 index 77eaf2812d25..d755901f58c4 100644 --- a/Documentation/hwmon/lm95245 +++ b/Documentation/hwmon/lm95245 @@ -2,10 +2,14 @@ Kernel driver lm95245 ================== Supported chips: - * National Semiconductor LM95245 + * TI LM95235 + Addresses scanned: I2C 0x18, 0x29, 0x4c + Datasheet: Publicly available at the TI website + http://www.ti.com/lit/ds/symlink/lm95235.pdf + * TI / 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 + Datasheet: Publicly available at the TI website + http://www.ti.com/lit/ds/symlink/lm95245.pdf Author: Alexander Stein @@ -13,10 +17,10 @@ Author: Alexander Stein Description ----------- -The LM95245 is an 11-bit digital temperature sensor with a 2-wire System +LM95235 and LM95245 are 11-bit digital temperature sensors 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 +The chips 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 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index fcca19b53bd2..085f5a853618 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1048,10 +1048,11 @@ config SENSORS_LM95241 will be called lm95241. config SENSORS_LM95245 - tristate "National Semiconductor LM95245 sensor chip" + tristate "National Semiconductor LM95245 and compatibles" depends on I2C help - If you say yes here you get support for LM95245 sensor chip. + If you say yes here you get support for LM95235 and LM95245 + temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm95245. diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 0ae0dfdafdff..e7aef4561c83 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -1,10 +1,8 @@ /* * Copyright (C) 2011 Alexander Stein * - * The LM95245 is a sensor chip made by National Semiconductors. + * The LM95245 is a sensor chip made by TI / National Semiconductor. * 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 * @@ -34,8 +32,6 @@ #include #include -#define DEVNAME "lm95245" - static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; @@ -98,7 +94,8 @@ static const unsigned short normal_i2c[] = { #define STATUS1_LOC 0x01 #define MANUFACTURER_ID 0x01 -#define DEFAULT_REVISION 0xB3 +#define LM95235_REVISION 0xB1 +#define LM95245_REVISION 0xB3 static const u8 lm95245_reg_address[] = { LM95245_REG_R_LOCAL_TEMPH_S, @@ -427,17 +424,32 @@ static int lm95245_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + int address = new_client->addr; + const char *name; + int rev, id; 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) + id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID); + if (id != MANUFACTURER_ID) return -ENODEV; - strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); + rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID); + switch (rev) { + case LM95235_REVISION: + if (address != 0x18 && address != 0x29 && address != 0x4c) + return -ENODEV; + name = "lm95235"; + break; + case LM95245_REVISION: + name = "lm95245"; + break; + default: + return -ENODEV; + } + + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -484,7 +496,8 @@ static int lm95245_probe(struct i2c_client *client, /* Driver data (common to all clients) */ static const struct i2c_device_id lm95245_id[] = { - { DEVNAME, 0 }, + { "lm95235", 0 }, + { "lm95245", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95245_id); @@ -492,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, lm95245_id); static struct i2c_driver lm95245_driver = { .class = I2C_CLASS_HWMON, .driver = { - .name = DEVNAME, + .name = "lm95245", }, .probe = lm95245_probe, .id_table = lm95245_id, @@ -503,5 +516,5 @@ static struct i2c_driver lm95245_driver = { module_i2c_driver(lm95245_driver); MODULE_AUTHOR("Alexander Stein "); -MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_DESCRIPTION("LM95235/LM95245 sensor driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From dfcd4c53be1da9e297bba340ec46f3269cbc239e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 22 Apr 2014 09:34:14 -0700 Subject: hwmon: (lm95234) Add support for LM95233 LM95233 is similar to LM95234, but it only supports two instead of four external temperature sensors. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm95234 | 15 +++++--- drivers/hwmon/Kconfig | 6 +-- drivers/hwmon/lm95234.c | 91 ++++++++++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/lm95234 b/Documentation/hwmon/lm95234 index a0e95ddfd372..32b777ef224c 100644 --- a/Documentation/hwmon/lm95234 +++ b/Documentation/hwmon/lm95234 @@ -2,6 +2,10 @@ Kernel driver lm95234 ===================== Supported chips: + * National Semiconductor / Texas Instruments LM95233 + Addresses scanned: I2C 0x18, 0x2a, 0x2b + Datasheet: Publicly available at the Texas Instruments website + http://www.ti.com/product/lm95233 * National Semiconductor / Texas Instruments LM95234 Addresses scanned: I2C 0x18, 0x4d, 0x4e Datasheet: Publicly available at the Texas Instruments website @@ -13,11 +17,12 @@ Author: Guenter Roeck Description ----------- -LM95234 is an 11-bit digital temperature sensor with a 2-wire System Management -Bus (SMBus) interface and TrueTherm technology that can very accurately monitor -the temperature of four remote diodes as well as its own temperature. -The four remote diodes can be external devices such as microprocessors, -graphics processors or diode-connected 2N3904s. The LM95234's TruTherm +LM95233 and LM95234 are 11-bit digital temperature sensors with a 2-wire +System Management Bus (SMBus) interface and TrueTherm technology +that can very accurately monitor the temperature of two (LM95233) +or four (LM95234) remote diodes as well as its own temperature. +The remote diodes can be external devices such as microprocessors, +graphics processors or diode-connected 2N3904s. The chip's TruTherm beta compensation technology allows sensing of 90 nm or 65 nm process thermal diodes accurately. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 085f5a853618..b1ce6a093a93 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1028,11 +1028,11 @@ config SENSORS_LM93 will be called lm93. config SENSORS_LM95234 - tristate "National Semiconductor LM95234" + tristate "National Semiconductor LM95234 and compatibles" depends on I2C help - If you say yes here you get support for the LM95234 temperature - sensor. + If you say yes here you get support for the LM95233 and LM95234 + temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm95234. diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 411202bdaf6b..8796de39ff9b 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -1,7 +1,7 @@ /* * Driver for Texas Instruments / National Semiconductor LM95234 * - * Copyright (c) 2013 Guenter Roeck + * Copyright (c) 2013, 2014 Guenter Roeck * * Derived from lm95241.c * Copyright (C) 2008, 2010 Davide Rizzo @@ -30,7 +30,10 @@ #define DRVNAME "lm95234" -static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; +enum chips { lm95233, lm95234 }; + +static const unsigned short normal_i2c[] = { + 0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END }; /* LM95234 registers */ #define LM95234_REG_MAN_ID 0xFE @@ -53,11 +56,13 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; #define LM95234_REG_TCRIT_HYST 0x5a #define NATSEMI_MAN_ID 0x01 +#define LM95233_CHIP_ID 0x89 #define LM95234_CHIP_ID 0x79 /* Client data (each client gets its own) */ struct lm95234_data { struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; unsigned long last_updated, interval; /* in jiffies */ bool valid; /* false until following fields are valid */ @@ -564,35 +569,23 @@ static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95234_attrs[] = { +static struct attribute *lm95234_common_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp3_type.dev_attr.attr, - &sensor_dev_attr_temp4_type.dev_attr.attr, - &sensor_dev_attr_temp5_type.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_crit.dev_attr.attr, &sensor_dev_attr_temp3_crit.dev_attr.attr, &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, @@ -601,18 +594,44 @@ static struct attribute *lm95234_attrs[] = { &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp3_offset.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static const struct attribute_group lm95234_common_group = { + .attrs = lm95234_common_attrs, +}; + +static struct attribute *lm95234_attrs[] = { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp4_type.dev_attr.attr, + &sensor_dev_attr_temp5_type.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, &sensor_dev_attr_temp4_offset.dev_attr.attr, &sensor_dev_attr_temp5_offset.dev_attr.attr, - &dev_attr_update_interval.attr, NULL }; -ATTRIBUTE_GROUPS(lm95234); + +static const struct attribute_group lm95234_group = { + .attrs = lm95234_attrs, +}; static int lm95234_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int address = client->addr; + u8 config_mask, model_mask; int mfg_id, chip_id, val; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -622,15 +641,31 @@ static int lm95234_detect(struct i2c_client *client, return -ENODEV; chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID); - if (chip_id != LM95234_CHIP_ID) + switch (chip_id) { + case LM95233_CHIP_ID: + if (address != 0x18 && address != 0x2a && address != 0x2b) + return -ENODEV; + config_mask = 0xbf; + model_mask = 0xf9; + name = "lm95233"; + break; + case LM95234_CHIP_ID: + if (address != 0x18 && address != 0x4d && address != 0x4e) + return -ENODEV; + config_mask = 0xbc; + model_mask = 0xe1; + name = "lm95234"; + break; + default: return -ENODEV; + } val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS); if (val & 0x30) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); - if (val & 0xbc) + if (val & config_mask) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); @@ -638,14 +673,14 @@ static int lm95234_detect(struct i2c_client *client, return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); - if (val & 0xe1) + if (val & model_mask) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); - if (val & 0xe1) + if (val & model_mask) return -ENODEV; - strlcpy(info->type, "lm95234", I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -698,15 +733,19 @@ static int lm95234_probe(struct i2c_client *client, if (err < 0) return err; + data->groups[0] = &lm95234_common_group; + if (id->driver_data == lm95234) + data->groups[1] = &lm95234_group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - lm95234_groups); + data, data->groups); return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ static const struct i2c_device_id lm95234_id[] = { - { "lm95234", 0 }, + { "lm95233", lm95233 }, + { "lm95234", lm95234 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95234_id); @@ -725,5 +764,5 @@ static struct i2c_driver lm95234_driver = { module_i2c_driver(lm95234_driver); MODULE_AUTHOR("Guenter Roeck "); -MODULE_DESCRIPTION("LM95234 sensor driver"); +MODULE_DESCRIPTION("LM95233/LM95234 sensor driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 06adbaec2a7a3d04741557b411e264c7f9c91c85 Mon Sep 17 00:00:00 2001 From: Patrick Titiano Date: Thu, 4 Dec 2014 17:45:51 +0100 Subject: hwmon: (tmp401) Add support for TI TMP435 Signed-off-by: Patrick Titiano [Bartosz Golaszewski: prepared for submission, code review fixes] Signed-off-by: Bartosz Golaszewski [Guenter Roeck: Merged two patches into one] Signed-off-by: Guenter Roeck --- Documentation/hwmon/tmp401 | 8 ++++++-- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/tmp401.c | 13 +++++++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/tmp401 b/Documentation/hwmon/tmp401 index f91e3fa7e5ec..445ff7b0f6a7 100644 --- a/Documentation/hwmon/tmp401 +++ b/Documentation/hwmon/tmp401 @@ -18,6 +18,10 @@ Supported chips: Prefix: 'tmp432' Addresses scanned: I2C 0x4c, 0x4d Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp432.html + * Texas Instruments TMP435 + Prefix: 'tmp435' + Addresses scanned: I2C 0x4c + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html Authors: Hans de Goede @@ -27,8 +31,8 @@ Description ----------- This driver implements support for Texas Instruments TMP401, TMP411, -TMP431, and TMP432 chips. These chips implement one or two remote and -one local temperature sensors. Temperature is measured in degrees +TMP431, TMP432 and TMP435 chips. These chips implement one or two remote +and one local temperature sensors. Temperature is measured in degrees Celsius. Resolution of the remote sensor is 0.0625 degree. Local sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not supported by the driver so far, so using the default resolution of 0.5 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b1ce6a093a93..6529c09c46f0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1466,7 +1466,7 @@ config SENSORS_TMP401 depends on I2C help If you say yes here you get support for Texas Instruments TMP401, - TMP411, TMP431, and TMP432 temperature sensor chips. + TMP411, TMP431, TMP432 and TMP435 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 7fa6e7d0b9b6..ccd993880d74 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -46,7 +46,7 @@ /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { tmp401, tmp411, tmp431, tmp432 }; +enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; /* * The TMP401 registers, note some registers have different addresses for @@ -136,6 +136,7 @@ static const u8 TMP432_STATUS_REG[] = { #define TMP411C_DEVICE_ID 0x10 #define TMP431_DEVICE_ID 0x31 #define TMP432_DEVICE_ID 0x32 +#define TMP435_DEVICE_ID 0x35 /* * Driver data (common to all clients) @@ -146,6 +147,7 @@ static const struct i2c_device_id tmp401_id[] = { { "tmp411", tmp411 }, { "tmp431", tmp431 }, { "tmp432", tmp432 }, + { "tmp435", tmp435 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -684,6 +686,11 @@ static int tmp401_detect(struct i2c_client *client, return -ENODEV; kind = tmp432; break; + case TMP435_DEVICE_ID: + if (client->addr != 0x4c) + return -ENODEV; + kind = tmp435; + break; default: return -ENODEV; } @@ -705,7 +712,9 @@ static int tmp401_detect(struct i2c_client *client, static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; + static const char * const names[] = { + "TMP401", "TMP411", "TMP431", "TMP432", "TMP435" + }; struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp401_data *data; -- cgit v1.2.3 From 90652efeba1a05300931b3fad53540b9bca73948 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 4 Dec 2014 17:45:53 +0100 Subject: hwmon: (tmp401) Bail out from tmp401_probe() in case of write errors The return value of i2c_smbus_read_byte_data() is checked in tmp401_init_client(), but only a warning is printed and the device is registered anyway. This leads to devices being registered even if they cannot be physically detected. Bail out from probe in case of write errors and notify the user. Signed-off-by: Bartosz Golaszewski Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp401.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index ccd993880d74..f2182ee80830 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -615,10 +615,10 @@ static const struct attribute_group tmp432_group = { * Begin non sysfs callback code (aka Real code) */ -static void tmp401_init_client(struct tmp401_data *data, - struct i2c_client *client) +static int tmp401_init_client(struct tmp401_data *data, + struct i2c_client *client) { - int config, config_orig; + int config, config_orig, status = 0; /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); @@ -626,16 +626,18 @@ static void tmp401_init_client(struct tmp401_data *data, /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (config < 0) { - dev_warn(&client->dev, "Initialization failed!\n"); - return; - } + if (config < 0) + return config; config_orig = config; config &= ~TMP401_CONFIG_SHUTDOWN; if (config != config_orig) - i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config); + status = i2c_smbus_write_byte_data(client, + TMP401_CONFIG_WRITE, + config); + + return status; } static int tmp401_detect(struct i2c_client *client, @@ -718,7 +720,7 @@ static int tmp401_probe(struct i2c_client *client, struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp401_data *data; - int groups = 0; + int groups = 0, status; data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) @@ -729,7 +731,9 @@ static int tmp401_probe(struct i2c_client *client, data->kind = id->driver_data; /* Initialize the TMP401 chip */ - tmp401_init_client(data, client); + status = tmp401_init_client(data, client); + if (status < 0) + return status; /* Register sysfs hooks */ data->groups[groups++] = &tmp401_group; -- cgit v1.2.3 From 52a95c1185220feb514c8e167bd6033c0da6f576 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 4 Dec 2014 10:58:47 -0600 Subject: hwmon: (gpio-fan) Allow usage of gpio operations that may sleep Certain I2C based GPIO expanders could be used in sleepable context, this results in: [ 115.890569] ------------[ cut here ]------------ [ 115.895422] WARNING: CPU: 0 PID: 1115 at drivers/gpio/gpiolib.c:1370 gpiod_set_raw_value+0x40/0x4c() [ 115.905024] Modules linked in: [ 115.908229] CPU: 0 PID: 1115 Comm: sh Tainted: G W 3.18.0-rc7-next-20141203-dirty #1 [ 115.917461] Hardware name: Generic DRA74X (Flattened Device Tree) [ 115.923876] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 115.932013] [] (show_stack) from [] (dump_stack+0x78/0x94) [ 115.939594] [] (dump_stack) from [] (warn_slowpath_common+0x7c/0xb4) [ 115.948094] [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [ 115.957315] [] (warn_slowpath_null) from [] (gpiod_set_raw_value+0x40/0x4c) [ 115.966457] [] (gpiod_set_raw_value) from [] (set_fan_speed+0x4c/0x64) [ 115.975145] [] (set_fan_speed) from [] (set_rpm+0x98/0xac) [ 115.982742] [] (set_rpm) from [] (dev_attr_store+0x18/0x24) [ 115.990426] [] (dev_attr_store) from [] (sysfs_kf_write+0x4c/0x50) [ 115.998742] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xbc/0x19c) [ 116.007333] [] (kernfs_fop_write) from [] (vfs_write+0xb0/0x1a0) [ 116.015461] [] (vfs_write) from [] (SyS_write+0x44/0x84) [ 116.022881] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x48) [ 116.030833] ---[ end trace 3a0b636123acab82 ]--- So, switch over to sleepable GPIO operations as there is no mandatory need for non-sleepable gpio operations in the fan driver. This allows the fan driver to be used with i2c based gpio expanders such as palmas_gpio. Signed-off-by: Nishanth Menon Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 4efa1734bdad..7802eb2a442f 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -79,7 +79,7 @@ static ssize_t show_fan_alarm(struct device *dev, { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); struct gpio_fan_alarm *alarm = fan_data->alarm; - int value = gpio_get_value(alarm->gpio); + int value = gpio_get_value_cansleep(alarm->gpio); if (alarm->active_low) value = !value; @@ -131,7 +131,7 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) int i; for (i = 0; i < fan_data->num_ctrl; i++) - gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); + gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1); } static int __get_fan_ctrl(struct gpio_fan_data *fan_data) @@ -142,7 +142,7 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data) for (i = 0; i < fan_data->num_ctrl; i++) { int value; - value = gpio_get_value(fan_data->ctrl[i]); + value = gpio_get_value_cansleep(fan_data->ctrl[i]); ctrl_val |= (value << i); } return ctrl_val; @@ -369,7 +369,8 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, if (err) return err; - err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + err = gpio_direction_output(ctrl[i], + gpio_get_value_cansleep(ctrl[i])); if (err) return err; } -- cgit v1.2.3 From b95579cd8795442e75c8846fa6eeb4fb442e9d83 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 4 Dec 2014 10:58:56 -0600 Subject: hwmon: (gpio-fan) Add a shutdown handler to poweroff the fans Poweroff the fans when shutting down the system. Else, echo '1' > /sys/class/hwmon/hwmon0/fan1_target; poweroff leaves the fan running if the System power off does not drive the gpio expander which might control the fan power supply. Signed-off-by: Nishanth Menon Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 7802eb2a442f..36abf814b8c7 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -550,6 +550,14 @@ static int gpio_fan_probe(struct platform_device *pdev) return 0; } +static void gpio_fan_shutdown(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, 0); +} + #ifdef CONFIG_PM_SLEEP static int gpio_fan_suspend(struct device *dev) { @@ -581,6 +589,7 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, + .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", .pm = GPIO_FAN_PM, -- cgit v1.2.3 From 4ad40cc568c5537de11092d3362f9cb287f915d9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 4 Dec 2014 09:58:15 -0800 Subject: hwmon: (lm75) Strengthen detect function A chip returning 0x00 in all registers is erroneously detected as LM75. Check hysteresis and temperature limit registers and abort if both are 0 to reduce the likelyhood for this to happen. Reviewed-by: Rob Coulson Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/lm75.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index f58439b817b5..6753fd940c76 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -415,6 +415,12 @@ static int lm75_detect(struct i2c_client *new_client, || i2c_smbus_read_byte_data(new_client, 7) != os) return -ENODEV; } + /* + * It is very unlikely that this is a LM75 if both + * hysteresis and temperature limit registers are 0. + */ + if (hyst == 0 && os == 0) + return -ENODEV; /* Addresses cycling */ for (i = 8; i <= 248; i += 40) { -- cgit v1.2.3 From 907a6d5824599d09e986105a5a880d119a996c4b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Dec 2014 10:15:03 -0800 Subject: hwmon: (tmp401) Detect TMP435 on all addresses it supports TMP435 supports a range of I2C addresses, not just 0x4c. Cc: Patrick Titiano Cc: Bartosz Golaszewski Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- Documentation/hwmon/tmp401 | 2 +- drivers/hwmon/tmp401.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/tmp401 b/Documentation/hwmon/tmp401 index 445ff7b0f6a7..8eb88e974055 100644 --- a/Documentation/hwmon/tmp401 +++ b/Documentation/hwmon/tmp401 @@ -20,7 +20,7 @@ Supported chips: Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp432.html * Texas Instruments TMP435 Prefix: 'tmp435' - Addresses scanned: I2C 0x4c + Addresses scanned: I2C 0x37, 0x48 - 0x4f Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html Authors: diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index f2182ee80830..99664ebc738d 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -44,7 +44,8 @@ #include /* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d, + 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; @@ -679,18 +680,16 @@ static int tmp401_detect(struct i2c_client *client, kind = tmp411; break; case TMP431_DEVICE_ID: - if (client->addr == 0x4e) + if (client->addr != 0x4c && client->addr != 0x4d) return -ENODEV; kind = tmp431; break; case TMP432_DEVICE_ID: - if (client->addr == 0x4e) + if (client->addr != 0x4c && client->addr != 0x4d) return -ENODEV; kind = tmp432; break; case TMP435_DEVICE_ID: - if (client->addr != 0x4c) - return -ENODEV; kind = tmp435; break; default: -- cgit v1.2.3