diff options
Diffstat (limited to 'drivers/hwmon')
46 files changed, 2438 insertions, 780 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 0b62c3c6b7ce..9ec854ae118b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -68,6 +68,16 @@ config SENSORS_ABITUGURU3 This driver can also be built as a module. If so, the module will be called abituguru3. +config SENSORS_AD7314 + tristate "Analog Devices AD7314 and compatibles" + depends on SPI && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + AD7314, ADT7301 and ADT7302 temperature sensors. + + This driver can also be built as a module. If so, the module + will be called ad7314. + config SENSORS_AD7414 tristate "Analog Devices AD7414" depends on I2C && EXPERIMENTAL @@ -303,6 +313,16 @@ config SENSORS_DS1621 This driver can also be built as a module. If so, the module will be called ds1621. +config SENSORS_EXYNOS4_TMU + tristate "Temperature sensor on Samsung EXYNOS4" + depends on EXYNOS4_DEV_TMU + help + If you say yes here you get support for TMU (Thermal Managment + Unit) on SAMSUNG EXYNOS4 series of SoC. + + This driver can also be built as a module. If so, the module + will be called exynos4-tmu. + config SENSORS_I5K_AMB tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets" depends on PCI && EXPERIMENTAL @@ -315,6 +335,7 @@ config SENSORS_I5K_AMB config SENSORS_F71805F tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" + depends on !PPC help If you say yes here you get support for hardware monitoring features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG @@ -325,6 +346,7 @@ config SENSORS_F71805F config SENSORS_F71882FG tristate "Fintek F71882FG and compatibles" + depends on !PPC help If you say yes here you get support for hardware monitoring features of many Fintek Super-I/O (LPC) chips. The currently @@ -448,6 +470,7 @@ config SENSORS_IBMPEX config SENSORS_IT87 tristate "ITE IT87xx and compatibles" + depends on !PPC select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, @@ -531,6 +554,7 @@ config SENSORS_LM75 If you say yes here you get support for one common type of temperature sensor chip, with models including: + - Analog Devices ADT75 - Dallas Semiconductor DS75 and DS1775 - Maxim MAX6625 and MAX6626 - Microchip MCP980x @@ -803,6 +827,7 @@ config SENSORS_NTC_THERMISTOR config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" + depends on !PPC select HWMON_VID help If you say yes here you get access to the hardware monitoring @@ -816,6 +841,7 @@ config SENSORS_PC87360 config SENSORS_PC87427 tristate "National Semiconductor PC87427" + depends on !PPC help If you say yes here you get access to the hardware monitoring functions of the National Semiconductor PC87427 Super-I/O chip. @@ -907,7 +933,7 @@ config SENSORS_SMM665 config SENSORS_DME1737 tristate "SMSC DME1737, SCH311x and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C && EXPERIMENTAL && !PPC select HWMON_VID help If you say yes here you get support for the hardware monitoring @@ -949,6 +975,7 @@ config SENSORS_EMC6W201 config SENSORS_SMSC47M1 tristate "SMSC LPC47M10x and compatibles" + depends on !PPC help If you say yes here you get support for the integrated fan monitoring and control capabilities of the SMSC LPC47B27x, @@ -982,7 +1009,7 @@ config SENSORS_SMSC47M192 config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" - depends on EXPERIMENTAL + depends on EXPERIMENTAL && !PPC help If you say yes here you get support for the SMSC LPC47B397-NC sensor chip. @@ -996,6 +1023,7 @@ config SENSORS_SCH56XX_COMMON config SENSORS_SCH5627 tristate "SMSC SCH5627" + depends on !PPC select SENSORS_SCH56XX_COMMON help If you say yes here you get support for the hardware monitoring @@ -1006,6 +1034,7 @@ config SENSORS_SCH5627 config SENSORS_SCH5636 tristate "SMSC SCH5636" + depends on !PPC select SENSORS_SCH56XX_COMMON help SMSC SCH5636 Super I/O chips include an embedded microcontroller for @@ -1129,6 +1158,7 @@ config SENSORS_VIA686A config SENSORS_VT1211 tristate "VIA VT1211" + depends on !PPC select HWMON_VID help If you say yes here then you get support for hardware monitoring @@ -1241,6 +1271,7 @@ config SENSORS_W83L786NG config SENSORS_W83627HF tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" + depends on !PPC select HWMON_VID help If you say yes here you get support for the Winbond W836X7 series @@ -1251,7 +1282,8 @@ config SENSORS_W83627HF will be called w83627hf. config SENSORS_W83627EHF - tristate "Winbond W83627EHF/EHG/DHG, W83667HG, NCT6775F, NCT6776F" + tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG, NCT6775F, NCT6776F" + depends on !PPC select HWMON_VID help If you say yes here you get support for the hardware @@ -1260,7 +1292,8 @@ config SENSORS_W83627EHF This driver also supports the W83627EHG, which is the lead-free version of the W83627EHF, and the W83627DHG, which is a similar chip suited for specific Intel processors that use PECI such as - the Core 2 Duo. + the Core 2 Duo. And also the W83627UHG, which is a stripped down + version of the W83627DHG (as far as hardware monitoring goes.) This driver also supports Nuvoton W83667HG, W83667HG-B, NCT6775F (also known as W83667HG-I), and NCT6776F. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 3c9ccefea791..8251ce8cd035 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o +obj-$(CONFIG_SENSORS_AD7314) += ad7314.o obj-$(CONFIG_SENSORS_AD7414) += ad7414.o obj-$(CONFIG_SENSORS_AD7418) += ad7418.o obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o @@ -47,6 +48,7 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o +obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c new file mode 100644 index 000000000000..318e38e85376 --- /dev/null +++ b/drivers/hwmon/ad7314.c @@ -0,0 +1,186 @@ +/* + * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302 + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + * Conversion to hwmon from IIO done by Jonathan Cameron <jic23@cam.ac.uk> + */ +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +/* + * AD7314 power mode + */ +#define AD7314_PD 0x2000 + +/* + * AD7314 temperature masks + */ +#define AD7314_TEMP_SIGN 0x200 +#define AD7314_TEMP_MASK 0x7FE0 +#define AD7314_TEMP_OFFSET 5 + +/* + * ADT7301 and ADT7302 temperature masks + */ +#define ADT7301_TEMP_SIGN 0x2000 +#define ADT7301_TEMP_MASK 0x3FFF + +enum ad7314_variant { + adt7301, + adt7302, + ad7314, +}; + +struct ad7314_data { + struct spi_device *spi_dev; + struct device *hwmon_dev; + u16 rx ____cacheline_aligned; +}; + +static int ad7314_spi_read(struct ad7314_data *chip, s16 *data) +{ + int ret; + + ret = spi_read(chip->spi_dev, (u8 *)&chip->rx, sizeof(chip->rx)); + if (ret < 0) { + dev_err(&chip->spi_dev->dev, "SPI read error\n"); + return ret; + } + + *data = be16_to_cpu(chip->rx); + + return ret; +} + +static ssize_t ad7314_show_temperature(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad7314_data *chip = dev_get_drvdata(dev); + s16 data; + int ret; + + ret = ad7314_spi_read(chip, &data); + if (ret < 0) + return ret; + switch (spi_get_device_id(chip->spi_dev)->driver_data) { + case ad7314: + data = (data & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET; + data = (data << 6) >> 6; + + return sprintf(buf, "%d\n", 250 * data); + case adt7301: + case adt7302: + /* + * Documented as a 13 bit twos complement register + * with a sign bit - which is a 14 bit 2's complement + * register. 1lsb - 31.25 milli degrees centigrade + */ + data &= ADT7301_TEMP_MASK; + data = (data << 2) >> 2; + + return sprintf(buf, "%d\n", + DIV_ROUND_CLOSEST(data * 3125, 100)); + default: + return -EINVAL; + } +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + ad7314_show_temperature, NULL, 0); + +static struct attribute *ad7314_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7314_group = { + .attrs = ad7314_attributes, +}; + +static int __devinit ad7314_probe(struct spi_device *spi_dev) +{ + int ret; + struct ad7314_data *chip; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + ret = -ENOMEM; + goto error_ret; + } + dev_set_drvdata(&spi_dev->dev, chip); + + ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group); + if (ret < 0) + goto error_free_chip; + chip->hwmon_dev = hwmon_device_register(&spi_dev->dev); + if (IS_ERR(chip->hwmon_dev)) { + ret = PTR_ERR(chip->hwmon_dev); + goto error_remove_group; + } + + return 0; +error_remove_group: + sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); +error_free_chip: + kfree(chip); +error_ret: + return ret; +} + +static int __devexit ad7314_remove(struct spi_device *spi_dev) +{ + struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev); + + hwmon_device_unregister(chip->hwmon_dev); + sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); + kfree(chip); + + return 0; +} + +static const struct spi_device_id ad7314_id[] = { + { "adt7301", adt7301 }, + { "adt7302", adt7302 }, + { "ad7314", ad7314 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7314_id); + +static struct spi_driver ad7314_driver = { + .driver = { + .name = "ad7314", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad7314_probe, + .remove = __devexit_p(ad7314_remove), + .id_table = ad7314_id, +}; + +static __init int ad7314_init(void) +{ + return spi_register_driver(&ad7314_driver); +} +module_init(ad7314_init); + +static __exit void ad7314_exit(void) +{ + spi_unregister_driver(&ad7314_driver); +} +module_exit(ad7314_exit); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital" + " temperature sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index d46c0c758ddf..df29a7fff9e7 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -58,10 +58,9 @@ static inline int ad7414_temp_from_reg(s16 reg) static inline int ad7414_read(struct i2c_client *client, u8 reg) { - if (reg == AD7414_REG_TEMP) { - int value = i2c_smbus_read_word_data(client, reg); - return (value < 0) ? value : swab16(value); - } else + if (reg == AD7414_REG_TEMP) + return i2c_smbus_read_word_swapped(client, reg); + else return i2c_smbus_read_byte_data(client, reg); } diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index ffc781fec185..8cb718ce8237 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -76,20 +76,6 @@ static struct i2c_driver ad7418_driver = { .id_table = ad7418_id, }; -/* All registers are word-sized, except for the configuration registers. - * AD7418 uses a high-byte first convention. Do NOT use those functions to - * access the configuration registers CONF and CONF2, as they are byte-sized. - */ -static inline int ad7418_read(struct i2c_client *client, u8 reg) -{ - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value) -{ - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - static void ad7418_init_client(struct i2c_client *client) { struct ad7418_data *data = i2c_get_clientdata(client); @@ -128,7 +114,9 @@ static struct ad7418_data *ad7418_update_device(struct device *dev) udelay(30); for (i = 0; i < 3; i++) { - data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]); + data->temp[i] = + i2c_smbus_read_word_swapped(client, + AD7418_REG_TEMP[i]); } for (i = 0, ch = 4; i < data->adc_max; i++, ch--) { @@ -138,11 +126,12 @@ static struct ad7418_data *ad7418_update_device(struct device *dev) udelay(15); data->in[data->adc_max - 1 - i] = - ad7418_read(client, AD7418_REG_ADC); + i2c_smbus_read_word_swapped(client, + AD7418_REG_ADC); } /* restore old configuration value */ - ad7418_write(client, AD7418_REG_CONF, cfg); + i2c_smbus_write_word_swapped(client, AD7418_REG_CONF, cfg); data->last_updated = jiffies; data->valid = 1; @@ -182,7 +171,9 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->lock); data->temp[attr->index] = LM75_TEMP_TO_REG(temp); - ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]); + i2c_smbus_write_word_swapped(client, + AD7418_REG_TEMP[attr->index], + data->temp[attr->index]); mutex_unlock(&data->lock); return count; } diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index e9beeda4cbe5..eedca3cf9968 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c @@ -59,19 +59,6 @@ struct ads1015_data { struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; }; -static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg) -{ - s32 data = i2c_smbus_read_word_data(client, reg); - - return (data < 0) ? data : swab16(data); -} - -static s32 ads1015_write_reg(struct i2c_client *client, unsigned int reg, - u16 val) -{ - return i2c_smbus_write_word_data(client, reg, swab16(val)); -} - static int ads1015_read_value(struct i2c_client *client, unsigned int channel, int *value) { @@ -87,7 +74,7 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel, mutex_lock(&data->update_lock); /* get channel parameters */ - res = ads1015_read_reg(client, ADS1015_CONFIG); + res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); if (res < 0) goto err_unlock; config = res; @@ -101,13 +88,13 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel, config |= (pga & 0x0007) << 9; config |= (data_rate & 0x0007) << 5; - res = ads1015_write_reg(client, ADS1015_CONFIG, config); + res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config); if (res < 0) goto err_unlock; /* wait until conversion finished */ msleep(conversion_time_ms); - res = ads1015_read_reg(client, ADS1015_CONFIG); + res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); if (res < 0) goto err_unlock; config = res; @@ -117,7 +104,7 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel, goto err_unlock; } - res = ads1015_read_reg(client, ADS1015_CONVERSION); + res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION); if (res < 0) goto err_unlock; conversion = res; diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index c42c5a69a664..cfcc3b6fb6bf 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -74,13 +74,6 @@ static int ads7828_detect(struct i2c_client *client, static int ads7828_probe(struct i2c_client *client, const struct i2c_device_id *id); -/* The ADS7828 returns the 12-bit sample in two bytes, - these are read as a word then byte-swapped */ -static u16 ads7828_read_value(struct i2c_client *client, u8 reg) -{ - return swab16(i2c_smbus_read_word_data(client, reg)); -} - static inline u8 channel_cmd_byte(int ch) { /* cmd byte C2,C1,C0 - see datasheet */ @@ -104,7 +97,8 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) for (ch = 0; ch < ADS7828_NCH; ch++) { u8 cmd = channel_cmd_byte(ch); - data->adc_input[ch] = ads7828_read_value(client, cmd); + data->adc_input[ch] = + i2c_smbus_read_word_swapped(client, cmd); } data->last_updated = jiffies; data->valid = 1; @@ -203,7 +197,7 @@ static int ads7828_detect(struct i2c_client *client, for (ch = 0; ch < ADS7828_NCH; ch++) { u16 in_data; u8 cmd = channel_cmd_byte(ch); - in_data = ads7828_read_value(client, cmd); + in_data = i2c_smbus_read_word_swapped(client, cmd); if (in_data & 0xF000) { pr_debug("%s : Doesn't look like an ads7828 device\n", __func__); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index c02a052d3085..d7bd1f3f2a31 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -829,17 +829,17 @@ static int asb100_read_value(struct i2c_client *client, u16 reg) /* convert from ISA to LM75 I2C addresses */ switch (reg & 0xff) { case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); + res = i2c_smbus_read_word_swapped(cl, 0); break; case 0x52: /* CONFIG */ res = i2c_smbus_read_byte_data(cl, 1); break; case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); + res = i2c_smbus_read_word_swapped(cl, 2); break; case 0x55: /* MAX */ default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); + res = i2c_smbus_read_word_swapped(cl, 3); break; } } @@ -877,10 +877,10 @@ static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) i2c_smbus_write_byte_data(cl, 1, value & 0xff); break; case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); + i2c_smbus_write_word_swapped(cl, 2, value); break; case 0x55: /* MAX */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); + i2c_smbus_write_word_swapped(cl, 3, value); break; } } diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 932383786642..104b3767516c 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -60,14 +60,13 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #ifdef CONFIG_SMP #define TO_PHYS_ID(cpu) cpu_data(cpu).phys_proc_id #define TO_CORE_ID(cpu) cpu_data(cpu).cpu_core_id -#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) #define for_each_sibling(i, cpu) for_each_cpu(i, cpu_sibling_mask(cpu)) #else #define TO_PHYS_ID(cpu) (cpu) #define TO_CORE_ID(cpu) (cpu) -#define TO_ATTR_NO(cpu) (cpu) #define for_each_sibling(i, cpu) for (i = 0; false; ) #endif +#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) /* * Per-Core Temperature Data @@ -325,15 +324,6 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) return adjust_tjmax(c, id, dev); } -static void __devinit get_ucode_rev_on_cpu(void *edx) -{ - u32 eax; - - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - sync_core(); - rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); -} - static int create_name_attr(struct platform_data *pdata, struct device *dev) { sysfs_attr_init(&pdata->name_attr.attr); @@ -380,27 +370,16 @@ exit_free: static int __cpuinit chk_ucode_version(unsigned int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); - int err; - u32 edx; /* * Check if we have problem with errata AE18 of Core processors: * Readings might stop update when processor visited too deep sleep, * fixed for stepping D0 (6EC). */ - if (c->x86_model == 0xe && c->x86_mask < 0xc) { - /* check for microcode update */ - err = smp_call_function_single(cpu, get_ucode_rev_on_cpu, - &edx, 1); - if (err) { - pr_err("Cannot determine microcode revision of " - "CPU#%u (%d)!\n", cpu, err); - return -ENODEV; - } else if (edx < 0x39) { - pr_err("Errata AE18 not fixed, update BIOS or " - "microcode of the CPU!\n"); - return -ENODEV; - } + if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) { + pr_err("Errata AE18 not fixed, update BIOS or " + "microcode of the CPU!\n"); + return -ENODEV; } return 0; } diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index e11363467a8d..ef1ac996752e 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -80,24 +80,6 @@ struct ds1621_data { u8 conf; /* Register encoding, combined */ }; -/* Temperature registers are word-sized. - DS1621 uses a high-byte first convention, which is exactly opposite to - the SMBus standard. */ -static int ds1621_read_temp(struct i2c_client *client, u8 reg) -{ - int ret; - - ret = i2c_smbus_read_word_data(client, reg); - if (ret < 0) - return ret; - return swab16(ret); -} - -static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value) -{ - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - static void ds1621_init_client(struct i2c_client *client) { u8 conf, new_conf; @@ -136,7 +118,7 @@ static struct ds1621_data *ds1621_update_client(struct device *dev) data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); for (i = 0; i < ARRAY_SIZE(data->temp); i++) - data->temp[i] = ds1621_read_temp(client, + data->temp[i] = i2c_smbus_read_word_swapped(client, DS1621_REG_TEMP[i]); /* reset alarms if necessary */ @@ -177,8 +159,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, mutex_lock(&data->update_lock); data->temp[attr->index] = val; - ds1621_write_temp(client, DS1621_REG_TEMP[attr->index], - data->temp[attr->index]); + i2c_smbus_write_word_swapped(client, DS1621_REG_TEMP[attr->index], + data->temp[attr->index]); mutex_unlock(&data->update_lock); return count; } diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index 4f7c3fc40a89..225ae4f36583 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -75,33 +75,13 @@ struct ds620_data { s16 temp[3]; /* Register values, word */ }; -/* - * Temperature registers are word-sized. - * DS620 uses a high-byte first convention, which is exactly opposite to - * the SMBus standard. - */ -static int ds620_read_temp(struct i2c_client *client, u8 reg) -{ - int ret; - - ret = i2c_smbus_read_word_data(client, reg); - if (ret < 0) - return ret; - return swab16(ret); -} - -static int ds620_write_temp(struct i2c_client *client, u8 reg, u16 value) -{ - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - static void ds620_init_client(struct i2c_client *client) { struct ds620_platform_data *ds620_info = client->dev.platform_data; u16 conf, new_conf; new_conf = conf = - swab16(i2c_smbus_read_word_data(client, DS620_REG_CONF)); + i2c_smbus_read_word_swapped(client, DS620_REG_CONF); /* switch to continuous conversion mode */ new_conf &= ~DS620_REG_CONFIG_1SHOT; @@ -118,8 +98,7 @@ static void ds620_init_client(struct i2c_client *client) new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0; if (conf != new_conf) - i2c_smbus_write_word_data(client, DS620_REG_CONF, - swab16(new_conf)); + i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf); /* start conversion */ i2c_smbus_write_byte(client, DS620_COM_START); @@ -141,8 +120,8 @@ static struct ds620_data *ds620_update_client(struct device *dev) dev_dbg(&client->dev, "Starting ds620 update\n"); for (i = 0; i < ARRAY_SIZE(data->temp); i++) { - res = ds620_read_temp(client, - DS620_REG_TEMP[i]); + res = i2c_smbus_read_word_swapped(client, + DS620_REG_TEMP[i]); if (res < 0) { ret = ERR_PTR(res); goto abort; @@ -191,8 +170,8 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, mutex_lock(&data->update_lock); data->temp[attr->index] = val; - ds620_write_temp(client, DS620_REG_TEMP[attr->index], - data->temp[attr->index]); + i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index], + data->temp[attr->index]); mutex_unlock(&data->update_lock); return count; } @@ -210,16 +189,15 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *da, return PTR_ERR(data); /* reset alarms if necessary */ - res = i2c_smbus_read_word_data(client, DS620_REG_CONF); + res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF); if (res < 0) return res; - conf = swab16(res); - new_conf = conf; + new_conf = conf = res; new_conf &= ~attr->index; if (conf != new_conf) { - res = i2c_smbus_write_word_data(client, DS620_REG_CONF, - swab16(new_conf)); + res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF, + new_conf); if (res < 0) return res; } diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c new file mode 100644 index 000000000000..faa0884f61f6 --- /dev/null +++ b/drivers/hwmon/exynos4_tmu.c @@ -0,0 +1,524 @@ +/* + * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit) + * + * Copyright (C) 2011 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/workqueue.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/io.h> +#include <linux/mutex.h> + +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#include <linux/platform_data/exynos4_tmu.h> + +#define EXYNOS4_TMU_REG_TRIMINFO 0x0 +#define EXYNOS4_TMU_REG_CONTROL 0x20 +#define EXYNOS4_TMU_REG_STATUS 0x28 +#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40 +#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44 +#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50 +#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54 +#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58 +#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C +#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60 +#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64 +#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68 +#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C +#define EXYNOS4_TMU_REG_INTEN 0x70 +#define EXYNOS4_TMU_REG_INTSTAT 0x74 +#define EXYNOS4_TMU_REG_INTCLEAR 0x78 + +#define EXYNOS4_TMU_GAIN_SHIFT 8 +#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24 + +#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff +#define EXYNOS4_TMU_CORE_ON 3 +#define EXYNOS4_TMU_CORE_OFF 2 +#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50 +#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1 +#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10 +#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100 +#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000 +#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111 + +struct exynos4_tmu_data { + struct exynos4_tmu_platform_data *pdata; + struct device *hwmon_dev; + struct resource *mem; + void __iomem *base; + int irq; + struct work_struct irq_work; + struct mutex lock; + struct clk *clk; + u8 temp_error1, temp_error2; +}; + +/* + * TMU treats temperature as a mapped temperature code. + * The temperature is converted differently depending on the calibration type. + */ +static int temp_to_code(struct exynos4_tmu_data *data, u8 temp) +{ + struct exynos4_tmu_platform_data *pdata = data->pdata; + int temp_code; + + /* temp should range between 25 and 125 */ + if (temp < 25 || temp > 125) { + temp_code = -EINVAL; + goto out; + } + + switch (pdata->cal_type) { + case TYPE_TWO_POINT_TRIMMING: + temp_code = (temp - 25) * + (data->temp_error2 - data->temp_error1) / + (85 - 25) + data->temp_error1; + break; + case TYPE_ONE_POINT_TRIMMING: + temp_code = temp + data->temp_error1 - 25; + break; + default: + temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; + break; + } +out: + return temp_code; +} + +/* + * Calculate a temperature value from a temperature code. + * The unit of the temperature is degree Celsius. + */ +static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code) +{ + struct exynos4_tmu_platform_data *pdata = data->pdata; + int temp; + + /* temp_code should range between 75 and 175 */ + if (temp_code < 75 || temp_code > 175) { + temp = -ENODATA; + goto out; + } + + switch (pdata->cal_type) { + case TYPE_TWO_POINT_TRIMMING: + temp = (temp_code - data->temp_error1) * (85 - 25) / + (data->temp_error2 - data->temp_error1) + 25; + break; + case TYPE_ONE_POINT_TRIMMING: + temp = temp_code - data->temp_error1 + 25; + break; + default: + temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; + break; + } +out: + return temp; +} + +static int exynos4_tmu_initialize(struct platform_device *pdev) +{ + struct exynos4_tmu_data *data = platform_get_drvdata(pdev); + struct exynos4_tmu_platform_data *pdata = data->pdata; + unsigned int status, trim_info; + int ret = 0, threshold_code; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + status = readb(data->base + EXYNOS4_TMU_REG_STATUS); + if (!status) { + ret = -EBUSY; + goto out; + } + + /* Save trimming info in order to perform calibration */ + trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO); + data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK; + data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK); + + /* Write temperature code for threshold */ + threshold_code = temp_to_code(data, pdata->threshold); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + writeb(threshold_code, + data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP); + + writeb(pdata->trigger_levels[0], + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0); + writeb(pdata->trigger_levels[1], + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1); + writeb(pdata->trigger_levels[2], + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2); + writeb(pdata->trigger_levels[3], + data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3); + + writel(EXYNOS4_TMU_INTCLEAR_VAL, + data->base + EXYNOS4_TMU_REG_INTCLEAR); +out: + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return ret; +} + +static void exynos4_tmu_control(struct platform_device *pdev, bool on) +{ + struct exynos4_tmu_data *data = platform_get_drvdata(pdev); + struct exynos4_tmu_platform_data *pdata = data->pdata; + unsigned int con, interrupt_en; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT | + pdata->gain << EXYNOS4_TMU_GAIN_SHIFT; + if (on) { + con |= EXYNOS4_TMU_CORE_ON; + interrupt_en = pdata->trigger_level3_en << 12 | + pdata->trigger_level2_en << 8 | + pdata->trigger_level1_en << 4 | + pdata->trigger_level0_en; + } else { + con |= EXYNOS4_TMU_CORE_OFF; + interrupt_en = 0; /* Disable all interrupts */ + } + writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN); + writel(con, data->base + EXYNOS4_TMU_REG_CONTROL); + + clk_disable(data->clk); + mutex_unlock(&data->lock); +} + +static int exynos4_tmu_read(struct exynos4_tmu_data *data) +{ + u8 temp_code; + int temp; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP); + temp = code_to_temp(data, temp_code); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return temp; +} + +static void exynos4_tmu_work(struct work_struct *work) +{ + struct exynos4_tmu_data *data = container_of(work, + struct exynos4_tmu_data, irq_work); + + mutex_lock(&data->lock); + clk_enable(data->clk); + + writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR); + + kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); + + enable_irq(data->irq); + + clk_disable(data->clk); + mutex_unlock(&data->lock); +} + +static irqreturn_t exynos4_tmu_irq(int irq, void *id) +{ + struct exynos4_tmu_data *data = id; + + disable_irq_nosync(irq); + schedule_work(&data->irq_work); + + return IRQ_HANDLED; +} + +static ssize_t exynos4_tmu_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "exynos4-tmu\n"); +} + +static ssize_t exynos4_tmu_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos4_tmu_data *data = dev_get_drvdata(dev); + int ret; + + ret = exynos4_tmu_read(data); + if (ret < 0) + return ret; + + /* convert from degree Celsius to millidegree Celsius */ + return sprintf(buf, "%d\n", ret * 1000); +} + +static ssize_t exynos4_tmu_show_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct exynos4_tmu_data *data = dev_get_drvdata(dev); + struct exynos4_tmu_platform_data *pdata = data->pdata; + int temp; + unsigned int trigger_level; + + temp = exynos4_tmu_read(data); + if (temp < 0) + return temp; + + trigger_level = pdata->threshold + pdata->trigger_levels[attr->index]; + + return sprintf(buf, "%d\n", !!(temp > trigger_level)); +} + +static ssize_t exynos4_tmu_show_level(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct exynos4_tmu_data *data = dev_get_drvdata(dev); + struct exynos4_tmu_platform_data *pdata = data->pdata; + unsigned int temp = pdata->threshold + + pdata->trigger_levels[attr->index]; + + return sprintf(buf, "%u\n", temp * 1000); +} + +static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, + exynos4_tmu_show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, + exynos4_tmu_show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, + exynos4_tmu_show_alarm, NULL, 3); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2); +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO, + exynos4_tmu_show_level, NULL, 3); + +static struct attribute *exynos4_tmu_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_temp1_input.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_emergency_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group exynos4_tmu_attr_group = { + .attrs = exynos4_tmu_attributes, +}; + +static int __devinit exynos4_tmu_probe(struct platform_device *pdev) +{ + struct exynos4_tmu_data *data; + struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); + return -ENOMEM; + } + + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) { + ret = data->irq; + dev_err(&pdev->dev, "Failed to get platform irq\n"); + goto err_free; + } + + INIT_WORK(&data->irq_work, exynos4_tmu_work); + + data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!data->mem) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get platform resource\n"); + goto err_free; + } + + data->mem = request_mem_region(data->mem->start, + resource_size(data->mem), pdev->name); + if (!data->mem) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to request memory region\n"); + goto err_free; + } + + data->base = ioremap(data->mem->start, resource_size(data->mem)); + if (!data->base) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to ioremap memory\n"); + goto err_mem_region; + } + + ret = request_irq(data->irq, exynos4_tmu_irq, + IRQF_TRIGGER_RISING, + "exynos4-tmu", data); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); + goto err_io_remap; + } + + data->clk = clk_get(NULL, "tmu_apbif"); + if (IS_ERR(data->clk)) { + ret = PTR_ERR(data->clk); + dev_err(&pdev->dev, "Failed to get clock\n"); + goto err_irq; + } + + data->pdata = pdata; + platform_set_drvdata(pdev, data); + mutex_init(&data->lock); + + ret = exynos4_tmu_initialize(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize TMU\n"); + goto err_clk; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs group\n"); + goto err_clk; + } + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + dev_err(&pdev->dev, "Failed to register hwmon device\n"); + goto err_create_group; + } + + exynos4_tmu_control(pdev, true); + + return 0; + +err_create_group: + sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); +err_clk: + platform_set_drvdata(pdev, NULL); + clk_put(data->clk); +err_irq: + free_irq(data->irq, data); +err_io_remap: + iounmap(data->base); +err_mem_region: + release_mem_region(data->mem->start, resource_size(data->mem)); +err_free: + kfree(data); + + return ret; +} + +static int __devexit exynos4_tmu_remove(struct platform_device *pdev) +{ + struct exynos4_tmu_data *data = platform_get_drvdata(pdev); + + exynos4_tmu_control(pdev, false); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); + + clk_put(data->clk); + + free_irq(data->irq, data); + + iounmap(data->base); + release_mem_region(data->mem->start, resource_size(data->mem)); + + platform_set_drvdata(pdev, NULL); + + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state) +{ + exynos4_tmu_control(pdev, false); + + return 0; +} + +static int exynos4_tmu_resume(struct platform_device *pdev) +{ + exynos4_tmu_initialize(pdev); + exynos4_tmu_control(pdev, true); + + return 0; +} +#else +#define exynos4_tmu_suspend NULL +#define exynos4_tmu_resume NULL +#endif + +static struct platform_driver exynos4_tmu_driver = { + .driver = { + .name = "exynos4-tmu", + .owner = THIS_MODULE, + }, + .probe = exynos4_tmu_probe, + .remove = __devexit_p(exynos4_tmu_remove), + .suspend = exynos4_tmu_suspend, + .resume = exynos4_tmu_resume, +}; + +static int __init exynos4_tmu_driver_init(void) +{ + return platform_driver_register(&exynos4_tmu_driver); +} +module_init(exynos4_tmu_driver_init); + +static void __exit exynos4_tmu_driver_exit(void) +{ + platform_driver_unregister(&exynos4_tmu_driver); +} +module_exit(exynos4_tmu_driver_exit); + +MODULE_DESCRIPTION("EXYNOS4 TMU Driver"); +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:exynos4-tmu"); diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 2d96ed2bf8ed..59dd881c71d8 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -605,7 +605,7 @@ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { /* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the standard models */ -static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { +static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), @@ -627,7 +627,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { 0, 0), SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), @@ -649,7 +649,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { 0, 1), SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - +}, { SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 2), @@ -671,12 +671,12 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { 0, 2), SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}; +} }; /* PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the pwm setting when the temperature is above the pwmX_auto_point1_temp can be programmed instead of being hardcoded to 0xff */ -static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = { +static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), @@ -701,7 +701,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = { 0, 0), SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), @@ -726,7 +726,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = { 0, 1), SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - +}, { SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 2), @@ -751,7 +751,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = { 0, 2), SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}; +} }; /* PWM attr for the standard models */ static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { @@ -928,7 +928,7 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = { /* PWM attr for the f8000, zones mapped to temp instead of to pwm! Also the register block at offset A0 maps to TEMP1 (so our temp2, as the F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ -static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { +static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), @@ -969,7 +969,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 2), SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), - +}, { SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), @@ -1010,7 +1010,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 0), SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 2), @@ -1051,7 +1051,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 1), SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), -}; +} }; /* Super I/O functions */ static inline int superio_inb(int base, int reg) @@ -2154,6 +2154,104 @@ static void f71882fg_remove_sysfs_files(struct platform_device *pdev, device_remove_file(&pdev->dev, &attr[i].dev_attr); } +static int __devinit f71882fg_create_fan_sysfs_files( + struct platform_device *pdev, int idx) +{ + struct f71882fg_data *data = platform_get_drvdata(pdev); + int err; + + /* Sanity check the pwm setting */ + err = 0; + switch (data->type) { + case f71858fg: + if (((data->pwm_enable >> (idx * 2)) & 3) == 3) + err = 1; + break; + case f71862fg: + if (((data->pwm_enable >> (idx * 2)) & 1) != 1) + err = 1; + break; + case f8000: + if (idx == 2) + err = data->pwm_enable & 0x20; + break; + default: + break; + } + if (err) { + dev_err(&pdev->dev, + "Invalid (reserved) pwm settings: 0x%02x, " + "skipping fan %d\n", + (data->pwm_enable >> (idx * 2)) & 3, idx + 1); + return 0; /* This is a non fatal condition */ + } + + err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[idx][0], + ARRAY_SIZE(fxxxx_fan_attr[0])); + if (err) + return err; + + if (f71882fg_fan_has_beep[data->type]) { + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_fan_beep_attr[idx], + 1); + if (err) + return err; + } + + dev_info(&pdev->dev, "Fan: %d is in %s mode\n", idx + 1, + (data->pwm_enable & (1 << (2 * idx))) ? "duty-cycle" : "RPM"); + + /* Check for unsupported auto pwm settings */ + switch (data->type) { + case f71808e: + case f71808a: + case f71869: + case f71869a: + case f71889fg: + case f71889ed: + case f71889a: + data->pwm_auto_point_mapping[idx] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(idx)); + if ((data->pwm_auto_point_mapping[idx] & 0x80) || + (data->pwm_auto_point_mapping[idx] & 3) == 0) { + dev_warn(&pdev->dev, + "Auto pwm controlled by raw digital " + "data, disabling pwm auto_point " + "sysfs attributes for fan %d\n", idx + 1); + return 0; /* This is a non fatal condition */ + } + break; + default: + break; + } + + switch (data->type) { + case f71862fg: + err = f71882fg_create_sysfs_files(pdev, + &f71862fg_auto_pwm_attr[idx][0], + ARRAY_SIZE(f71862fg_auto_pwm_attr[0])); + break; + case f71808e: + case f71869: + err = f71882fg_create_sysfs_files(pdev, + &f71869_auto_pwm_attr[idx][0], + ARRAY_SIZE(f71869_auto_pwm_attr[0])); + break; + case f8000: + err = f71882fg_create_sysfs_files(pdev, + &f8000_auto_pwm_attr[idx][0], + ARRAY_SIZE(f8000_auto_pwm_attr[0])); + break; + default: + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[idx][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0])); + } + + return err; +} + static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; @@ -2272,117 +2370,29 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - /* Sanity check the pwm settings */ - switch (data->type) { - case f71858fg: - err = 0; - for (i = 0; i < nr_fans; i++) - if (((data->pwm_enable >> (i * 2)) & 3) == 3) - err = 1; - break; - case f71862fg: - err = (data->pwm_enable & 0x15) != 0x15; - break; - case f8000: - err = data->pwm_enable & 0x20; - break; - default: - err = 0; - break; - } - if (err) { - dev_err(&pdev->dev, - "Invalid (reserved) pwm settings: 0x%02x\n", - (unsigned int)data->pwm_enable); - err = -ENODEV; - goto exit_unregister_sysfs; - } - - err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0], - ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - if (err) - goto exit_unregister_sysfs; - - if (f71882fg_fan_has_beep[data->type]) { - err = f71882fg_create_sysfs_files(pdev, - fxxxx_fan_beep_attr, nr_fans); + for (i = 0; i < nr_fans; i++) { + err = f71882fg_create_fan_sysfs_files(pdev, i); if (err) goto exit_unregister_sysfs; } - switch (data->type) { - case f71808e: - case f71808a: - case f71869: - case f71869a: - case f71889fg: - case f71889ed: - case f71889a: - for (i = 0; i < nr_fans; i++) { - data->pwm_auto_point_mapping[i] = - f71882fg_read8(data, - F71882FG_REG_POINT_MAPPING(i)); - if ((data->pwm_auto_point_mapping[i] & 0x80) || - (data->pwm_auto_point_mapping[i] & 3) == 0) - break; - } - if (i != nr_fans) { - dev_warn(&pdev->dev, - "Auto pwm controlled by raw digital " - "data, disabling pwm auto_point " - "sysfs attributes\n"); - goto no_pwm_auto_point; - } - break; - default: - break; - } - + /* Some types have 1 extra fan with limited functionality */ switch (data->type) { case f71808a: err = f71882fg_create_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); - if (err) - goto exit_unregister_sysfs; - err = f71882fg_create_sysfs_files(pdev, f71808a_fan3_attr, ARRAY_SIZE(f71808a_fan3_attr)); break; - case f71862fg: - err = f71882fg_create_sysfs_files(pdev, - f71862fg_auto_pwm_attr, - ARRAY_SIZE(f71862fg_auto_pwm_attr)); - break; - case f71808e: - case f71869: - err = f71882fg_create_sysfs_files(pdev, - f71869_auto_pwm_attr, - ARRAY_SIZE(f71869_auto_pwm_attr)); - break; case f8000: err = f71882fg_create_sysfs_files(pdev, f8000_fan_attr, ARRAY_SIZE(f8000_fan_attr)); - if (err) - goto exit_unregister_sysfs; - err = f71882fg_create_sysfs_files(pdev, - f8000_auto_pwm_attr, - ARRAY_SIZE(f8000_auto_pwm_attr)); break; default: - err = f71882fg_create_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + break; } if (err) goto exit_unregister_sysfs; - -no_pwm_auto_point: - for (i = 0; i < nr_fans; i++) - dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1, - (data->pwm_enable & (1 << 2 * i)) ? - "duty-cycle" : "RPM"); } data->hwmon_dev = hwmon_device_register(&pdev->dev); @@ -2476,22 +2486,23 @@ static int f71882fg_remove(struct platform_device *pdev) break; case f71862fg: f71882fg_remove_sysfs_files(pdev, - f71862fg_auto_pwm_attr, - ARRAY_SIZE(f71862fg_auto_pwm_attr)); + &f71862fg_auto_pwm_attr[0][0], + ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * + nr_fans); break; case f71808e: case f71869: f71882fg_remove_sysfs_files(pdev, - f71869_auto_pwm_attr, - ARRAY_SIZE(f71869_auto_pwm_attr)); + &f71869_auto_pwm_attr[0][0], + ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); break; case f8000: f71882fg_remove_sysfs_files(pdev, f8000_fan_attr, ARRAY_SIZE(f8000_fan_attr)); f71882fg_remove_sysfs_files(pdev, - f8000_auto_pwm_attr, - ARRAY_SIZE(f8000_auto_pwm_attr)); + &f8000_auto_pwm_attr[0][0], + ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); break; default: f71882fg_remove_sysfs_files(pdev, diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index e7ae5743e181..a13e2da97e30 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -591,7 +591,7 @@ static int gl518_remove(struct i2c_client *client) static int gl518_read_value(struct i2c_client *client, u8 reg) { if ((reg >= 0x07) && (reg <= 0x0c)) - return swab16(i2c_smbus_read_word_data(client, reg)); + return i2c_smbus_read_word_swapped(client, reg); else return i2c_smbus_read_byte_data(client, reg); } @@ -599,7 +599,7 @@ static int gl518_read_value(struct i2c_client *client, u8 reg) static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) { if ((reg >= 0x07) && (reg <= 0x0c)) - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); else return i2c_smbus_write_byte_data(client, reg, value); } diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 131ea8625f08..cd6085bbfba7 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -821,7 +821,7 @@ static int gl520_remove(struct i2c_client *client) static int gl520_read_value(struct i2c_client *client, u8 reg) { if ((reg >= 0x07) && (reg <= 0x0c)) - return swab16(i2c_smbus_read_word_data(client, reg)); + return i2c_smbus_read_word_swapped(client, reg); else return i2c_smbus_read_byte_data(client, reg); } @@ -829,7 +829,7 @@ static int gl520_read_value(struct i2c_client *client, u8 reg) static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) { if ((reg >= 0x07) && (reg <= 0x0c)) - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); else return i2c_smbus_write_byte_data(client, reg, value); } diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a61e7815a2a9..6460487e41b5 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -27,8 +27,7 @@ static struct class *hwmon_class; -static DEFINE_IDR(hwmon_idr); -static DEFINE_SPINLOCK(idr_lock); +static DEFINE_IDA(hwmon_ida); /** * hwmon_device_register - register w/ hwmon @@ -42,30 +41,17 @@ static DEFINE_SPINLOCK(idr_lock); struct device *hwmon_device_register(struct device *dev) { struct device *hwdev; - int id, err; - -again: - if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) - return ERR_PTR(-ENOMEM); - - spin_lock(&idr_lock); - err = idr_get_new(&hwmon_idr, NULL, &id); - spin_unlock(&idr_lock); + int id; - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return ERR_PTR(err); + id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); - id = id & MAX_ID_MASK; hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, HWMON_ID_FORMAT, id); - if (IS_ERR(hwdev)) { - spin_lock(&idr_lock); - idr_remove(&hwmon_idr, id); - spin_unlock(&idr_lock); - } + if (IS_ERR(hwdev)) + ida_simple_remove(&hwmon_ida, id); return hwdev; } @@ -81,9 +67,7 @@ void hwmon_device_unregister(struct device *dev) if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); - spin_lock(&idr_lock); - idr_remove(&hwmon_idr, id); - spin_unlock(&idr_lock); + ida_simple_remove(&hwmon_ida, id); } else dev_dbg(dev->parent, "hwmon_device_unregister() failed: bad class ID!\n"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index c316294c48b4..6a967d7dbdee 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -88,8 +88,7 @@ #define AEM_MIN_POWER_INTERVAL 200 #define UJ_PER_MJ 1000L -static DEFINE_IDR(aem_idr); -static DEFINE_SPINLOCK(aem_idr_lock); +static DEFINE_IDA(aem_ida); static struct platform_driver aem_driver = { .driver = { @@ -148,8 +147,9 @@ struct aem_data { int id; struct aem_ipmi_data ipmi; - /* Function to update sensors */ + /* Function and buffer to update sensors */ void (*update)(struct aem_data *data); + struct aem_read_sensor_resp *rs_resp; /* * AEM 1.x sensors: @@ -246,8 +246,6 @@ static void aem_bmc_gone(int iface); static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); static void aem_remove_sensors(struct aem_data *data); -static int aem_init_aem1(struct aem_ipmi_data *probe); -static int aem_init_aem2(struct aem_ipmi_data *probe); static int aem1_find_sensors(struct aem_data *data); static int aem2_find_sensors(struct aem_data *data); static void update_aem1_sensors(struct aem_data *data); @@ -356,47 +354,16 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) complete(&data->read_complete); } -/* ID functions */ - -/* Obtain an id */ -static int aem_idr_get(int *id) -{ - int i, err; - -again: - if (unlikely(!idr_pre_get(&aem_idr, GFP_KERNEL))) - return -ENOMEM; - - spin_lock(&aem_idr_lock); - err = idr_get_new(&aem_idr, NULL, &i); - spin_unlock(&aem_idr_lock); - - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return err; - - *id = i & MAX_ID_MASK; - return 0; -} - -/* Release an object ID */ -static void aem_idr_put(int id) -{ - spin_lock(&aem_idr_lock); - idr_remove(&aem_idr, id); - spin_unlock(&aem_idr_lock); -} - /* Sensor support functions */ -/* Read a sensor value */ +/* Read a sensor value; must be called with data->lock held */ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, void *buf, size_t size) { int rs_size, res; struct aem_read_sensor_req rs_req; - struct aem_read_sensor_resp *rs_resp; + /* Use preallocated rx buffer */ + struct aem_read_sensor_resp *rs_resp = data->rs_resp; struct aem_ipmi_data *ipmi = &data->ipmi; /* AEM registers are 1, 2, 4 or 8 bytes */ @@ -422,10 +389,6 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, ipmi->tx_message.data_len = sizeof(rs_req); rs_size = sizeof(*rs_resp) + size; - rs_resp = kzalloc(rs_size, GFP_KERNEL); - if (!rs_resp) - return -ENOMEM; - ipmi->rx_msg_data = rs_resp; ipmi->rx_msg_len = rs_size; @@ -468,7 +431,6 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, res = 0; out: - kfree(rs_resp); return res; } @@ -526,11 +488,12 @@ static void aem_delete(struct aem_data *data) { list_del(&data->list); aem_remove_sensors(data); + kfree(data->rs_resp); hwmon_device_unregister(data->hwmon_dev); ipmi_destroy_user(data->ipmi.user); platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); kfree(data); } @@ -587,7 +550,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - if (aem_idr_get(&data->id)) + data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + if (data->id < 0) goto id_err; data->pdev = platform_device_alloc(DRVNAME, data->id); @@ -602,24 +566,31 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) platform_set_drvdata(data->pdev, data); /* Set up IPMI interface */ - if (aem_init_ipmi_data(&data->ipmi, probe->interface, - probe->bmc_device)) + res = aem_init_ipmi_data(&data->ipmi, probe->interface, + probe->bmc_device); + if (res) goto ipmi_err; /* Register with hwmon */ data->hwmon_dev = hwmon_device_register(&data->pdev->dev); - if (IS_ERR(data->hwmon_dev)) { dev_err(&data->pdev->dev, "Unable to register hwmon " "device for IPMI interface %d\n", probe->interface); + res = PTR_ERR(data->hwmon_dev); goto hwmon_reg_err; } data->update = update_aem1_sensors; + data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL); + if (!data->rs_resp) { + res = -ENOMEM; + goto alloc_resp_err; + } /* Find sensors */ - if (aem1_find_sensors(data)) + res = aem1_find_sensors(data); + if (res) goto sensor_err; /* Add to our list of AEM devices */ @@ -631,6 +602,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) return 0; sensor_err: + kfree(data->rs_resp); +alloc_resp_err: hwmon_device_unregister(data->hwmon_dev); hwmon_reg_err: ipmi_destroy_user(data->ipmi.user); @@ -638,7 +611,7 @@ ipmi_err: platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); dev_err: - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); id_err: kfree(data); @@ -646,7 +619,7 @@ id_err: } /* Find and initialize all AEM1 instances */ -static int aem_init_aem1(struct aem_ipmi_data *probe) +static void aem_init_aem1(struct aem_ipmi_data *probe) { int num, i, err; @@ -657,11 +630,8 @@ static int aem_init_aem1(struct aem_ipmi_data *probe) dev_err(probe->bmc_device, "Error %d initializing AEM1 0x%X\n", err, i); - return err; } } - - return 0; } /* Probe functions for AEM2 devices */ @@ -720,7 +690,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - if (aem_idr_get(&data->id)) + data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + if (data->id < 0) goto id_err; data->pdev = platform_device_alloc(DRVNAME, data->id); @@ -735,24 +706,31 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, platform_set_drvdata(data->pdev, data); /* Set up IPMI interface */ - if (aem_init_ipmi_data(&data->ipmi, probe->interface, - probe->bmc_device)) + res = aem_init_ipmi_data(&data->ipmi, probe->interface, + probe->bmc_device); + if (res) goto ipmi_err; /* Register with hwmon */ data->hwmon_dev = hwmon_device_register(&data->pdev->dev); - if (IS_ERR(data->hwmon_dev)) { dev_err(&data->pdev->dev, "Unable to register hwmon " "device for IPMI interface %d\n", probe->interface); + res = PTR_ERR(data->hwmon_dev); goto hwmon_reg_err; } data->update = update_aem2_sensors; + data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL); + if (!data->rs_resp) { + res = -ENOMEM; + goto alloc_resp_err; + } /* Find sensors */ - if (aem2_find_sensors(data)) + res = aem2_find_sensors(data); + if (res) goto sensor_err; /* Add to our list of AEM devices */ @@ -764,6 +742,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, return 0; sensor_err: + kfree(data->rs_resp); +alloc_resp_err: hwmon_device_unregister(data->hwmon_dev); hwmon_reg_err: ipmi_destroy_user(data->ipmi.user); @@ -771,7 +751,7 @@ ipmi_err: platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); dev_err: - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); id_err: kfree(data); @@ -779,7 +759,7 @@ id_err: } /* Find and initialize all AEM2 instances */ -static int aem_init_aem2(struct aem_ipmi_data *probe) +static void aem_init_aem2(struct aem_ipmi_data *probe) { struct aem_find_instance_resp fi_resp; int err; @@ -798,12 +778,9 @@ static int aem_init_aem2(struct aem_ipmi_data *probe) dev_err(probe->bmc_device, "Error %d initializing AEM2 0x%X\n", err, fi_resp.module_handle); - return err; } i++; } - - return 0; } /* Probe a BMC for AEM firmware instances */ diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 02cebb74e206..2d3d72805ff4 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -154,8 +154,6 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id); static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info); static int jc42_remove(struct i2c_client *client); -static int jc42_read_value(struct i2c_client *client, u8 reg); -static int jc42_write_value(struct i2c_client *client, u8 reg, u16 value); static struct jc42_data *jc42_update_device(struct device *dev); @@ -187,7 +185,7 @@ static int jc42_suspend(struct device *dev) struct jc42_data *data = i2c_get_clientdata(client); data->config |= JC42_CFG_SHUTDOWN; - jc42_write_value(client, JC42_REG_CONFIG, data->config); + i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config); return 0; } @@ -197,7 +195,7 @@ static int jc42_resume(struct device *dev) struct jc42_data *data = i2c_get_clientdata(client); data->config &= ~JC42_CFG_SHUTDOWN; - jc42_write_value(client, JC42_REG_CONFIG, data->config); + i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config); return 0; } @@ -315,7 +313,7 @@ static ssize_t set_##value(struct device *dev, \ return -EINVAL; \ mutex_lock(&data->update_lock); \ data->value = jc42_temp_to_reg(val, data->extended); \ - err = jc42_write_value(client, reg, data->value); \ + err = i2c_smbus_write_word_swapped(client, reg, data->value); \ if (err < 0) \ ret = err; \ mutex_unlock(&data->update_lock); \ @@ -357,7 +355,8 @@ static ssize_t set_temp_crit_hyst(struct device *dev, data->config = (data->config & ~(JC42_CFG_HYST_MASK << JC42_CFG_HYST_SHIFT)) | (hyst << JC42_CFG_HYST_SHIFT); - err = jc42_write_value(client, JC42_REG_CONFIG, data->config); + err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, + data->config); if (err < 0) ret = err; mutex_unlock(&data->update_lock); @@ -452,10 +451,10 @@ static int jc42_detect(struct i2c_client *new_client, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - cap = jc42_read_value(new_client, JC42_REG_CAP); - config = jc42_read_value(new_client, JC42_REG_CONFIG); - manid = jc42_read_value(new_client, JC42_REG_MANID); - devid = jc42_read_value(new_client, JC42_REG_DEVICEID); + cap = i2c_smbus_read_word_swapped(new_client, JC42_REG_CAP); + config = i2c_smbus_read_word_swapped(new_client, JC42_REG_CONFIG); + manid = i2c_smbus_read_word_swapped(new_client, JC42_REG_MANID); + devid = i2c_smbus_read_word_swapped(new_client, JC42_REG_DEVICEID); if (cap < 0 || config < 0 || manid < 0 || devid < 0) return -ENODEV; @@ -489,14 +488,14 @@ static int jc42_probe(struct i2c_client *new_client, i2c_set_clientdata(new_client, data); mutex_init(&data->update_lock); - cap = jc42_read_value(new_client, JC42_REG_CAP); + cap = i2c_smbus_read_word_swapped(new_client, JC42_REG_CAP); if (cap < 0) { err = -EINVAL; goto exit_free; } data->extended = !!(cap & JC42_CAP_RANGE); - config = jc42_read_value(new_client, JC42_REG_CONFIG); + config = i2c_smbus_read_word_swapped(new_client, JC42_REG_CONFIG); if (config < 0) { err = -EINVAL; goto exit_free; @@ -504,7 +503,8 @@ static int jc42_probe(struct i2c_client *new_client, data->orig_config = config; if (config & JC42_CFG_SHUTDOWN) { config &= ~JC42_CFG_SHUTDOWN; - jc42_write_value(new_client, JC42_REG_CONFIG, config); + i2c_smbus_write_word_swapped(new_client, JC42_REG_CONFIG, + config); } data->config = config; @@ -535,25 +535,12 @@ static int jc42_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &jc42_group); if (data->config != data->orig_config) - jc42_write_value(client, JC42_REG_CONFIG, data->orig_config); + i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, + data->orig_config); kfree(data); return 0; } -/* All registers are word-sized. */ -static int jc42_read_value(struct i2c_client *client, u8 reg) -{ - int ret = i2c_smbus_read_word_data(client, reg); - if (ret < 0) - return ret; - return swab16(ret); -} - -static int jc42_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - static struct jc42_data *jc42_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -564,28 +551,29 @@ static struct jc42_data *jc42_update_device(struct device *dev) mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - val = jc42_read_value(client, JC42_REG_TEMP); + val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->temp_input = val; - val = jc42_read_value(client, JC42_REG_TEMP_CRITICAL); + val = i2c_smbus_read_word_swapped(client, + JC42_REG_TEMP_CRITICAL); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->temp_crit = val; - val = jc42_read_value(client, JC42_REG_TEMP_LOWER); + val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_LOWER); if (val < 0) { ret = ERR_PTR(val); goto abort; } data->temp_min = val; - val = jc42_read_value(client, JC42_REG_TEMP_UPPER); + val = i2c_smbus_read_word_swapped(client, JC42_REG_TEMP_UPPER); if (val < 0) { ret = ERR_PTR(val); goto abort; diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 29b9030d42c3..9e64d96620d3 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -34,7 +34,7 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, #define LM73_REG_CTRL 0x04 #define LM73_REG_ID 0x07 -#define LM73_ID 0x9001 /* or 0x190 after a swab16() */ +#define LM73_ID 0x9001 /* 0x0190, byte-swapped */ #define DRVNAME "lm73" #define LM73_TEMP_MIN (-40) #define LM73_TEMP_MAX 150 @@ -57,7 +57,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, /* Write value */ value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4), (LM73_TEMP_MAX*4)) << 5; - i2c_smbus_write_word_data(client, attr->index, swab16(value)); + i2c_smbus_write_word_swapped(client, attr->index, value); return count; } @@ -68,8 +68,8 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da, struct i2c_client *client = to_i2c_client(dev); /* use integer division instead of equivalent right shift to guarantee arithmetic shift and preserve the sign */ - int temp = ((s16) (swab16(i2c_smbus_read_word_data(client, - attr->index)))*250) / 32; + int temp = ((s16) (i2c_smbus_read_word_swapped(client, + attr->index))*250) / 32; return sprintf(buf, "%d\n", temp); } @@ -150,17 +150,31 @@ static int lm73_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - u16 id; - u8 ctrl; + int id, ctrl, conf; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; + /* + * Do as much detection as possible with byte reads first, as word + * reads can confuse other devices. + */ + ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL); + if (ctrl < 0 || (ctrl & 0x10)) + return -ENODEV; + + conf = i2c_smbus_read_byte_data(new_client, LM73_REG_CONF); + if (conf < 0 || (conf & 0x0c)) + return -ENODEV; + + id = i2c_smbus_read_byte_data(new_client, LM73_REG_ID); + if (id < 0 || id != (LM73_ID & 0xff)) + return -ENODEV; + /* Check device ID */ id = i2c_smbus_read_word_data(new_client, LM73_REG_ID); - ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL); - if ((id != LM73_ID) || (ctrl & 0x10)) + if (id < 0 || id != LM73_ID) return -ENODEV; strlcpy(info->type, "lm73", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index ef902d5d06ab..1888dd0fc05f 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -35,6 +35,7 @@ */ enum lm75_type { /* keep sorted in alphabetical order */ + adt75, ds1775, ds75, lm75, @@ -213,6 +214,7 @@ static int lm75_remove(struct i2c_client *client) } static const struct i2c_device_id lm75_ids[] = { + { "adt75", adt75, }, { "ds1775", ds1775, }, { "ds75", ds75, }, { "lm75", lm75, }, @@ -247,19 +249,30 @@ static int lm75_detect(struct i2c_client *new_client, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - /* Now, we do the remaining detection. There is no identification- - dedicated register so we have to rely on several tricks: - unused bits, registers cycling over 8-address boundaries, - addresses 0x04-0x07 returning the last read value. - The cycling+unused addresses combination is not tested, - since it would significantly slow the detection down and would - hardly add any value. - - The National Semiconductor LM75A is different than earlier - LM75s. It has an ID byte of 0xaX (where X is the chip - revision, with 1 being the only revision in existence) in - register 7, and unused registers return 0xff rather than the - last read value. */ + /* + * Now, we do the remaining detection. There is no identification- + * dedicated register so we have to rely on several tricks: + * unused bits, registers cycling over 8-address boundaries, + * addresses 0x04-0x07 returning the last read value. + * The cycling+unused addresses combination is not tested, + * since it would significantly slow the detection down and would + * hardly add any value. + * + * The National Semiconductor LM75A is different than earlier + * LM75s. It has an ID byte of 0xaX (where X is the chip + * revision, with 1 being the only revision in existence) in + * register 7, and unused registers return 0xff rather than the + * last read value. + * + * Note that this function only detects the original National + * Semiconductor LM75 and the LM75A. Clones from other vendors + * aren't detected, on purpose, because they are typically never + * found on PC hardware. They are found on embedded designs where + * they can be instantiated explicitly so detection is not needed. + * The absence of identification registers on all these clones + * would make their exhaustive detection very difficult and weak, + * and odds are that the driver would bind to unsupported devices. + */ /* Unused bits */ conf = i2c_smbus_read_byte_data(new_client, 1); @@ -371,13 +384,10 @@ static struct i2c_driver lm75_driver = { */ static int lm75_read_value(struct i2c_client *client, u8 reg) { - int value; - if (reg == LM75_REG_CONF) return i2c_smbus_read_byte_data(client, reg); - - value = i2c_smbus_read_word_data(client, reg); - return (value < 0) ? value : swab16(value); + else + return i2c_smbus_read_word_swapped(client, reg); } static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) @@ -385,7 +395,7 @@ static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) if (reg == LM75_REG_CONF) return i2c_smbus_write_byte_data(client, reg, value); else - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); } static struct lm75_data *lm75_update_device(struct device *dev) diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index b28a297be50c..8dfc6782d596 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -365,7 +365,7 @@ static u16 lm77_read_value(struct i2c_client *client, u8 reg) if (reg == LM77_REG_CONF) return i2c_smbus_read_byte_data(client, reg); else - return swab16(i2c_smbus_read_word_data(client, reg)); + return i2c_smbus_read_word_swapped(client, reg); } static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value) @@ -373,7 +373,7 @@ static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value) if (reg == LM77_REG_CONF) return i2c_smbus_write_byte_data(client, reg, value); else - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); } static void lm77_init_client(struct i2c_client *client) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 90ddb8774210..615bc4f4e530 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1105,41 +1105,37 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm90_detect(struct i2c_client *new_client, +static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; - int address = new_client->addr; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *name = NULL; - int man_id, chip_id, reg_config1, reg_config2, reg_convrate; + int man_id, chip_id, config1, config2, convrate; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; /* detection and identification */ - if ((man_id = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_MAN_ID)) < 0 - || (chip_id = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CHIP_ID)) < 0 - || (reg_config1 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG1)) < 0 - || (reg_convrate = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONVRATE)) < 0) + man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID); + config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); + convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE); + if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) return -ENODEV; if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) { - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG2); - if (reg_config2 < 0) + config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2); + if (config2 < 0) return -ENODEV; } else - reg_config2 = 0; /* Make compiler happy */ + config2 = 0; /* Make compiler happy */ if ((address == 0x4C || address == 0x4D) && man_id == 0x01) { /* National Semiconductor */ - if ((reg_config1 & 0x2A) == 0x00 - && (reg_config2 & 0xF8) == 0x00 - && reg_convrate <= 0x09) { + if ((config1 & 0x2A) == 0x00 + && (config2 & 0xF8) == 0x00 + && convrate <= 0x09) { if (address == 0x4C && (chip_id & 0xF0) == 0x20) { /* LM90 */ name = "lm90"; @@ -1163,8 +1159,8 @@ static int lm90_detect(struct i2c_client *new_client, if ((address == 0x4C || address == 0x4D) && man_id == 0x41) { /* Analog Devices */ if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (reg_config1 & 0x3F) == 0x00 - && reg_convrate <= 0x0A) { + && (config1 & 0x3F) == 0x00 + && convrate <= 0x0A) { name = "adm1032"; /* The ADM1032 supports PEC, but only if combined transactions are not used. */ @@ -1173,18 +1169,18 @@ static int lm90_detect(struct i2c_client *new_client, info->flags |= I2C_CLIENT_PEC; } else if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1B) == 0x00 - && reg_convrate <= 0x0A) { + && (config1 & 0x1B) == 0x00 + && convrate <= 0x0A) { name = "adt7461"; } else if (chip_id == 0x57 /* ADT7461A, NCT1008 */ - && (reg_config1 & 0x1B) == 0x00 - && reg_convrate <= 0x0A) { + && (config1 & 0x1B) == 0x00 + && convrate <= 0x0A) { name = "adt7461a"; } } else if (man_id == 0x4D) { /* Maxim */ - int reg_emerg, reg_emerg2, reg_status2; + int emerg, emerg2, status2; /* * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read @@ -1192,13 +1188,15 @@ static int lm90_detect(struct i2c_client *new_client, * exists, both readings will reflect the same value. Otherwise, * the readings will be different. */ - if ((reg_emerg = i2c_smbus_read_byte_data(new_client, - MAX6659_REG_R_REMOTE_EMERG)) < 0 - || i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID) < 0 - || (reg_emerg2 = i2c_smbus_read_byte_data(new_client, - MAX6659_REG_R_REMOTE_EMERG)) < 0 - || (reg_status2 = i2c_smbus_read_byte_data(new_client, - MAX6696_REG_R_STATUS2)) < 0) + emerg = i2c_smbus_read_byte_data(client, + MAX6659_REG_R_REMOTE_EMERG); + man_id = i2c_smbus_read_byte_data(client, + LM90_REG_R_MAN_ID); + emerg2 = i2c_smbus_read_byte_data(client, + MAX6659_REG_R_REMOTE_EMERG); + status2 = i2c_smbus_read_byte_data(client, + MAX6696_REG_R_STATUS2); + if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0) return -ENODEV; /* @@ -1216,8 +1214,8 @@ static int lm90_detect(struct i2c_client *new_client, */ if (chip_id == man_id && (address == 0x4C || address == 0x4D || address == 0x4E) - && (reg_config1 & 0x1F) == (man_id & 0x0F) - && reg_convrate <= 0x09) { + && (config1 & 0x1F) == (man_id & 0x0F) + && convrate <= 0x09) { if (address == 0x4C) name = "max6657"; else @@ -1235,10 +1233,10 @@ static int lm90_detect(struct i2c_client *new_client, * one of those registers exists. */ if (chip_id == 0x01 - && (reg_config1 & 0x10) == 0x00 - && (reg_status2 & 0x01) == 0x00 - && reg_emerg == reg_emerg2 - && reg_convrate <= 0x07) { + && (config1 & 0x10) == 0x00 + && (status2 & 0x01) == 0x00 + && emerg == emerg2 + && convrate <= 0x07) { name = "max6696"; } else /* @@ -1248,8 +1246,8 @@ static int lm90_detect(struct i2c_client *new_client, * second to last bit of config1 (software reset). */ if (chip_id == 0x01 - && (reg_config1 & 0x03) == 0x00 - && reg_convrate <= 0x07) { + && (config1 & 0x03) == 0x00 + && convrate <= 0x07) { name = "max6680"; } else /* @@ -1258,21 +1256,21 @@ static int lm90_detect(struct i2c_client *new_client, * register are unused and should return zero when read. */ if (chip_id == 0x59 - && (reg_config1 & 0x3f) == 0x00 - && reg_convrate <= 0x07) { + && (config1 & 0x3f) == 0x00 + && convrate <= 0x07) { name = "max6646"; } } else if (address == 0x4C && man_id == 0x5C) { /* Winbond/Nuvoton */ - if ((reg_config1 & 0x2A) == 0x00 - && (reg_config2 & 0xF8) == 0x00) { + if ((config1 & 0x2A) == 0x00 + && (config2 & 0xF8) == 0x00) { if (chip_id == 0x01 /* W83L771W/G */ - && reg_convrate <= 0x09) { + && convrate <= 0x09) { name = "w83l771"; } else if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ - && reg_convrate <= 0x08) { + && convrate <= 0x08) { name = "w83l771"; } } @@ -1280,9 +1278,9 @@ static int lm90_detect(struct i2c_client *new_client, if (address >= 0x48 && address <= 0x4F && man_id == 0xA1) { /* NXP Semiconductor/Philips */ if (chip_id == 0x00 - && (reg_config1 & 0x2A) == 0x00 - && (reg_config2 & 0xFE) == 0x00 - && reg_convrate <= 0x09) { + && (config1 & 0x2A) == 0x00 + && (config2 & 0xFE) == 0x00 + && convrate <= 0x09) { name = "sa56004"; } } @@ -1301,19 +1299,18 @@ static int lm90_detect(struct i2c_client *new_client, static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) { + struct device *dev = &client->dev; + if (data->flags & LM90_HAVE_TEMP3) - sysfs_remove_group(&client->dev.kobj, &lm90_temp3_group); + sysfs_remove_group(&dev->kobj, &lm90_temp3_group); if (data->flags & LM90_HAVE_EMERGENCY_ALARM) - sysfs_remove_group(&client->dev.kobj, - &lm90_emergency_alarm_group); + sysfs_remove_group(&dev->kobj, &lm90_emergency_alarm_group); if (data->flags & LM90_HAVE_EMERGENCY) - sysfs_remove_group(&client->dev.kobj, - &lm90_emergency_group); + sysfs_remove_group(&dev->kobj, &lm90_emergency_group); if (data->flags & LM90_HAVE_OFFSET) - device_remove_file(&client->dev, - &sensor_dev_attr_temp2_offset.dev_attr); - device_remove_file(&client->dev, &dev_attr_pec); - sysfs_remove_group(&client->dev.kobj, &lm90_group); + device_remove_file(dev, &sensor_dev_attr_temp2_offset.dev_attr); + device_remove_file(dev, &dev_attr_pec); + sysfs_remove_group(&dev->kobj, &lm90_group); } static void lm90_init_client(struct i2c_client *client) @@ -1362,10 +1359,11 @@ static void lm90_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); } -static int lm90_probe(struct i2c_client *new_client, +static int lm90_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = to_i2c_adapter(new_client->dev.parent); + struct device *dev = &client->dev; + struct i2c_adapter *adapter = to_i2c_adapter(dev->parent); struct lm90_data *data; int err; @@ -1374,14 +1372,14 @@ static int lm90_probe(struct i2c_client *new_client, err = -ENOMEM; goto exit; } - i2c_set_clientdata(new_client, data); + i2c_set_clientdata(client, data); mutex_init(&data->update_lock); /* Set the device type */ data->kind = id->driver_data; if (data->kind == adm1032) { if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - new_client->flags &= ~I2C_CLIENT_PEC; + client->flags &= ~I2C_CLIENT_PEC; } /* Different devices have different alarm bits triggering the @@ -1396,43 +1394,41 @@ static int lm90_probe(struct i2c_client *new_client, data->max_convrate = lm90_params[data->kind].max_convrate; /* Initialize the LM90 chip */ - lm90_init_client(new_client); + lm90_init_client(client); /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm90_group); + err = sysfs_create_group(&dev->kobj, &lm90_group); if (err) goto exit_free; - if (new_client->flags & I2C_CLIENT_PEC) { - err = device_create_file(&new_client->dev, &dev_attr_pec); + if (client->flags & I2C_CLIENT_PEC) { + err = device_create_file(dev, &dev_attr_pec); if (err) goto exit_remove_files; } if (data->flags & LM90_HAVE_OFFSET) { - err = device_create_file(&new_client->dev, + err = device_create_file(dev, &sensor_dev_attr_temp2_offset.dev_attr); if (err) goto exit_remove_files; } if (data->flags & LM90_HAVE_EMERGENCY) { - err = sysfs_create_group(&new_client->dev.kobj, - &lm90_emergency_group); + err = sysfs_create_group(&dev->kobj, &lm90_emergency_group); if (err) goto exit_remove_files; } if (data->flags & LM90_HAVE_EMERGENCY_ALARM) { - err = sysfs_create_group(&new_client->dev.kobj, + err = sysfs_create_group(&dev->kobj, &lm90_emergency_alarm_group); if (err) goto exit_remove_files; } if (data->flags & LM90_HAVE_TEMP3) { - err = sysfs_create_group(&new_client->dev.kobj, - &lm90_temp3_group); + err = sysfs_create_group(&dev->kobj, &lm90_temp3_group); if (err) goto exit_remove_files; } - data->hwmon_dev = hwmon_device_register(&new_client->dev); + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto exit_remove_files; @@ -1441,7 +1437,7 @@ static int lm90_probe(struct i2c_client *new_client, return 0; exit_remove_files: - lm90_remove_files(new_client, data); + lm90_remove_files(client, data); exit_free: kfree(data); exit: diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 7c31e6205f85..8fcbd4d422c5 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -117,16 +117,16 @@ static struct lm92_data *lm92_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { dev_dbg(&client->dev, "Updating lm92 data\n"); - data->temp1_input = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP)); - data->temp1_hyst = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HYST)); - data->temp1_crit = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_CRIT)); - data->temp1_min = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_LOW)); - data->temp1_max = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HIGH)); + data->temp1_input = i2c_smbus_read_word_swapped(client, + LM92_REG_TEMP); + data->temp1_hyst = i2c_smbus_read_word_swapped(client, + LM92_REG_TEMP_HYST); + data->temp1_crit = i2c_smbus_read_word_swapped(client, + LM92_REG_TEMP_CRIT); + data->temp1_min = i2c_smbus_read_word_swapped(client, + LM92_REG_TEMP_LOW); + data->temp1_max = i2c_smbus_read_word_swapped(client, + LM92_REG_TEMP_HIGH); data->last_updated = jiffies; data->valid = 1; @@ -158,7 +158,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co \ mutex_lock(&data->update_lock); \ data->value = TEMP_TO_REG(val); \ - i2c_smbus_write_word_data(client, reg, swab16(data->value)); \ + i2c_smbus_write_word_swapped(client, reg, data->value); \ mutex_unlock(&data->update_lock); \ return count; \ } @@ -194,8 +194,8 @@ static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute * mutex_lock(&data->update_lock); data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val; - i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST, - swab16(TEMP_TO_REG(data->temp1_hyst))); + i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST, + TEMP_TO_REG(data->temp1_hyst)); mutex_unlock(&data->update_lock); return count; } diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index dd2d7b9620c2..385886a4f224 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -137,10 +137,10 @@ static int max16065_read_adc(struct i2c_client *client, int reg) { int rv; - rv = i2c_smbus_read_word_data(client, reg); + rv = i2c_smbus_read_word_swapped(client, reg); if (unlikely(rv < 0)) return rv; - return ((rv & 0xff) << 2) | ((rv >> 14) & 0x03); + return rv >> 6; } static struct max16065_data *max16065_update_device(struct device *dev) diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index d5226c9e1201..ef65ab56b094 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -31,7 +31,7 @@ #define MC13783_ADC_NAME "mc13783-adc" struct mc13783_adc_priv { - struct mc13783 *mc13783; + struct mc13xxx *mc13xxx; struct device *hwmon_dev; }; @@ -51,8 +51,8 @@ static int mc13783_adc_read(struct device *dev, unsigned int sample[4]; int ret; - ret = mc13783_adc_do_conversion(priv->mc13783, - MC13783_ADC_MODE_MULT_CHAN, + ret = mc13xxx_adc_do_conversion(priv->mc13xxx, + MC13XXX_ADC_MODE_MULT_CHAN, channel, sample); if (ret) return ret; @@ -147,9 +147,9 @@ static const struct attribute_group mc13783_group_ts = { static int mc13783_adc_use_touchscreen(struct platform_device *pdev) { struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); - unsigned flags = mc13783_get_flags(priv->mc13783); + unsigned flags = mc13xxx_get_flags(priv->mc13xxx); - return flags & MC13783_USE_TOUCHSCREEN; + return flags & MC13XXX_USE_TOUCHSCREEN; } static int __init mc13783_adc_probe(struct platform_device *pdev) @@ -161,7 +161,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->mc13783 = dev_get_drvdata(pdev->dev.parent); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); platform_set_drvdata(pdev, priv); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index c9237b9dcff2..4b26f51920ba 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -20,17 +20,18 @@ config SENSORS_PMBUS help If you say yes here you get hardware monitoring support for generic PMBus devices, including but not limited to ADP4000, BMR450, BMR451, - BMR453, BMR454, LTC2978, NCP4200, and NCP4208. + BMR453, BMR454, NCP4200, and NCP4208. This driver can also be built as a module. If so, the module will be called pmbus. config SENSORS_ADM1275 - tristate "Analog Devices ADM1275" + tristate "Analog Devices ADM1275 and compatibles" default n help If you say yes here you get hardware monitoring support for Analog - Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. + Devices ADM1275 and ADM1276 Hot-Swap Controller and Digital Power + Monitor. This driver can also be built as a module. If so, the module will be called adm1275. @@ -45,6 +46,16 @@ config SENSORS_LM25066 This driver can also be built as a module. If so, the module will be called lm25066. +config SENSORS_LTC2978 + tristate "Linear Technologies LTC2978 and LTC3880" + default n + help + If you say yes here you get hardware monitoring support for Linear + Technology LTC2978 and LTC3880. + + This driver can also be built as a module. If so, the module will + be called ltc2978. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n @@ -97,4 +108,15 @@ config SENSORS_UCD9200 This driver can also be built as a module. If so, the module will be called ucd9200. +config SENSORS_ZL6100 + tristate "Intersil ZL6100 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for Intersil + ZL2004, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, and ZL6105 Digital + DC/DC Controllers. + + This driver can also be built as a module. If so, the module will + be called zl6100. + endif # PMBUS diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 623eedb1ed9a..789376c85dbb 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -6,8 +6,10 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o +obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index c936e2782309..980a4d9d5028 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -23,6 +23,8 @@ #include <linux/i2c.h> #include "pmbus.h" +enum chips { adm1275, adm1276 }; + #define ADM1275_PEAK_IOUT 0xd0 #define ADM1275_PEAK_VIN 0xd1 #define ADM1275_PEAK_VOUT 0xd2 @@ -31,14 +33,47 @@ #define ADM1275_VIN_VOUT_SELECT (1 << 6) #define ADM1275_VRANGE (1 << 5) +#define ADM1275_IOUT_WARN2_LIMIT 0xd7 +#define ADM1275_DEVICE_CONFIG 0xd8 + +#define ADM1275_IOUT_WARN2_SELECT (1 << 4) + +#define ADM1276_PEAK_PIN 0xda + +#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0) + +struct adm1275_data { + int id; + bool have_oc_fault; + struct pmbus_driver_info info; +}; + +#define to_adm1275_data(x) container_of(x, struct adm1275_data, info) + static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) { - int ret; + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); + int ret = 0; if (page) - return -EINVAL; + return -ENXIO; switch (reg) { + case PMBUS_IOUT_UC_FAULT_LIMIT: + if (data->have_oc_fault) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + if (!data->have_oc_fault) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + break; case PMBUS_VIRT_READ_IOUT_MAX: ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); break; @@ -48,10 +83,20 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) case PMBUS_VIRT_READ_VIN_MAX: ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); break; + case PMBUS_VIRT_READ_PIN_MAX: + if (data->id != adm1276) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); + break; case PMBUS_VIRT_RESET_IOUT_HISTORY: case PMBUS_VIRT_RESET_VOUT_HISTORY: case PMBUS_VIRT_RESET_VIN_HISTORY: - ret = 0; + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + if (data->id != adm1276) + ret = -ENXIO; break; default: ret = -ENODATA; @@ -66,9 +111,14 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, int ret; if (page) - return -EINVAL; + return -ENXIO; switch (reg) { + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT, + word); + break; case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); break; @@ -78,6 +128,41 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIRT_RESET_VIN_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); + int mfr_status, ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_STATUS_IOUT: + ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT); + if (ret < 0) + break; + mfr_status = pmbus_read_byte_data(client, page, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfr_status < 0) { + ret = mfr_status; + break; + } + if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) { + ret |= data->have_oc_fault ? + PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT; + } + break; default: ret = -ENODATA; break; @@ -88,16 +173,17 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, static int adm1275_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int config; + int config, device_config; int ret; struct pmbus_driver_info *info; + struct adm1275_data *data; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) return -ENODEV; - info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL); - if (!info) + data = kzalloc(sizeof(struct adm1275_data), GFP_KERNEL); + if (!data) return -ENOMEM; config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); @@ -106,6 +192,15 @@ static int adm1275_probe(struct i2c_client *client, goto err_mem; } + device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG); + if (device_config < 0) { + ret = device_config; + goto err_mem; + } + + data->id = id->driver_data; + info = &data->info; + info->pages = 1; info->format[PSC_VOLTAGE_IN] = direct; info->format[PSC_VOLTAGE_OUT] = direct; @@ -116,6 +211,7 @@ static int adm1275_probe(struct i2c_client *client, info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; info->read_word_data = adm1275_read_word_data; + info->read_byte_data = adm1275_read_byte_data; info->write_word_data = adm1275_write_word_data; if (config & ADM1275_VRANGE) { @@ -134,10 +230,36 @@ static int adm1275_probe(struct i2c_client *client, info->R[PSC_VOLTAGE_OUT] = -1; } - if (config & ADM1275_VIN_VOUT_SELECT) - info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; - else - info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; + if (device_config & ADM1275_IOUT_WARN2_SELECT) + data->have_oc_fault = true; + + switch (id->driver_data) { + case adm1275: + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + else + info->func[0] |= + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; + break; + case adm1276: + info->format[PSC_POWER] = direct; + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_STATUS_INPUT; + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + if (config & ADM1275_VRANGE) { + info->m[PSC_POWER] = 6043; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } else { + info->m[PSC_POWER] = 2115; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -1; + } + break; + } ret = pmbus_do_probe(client, id, info); if (ret) @@ -145,22 +267,23 @@ static int adm1275_probe(struct i2c_client *client, return 0; err_mem: - kfree(info); + kfree(data); return ret; } static int adm1275_remove(struct i2c_client *client) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - int ret; + const struct adm1275_data *data = to_adm1275_data(info); - ret = pmbus_do_remove(client); - kfree(info); - return ret; + pmbus_do_remove(client); + kfree(data); + return 0; } static const struct i2c_device_id adm1275_id[] = { - {"adm1275", 0}, + { "adm1275", adm1275 }, + { "adm1276", adm1276 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1275_id); @@ -185,7 +308,7 @@ static void __exit adm1275_exit(void) } MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); MODULE_LICENSE("GPL"); module_init(adm1275_init); module_exit(adm1275_exit); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index ac254fba551b..84a37f0c8db6 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -57,7 +57,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) int ret; if (page > 1) - return -EINVAL; + return -ENXIO; /* Map READ_VAUX into READ_VOUT register on page 1 */ if (page == 1) { @@ -85,7 +85,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) break; default: /* No other valid registers on page 1 */ - ret = -EINVAL; + ret = -ENXIO; break; } goto done; @@ -138,7 +138,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, int ret; if (page > 1) - return -EINVAL; + return -ENXIO; switch (reg) { case PMBUS_IIN_OC_WARN_LIMIT: @@ -164,10 +164,10 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, static int lm25066_write_byte(struct i2c_client *client, int page, u8 value) { if (page > 1) - return -EINVAL; + return -ENXIO; - if (page == 0) - return pmbus_write_byte(client, 0, value); + if (page <= 0) + return pmbus_write_byte(client, page, value); return 0; } @@ -309,11 +309,10 @@ static int lm25066_remove(struct i2c_client *client) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct lm25066_data *data = to_lm25066_data(info); - int ret; - ret = pmbus_do_remove(client); + pmbus_do_remove(client); kfree(data); - return ret; + return 0; } static const struct i2c_device_id lm25066_id[] = { diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c new file mode 100644 index 000000000000..820fff48910b --- /dev/null +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -0,0 +1,408 @@ +/* + * Hardware monitoring driver for LTC2978 and LTC3880 + * + * Copyright (c) 2011 Ericsson AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "pmbus.h" + +enum chips { ltc2978, ltc3880 }; + +/* LTC2978 and LTC3880 */ +#define LTC2978_MFR_VOUT_PEAK 0xdd +#define LTC2978_MFR_VIN_PEAK 0xde +#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf +#define LTC2978_MFR_SPECIAL_ID 0xe7 + +/* LTC2978 only */ +#define LTC2978_MFR_VOUT_MIN 0xfb +#define LTC2978_MFR_VIN_MIN 0xfc +#define LTC2978_MFR_TEMPERATURE_MIN 0xfd + +/* LTC3880 only */ +#define LTC3880_MFR_IOUT_PEAK 0xd7 +#define LTC3880_MFR_CLEAR_PEAKS 0xe3 +#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 + +#define LTC2978_ID_REV1 0x0121 +#define LTC2978_ID_REV2 0x0122 +#define LTC3880_ID 0x4000 +#define LTC3880_ID_MASK 0xff00 + +/* + * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which + * happens pretty much each time chip data is updated. Raw peak data therefore + * does not provide much value. To be able to provide useful peak data, keep an + * internal cache of measured peak data, which is only cleared if an explicit + * "clear peak" command is executed for the sensor in question. + */ +struct ltc2978_data { + enum chips id; + int vin_min, vin_max; + int temp_min, temp_max; + int vout_min[8], vout_max[8]; + int iout_max[2]; + int temp2_max[2]; + struct pmbus_driver_info info; +}; + +#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info) + +static inline int lin11_to_val(int data) +{ + s16 e = ((s16)data) >> 11; + s32 m = (((s16)(data << 5)) >> 5); + + /* + * mantissa is 10 bit + sign, exponent adds up to 15 bit. + * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31). + */ + e += 6; + return (e < 0 ? m >> -e : m << e); +} + +static int ltc2978_read_word_data_common(struct i2c_client *client, int page, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) > lin11_to_val(data->vin_max)) + data->vin_max = ret; + ret = data->vin_max; + } + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); + if (ret >= 0) { + /* + * VOUT is 16 bit unsigned with fixed exponent, + * so we can compare it directly + */ + if (ret > data->vout_max[page]) + data->vout_max[page] = ret; + ret = data->vout_max[page]; + } + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + LTC2978_MFR_TEMPERATURE_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) > lin11_to_val(data->temp_max)) + data->temp_max = ret; + ret = data->temp_max; + } + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MIN: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) < lin11_to_val(data->vin_min)) + data->vin_min = ret; + ret = data->vin_min; + } + break; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); + if (ret >= 0) { + /* + * VOUT_MIN is known to not be supported on some lots + * of LTC2978 revision 1, and will return the maximum + * possible voltage if read. If VOUT_MAX is valid and + * lower than the reading of VOUT_MIN, use it instead. + */ + if (data->vout_max[page] && ret > data->vout_max[page]) + ret = data->vout_max[page]; + if (ret < data->vout_min[page]) + data->vout_min[page] = ret; + ret = data->vout_min[page]; + } + break; + case PMBUS_VIRT_READ_TEMP_MIN: + ret = pmbus_read_word_data(client, page, + LTC2978_MFR_TEMPERATURE_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) + < lin11_to_val(data->temp_min)) + data->temp_min = ret; + ret = data->temp_min; + } + break; + case PMBUS_VIRT_READ_IOUT_MAX: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_READ_TEMP2_MAX: + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + ret = -ENXIO; + break; + default: + ret = ltc2978_read_word_data_common(client, page, reg); + break; + } + return ret; +} + +static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iout_max[page])) + data->iout_max[page] = ret; + ret = data->iout_max[page]; + } + break; + case PMBUS_VIRT_READ_TEMP2_MAX: + ret = pmbus_read_word_data(client, page, + LTC3880_MFR_TEMPERATURE2_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->temp2_max[page])) + data->temp2_max[page] = ret; + ret = data->temp2_max[page]; + } + break; + case PMBUS_VIRT_READ_VIN_MIN: + case PMBUS_VIRT_READ_VOUT_MIN: + case PMBUS_VIRT_READ_TEMP_MIN: + ret = -ENXIO; + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + ret = 0; + break; + default: + ret = ltc2978_read_word_data_common(client, page, reg); + break; + } + return ret; +} + +static int ltc2978_clear_peaks(struct i2c_client *client, int page, + enum chips id) +{ + int ret; + + if (id == ltc2978) + ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + else + ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); + + return ret; +} + +static int ltc2978_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_IOUT_HISTORY: + data->iout_max[page] = 0x7fff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + data->temp2_max[page] = 0x7fff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + data->vout_min[page] = 0xffff; + data->vout_max[page] = 0; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + data->vin_min = 0x7bff; + data->vin_max = 0; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + data->temp_min = 0x7bff; + data->temp_max = 0x7fff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id ltc2978_id[] = { + {"ltc2978", ltc2978}, + {"ltc3880", ltc3880}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2978_id); + +static int ltc2978_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int chip_id, ret, i; + struct ltc2978_data *data; + struct pmbus_driver_info *info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + data = kzalloc(sizeof(struct ltc2978_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); + if (chip_id < 0) { + ret = chip_id; + goto err_mem; + } + + if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { + data->id = ltc2978; + } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { + data->id = ltc3880; + } else { + dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); + ret = -ENODEV; + goto err_mem; + } + if (data->id != id->driver_data) + dev_warn(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, + ltc2978_id[data->id].name); + + info = &data->info; + info->write_word_data = ltc2978_write_word_data; + + data->vout_min[0] = 0xffff; + data->vin_min = 0x7bff; + data->temp_min = 0x7bff; + data->temp_max = 0x7fff; + + switch (id->driver_data) { + case ltc2978: + info->read_word_data = ltc2978_read_word_data; + info->pages = 8; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + for (i = 1; i < 8; i++) { + info->func[i] = PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT; + data->vout_min[i] = 0xffff; + } + break; + case ltc3880: + info->read_word_data = ltc3880_read_word_data; + info->pages = 2; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + data->vout_min[1] = 0xffff; + break; + default: + ret = -ENODEV; + goto err_mem; + } + + ret = pmbus_do_probe(client, id, info); + if (ret) + goto err_mem; + return 0; + +err_mem: + kfree(data); + return ret; +} + +static int ltc2978_remove(struct i2c_client *client) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct ltc2978_data *data = to_ltc2978_data(info); + + pmbus_do_remove(client); + kfree(data); + return 0; +} + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc2978_driver = { + .driver = { + .name = "ltc2978", + }, + .probe = ltc2978_probe, + .remove = ltc2978_remove, + .id_table = ltc2978_id, +}; + +static int __init ltc2978_init(void) +{ + return i2c_add_driver(<c2978_driver); +} + +static void __exit ltc2978_exit(void) +{ + i2c_del_driver(<c2978_driver); +} + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880"); +MODULE_LICENSE("GPL"); +module_init(ltc2978_init); +module_exit(ltc2978_exit); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index e50b296e8db4..1d77cf4d2d44 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -105,7 +105,8 @@ static int max16064_probe(struct i2c_client *client, static int max16064_remove(struct i2c_client *client) { - return pmbus_do_remove(client); + pmbus_do_remove(client); + return 0; } static const struct i2c_device_id max16064_id[] = { diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index fda621d2e458..beaf5a8d9c45 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -93,12 +93,14 @@ static int max34440_write_word_data(struct i2c_client *client, int page, static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) { - int ret; + int ret = 0; int mfg_status; - ret = pmbus_set_page(client, page); - if (ret < 0) - return ret; + if (page >= 0) { + ret = pmbus_set_page(client, page); + if (ret < 0) + return ret; + } switch (reg) { case PMBUS_STATUS_IOUT: @@ -224,7 +226,8 @@ static int max34440_probe(struct i2c_client *client, static int max34440_remove(struct i2c_client *client) { - return pmbus_do_remove(client); + pmbus_do_remove(client); + return 0; } static const struct i2c_device_id max34440_id[] = { diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index c3e72f1a3cfb..e2b74bb399ba 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -45,7 +45,7 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg) int ret; if (page) - return -EINVAL; + return -ENXIO; switch (reg) { case PMBUS_VIRT_READ_VOUT_MAX: @@ -101,8 +101,8 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) int ret = 0; int mfg_status; - if (page) - return -EINVAL; + if (page > 0) + return -ENXIO; switch (reg) { case PMBUS_STATUS_VOUT: @@ -182,7 +182,8 @@ static int max8688_probe(struct i2c_client *client, static int max8688_remove(struct i2c_client *client) { - return pmbus_do_remove(client); + pmbus_do_remove(client); + return 0; } static const struct i2c_device_id max8688_id[] = { diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 73de9f1f3194..995e873197e3 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -187,13 +187,12 @@ out: static int pmbus_remove(struct i2c_client *client) { - int ret; const struct pmbus_driver_info *info; info = pmbus_get_driver_info(client); - ret = pmbus_do_remove(client); + pmbus_do_remove(client); kfree(info); - return ret; + return 0; } /* @@ -205,10 +204,13 @@ static const struct i2c_device_id pmbus_id[] = { {"bmr451", 1}, {"bmr453", 1}, {"bmr454", 1}, - {"ltc2978", 8}, {"ncp4200", 1}, {"ncp4208", 1}, + {"pdt003", 1}, + {"pdt006", 1}, + {"pdt012", 1}, {"pmbus", 0}, + {"udt020", 1}, {} }; diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index a6ae20ffef6b..5d31d1c2c0f5 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -134,8 +134,16 @@ * Semantics: * Virtual registers are all word size. * READ registers are read-only; writes are either ignored or return an error. - * RESET registers are read/write. Reading returns zero (used for detection), - * writing any value causes the associated history to be reset. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. */ #define PMBUS_VIRT_BASE 0x100 #define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 0) @@ -160,6 +168,9 @@ #define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 19) #define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 20) #define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 22) +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 23) +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 24) /* * CAPABILITY @@ -320,6 +331,12 @@ struct pmbus_driver_info { * The following functions map manufacturing specific register values * to PMBus standard register values. Specify only if mapping is * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. */ int (*read_byte_data)(struct i2c_client *client, int page, int reg); int (*read_word_data)(struct i2c_client *client, int page, int reg); @@ -347,7 +364,7 @@ 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); int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info); -int pmbus_do_remove(struct i2c_client *client); +void pmbus_do_remove(struct i2c_client *client); const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 397fc59b5682..00460d8d8423 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -160,7 +160,7 @@ int pmbus_set_page(struct i2c_client *client, u8 page) rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); if (newpage != page) - rv = -EINVAL; + rv = -EIO; else data->currpage = page; } @@ -229,7 +229,7 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, return status; } if (reg >= PMBUS_VIRT_BASE) - return -EINVAL; + return -ENXIO; return pmbus_write_word_data(client, page, reg, word); } @@ -261,7 +261,7 @@ static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) return status; } if (reg >= PMBUS_VIRT_BASE) - return -EINVAL; + return -ENXIO; return pmbus_read_word_data(client, page, reg); } @@ -316,11 +316,11 @@ static int pmbus_check_status_cml(struct i2c_client *client) { int status, status2; - status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE); + status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE); if (status < 0 || (status & PB_STATUS_CML)) { - status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) - return -EINVAL; + return -EIO; } return 0; } @@ -371,8 +371,8 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) for (i = 0; i < info->pages; i++) data->status[PB_STATUS_BASE + i] - = pmbus_read_byte_data(client, i, - PMBUS_STATUS_BYTE); + = _pmbus_read_byte_data(client, i, + PMBUS_STATUS_BYTE); for (i = 0; i < info->pages; i++) { if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT)) continue; @@ -445,13 +445,8 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, exponent = data->exponent; mantissa = (u16) sensor->data; } else { /* LINEAR11 */ - exponent = (sensor->data >> 11) & 0x001f; - mantissa = sensor->data & 0x07ff; - - if (exponent > 0x0f) - exponent |= 0xffe0; /* sign extend exponent */ - if (mantissa > 0x03ff) - mantissa |= 0xfffff800; /* sign extend mantissa */ + exponent = ((s16)sensor->data) >> 11; + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; } val = mantissa; @@ -1401,7 +1396,42 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = { } }; -static const struct pmbus_limit_attr temp_limit_attrs23[] = { +static const struct pmbus_limit_attr temp_limit_attrs2[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs3[] = { { .reg = PMBUS_UT_WARN_LIMIT, .low = true, @@ -1450,8 +1480,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .sfunc = PMBUS_HAVE_STATUS_TEMP, .sbase = PB_STATUS_TEMP_BASE, .gbit = PB_STATUS_TEMPERATURE, - .limit = temp_limit_attrs23, - .nlimit = ARRAY_SIZE(temp_limit_attrs23), + .limit = temp_limit_attrs2, + .nlimit = ARRAY_SIZE(temp_limit_attrs2), }, { .reg = PMBUS_READ_TEMPERATURE_3, .class = PSC_TEMPERATURE, @@ -1462,8 +1492,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .sfunc = PMBUS_HAVE_STATUS_TEMP, .sbase = PB_STATUS_TEMP_BASE, .gbit = PB_STATUS_TEMPERATURE, - .limit = temp_limit_attrs23, - .nlimit = ARRAY_SIZE(temp_limit_attrs23), + .limit = temp_limit_attrs3, + .nlimit = ARRAY_SIZE(temp_limit_attrs3), } }; @@ -1593,10 +1623,10 @@ static void pmbus_find_attributes(struct i2c_client *client, static int pmbus_identify_common(struct i2c_client *client, struct pmbus_data *data) { - int vout_mode = -1, exponent; + int vout_mode = -1; if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) - vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + vout_mode = _pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); if (vout_mode >= 0 && vout_mode != 0xff) { /* * Not all chips support the VOUT_MODE command, @@ -1607,11 +1637,7 @@ static int pmbus_identify_common(struct i2c_client *client, if (data->info->format[PSC_VOLTAGE_OUT] != linear) return -ENODEV; - exponent = vout_mode & 0x1f; - /* and sign-extend it */ - if (exponent & 0x10) - exponent |= ~0x1f; - data->exponent = exponent; + data->exponent = ((s8)(vout_mode << 3)) >> 3; break; case 1: /* VID mode */ if (data->info->format[PSC_VOLTAGE_OUT] != vid) @@ -1682,7 +1708,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, if (info->pages <= 0 || info->pages > PMBUS_PAGES) { dev_err(&client->dev, "Bad number of PMBus pages: %d\n", info->pages); - ret = -EINVAL; + ret = -ENODEV; goto out_data; } @@ -1764,7 +1790,7 @@ out_data: } EXPORT_SYMBOL_GPL(pmbus_do_probe); -int pmbus_do_remove(struct i2c_client *client) +void pmbus_do_remove(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); @@ -1774,7 +1800,6 @@ int pmbus_do_remove(struct i2c_client *client) kfree(data->booleans); kfree(data->sensors); kfree(data); - return 0; } EXPORT_SYMBOL_GPL(pmbus_do_remove); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index d0ddb60155c9..4ff6cf289f85 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -74,8 +74,8 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_FAN_CONFIG_12: - if (page) - return -EINVAL; + if (page > 0) + return -ENXIO; ret = ucd9000_get_fan_config(client, 0); if (ret < 0) @@ -88,8 +88,8 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) ret = fan_config; break; case PMBUS_FAN_CONFIG_34: - if (page) - return -EINVAL; + if (page > 0) + return -ENXIO; ret = ucd9000_get_fan_config(client, 2); if (ret < 0) @@ -239,13 +239,12 @@ out: static int ucd9000_remove(struct i2c_client *client) { - int ret; struct ucd9000_data *data; data = to_ucd9000_data(pmbus_get_driver_info(client)); - ret = pmbus_do_remove(client); + pmbus_do_remove(client); kfree(data); - return ret; + return 0; } diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index c65e9da707cc..6e1c1a80ab85 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -171,13 +171,12 @@ out: static int ucd9200_remove(struct i2c_client *client) { - int ret; const struct pmbus_driver_info *info; info = pmbus_get_driver_info(client); - ret = pmbus_do_remove(client); + pmbus_do_remove(client); kfree(info); - return ret; + return 0; } diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c new file mode 100644 index 000000000000..2bc980006f83 --- /dev/null +++ b/drivers/hwmon/pmbus/zl6100.c @@ -0,0 +1,256 @@ +/* + * Hardware monitoring driver for ZL6100 and compatibles + * + * Copyright (c) 2011 Ericsson AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/ktime.h> +#include <linux/delay.h> +#include "pmbus.h" + +enum chips { zl2004, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105 }; + +struct zl6100_data { + int id; + ktime_t access; /* chip access time */ + struct pmbus_driver_info info; +}; + +#define to_zl6100_data(x) container_of(x, struct zl6100_data, info) + +#define ZL6100_DEVICE_ID 0xe4 + +#define ZL6100_WAIT_TIME 1000 /* uS */ + +static ushort delay = ZL6100_WAIT_TIME; +module_param(delay, ushort, 0644); +MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); + +/* Some chips need a delay between accesses */ +static inline void zl6100_wait(const struct zl6100_data *data) +{ + if (delay) { + s64 delta = ktime_us_delta(ktime_get(), data->access); + if (delta < delay) + udelay(delay - delta); + } +} + +static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret; + + if (page || reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + zl6100_wait(data); + ret = pmbus_read_word_data(client, page, reg); + data->access = ktime_get(); + + return ret; +} + +static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + zl6100_wait(data); + ret = pmbus_read_byte_data(client, page, reg); + data->access = ktime_get(); + + return ret; +} + +static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret; + + if (page || reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + zl6100_wait(data); + ret = pmbus_write_word_data(client, page, reg, word); + data->access = ktime_get(); + + return ret; +} + +static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + zl6100_wait(data); + ret = pmbus_write_byte(client, page, value); + data->access = ktime_get(); + + return ret; +} + +static const struct i2c_device_id zl6100_id[] = { + {"zl2004", zl2004}, + {"zl2006", zl2006}, + {"zl2008", zl2008}, + {"zl2105", zl2105}, + {"zl2106", zl2106}, + {"zl6100", zl6100}, + {"zl6105", zl6105}, + { } +}; +MODULE_DEVICE_TABLE(i2c, zl6100_id); + +static int zl6100_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct zl6100_data *data; + struct pmbus_driver_info *info; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID, + device_id); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device ID\n"); + return ret; + } + device_id[ret] = '\0'; + dev_info(&client->dev, "Device ID %s\n", device_id); + + mid = NULL; + for (mid = zl6100_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + if (id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + data = kzalloc(sizeof(struct zl6100_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = mid->driver_data; + + /* + * ZL2008, ZL2105, and ZL6100 are known to require a wait time + * between I2C accesses. ZL2004 and ZL6105 are known to be safe. + * + * Only clear the wait time for chips known to be safe. The wait time + * can be cleared later for additional chips if tests show that it + * is not needed (in other words, better be safe than sorry). + */ + if (data->id == zl2004 || data->id == zl6105) + delay = 0; + + /* + * Since there was a direct I2C device access above, wait before + * accessing the chip again. + * Set the timestamp, wait, then set it again. This should provide + * enough buffer time to be safe. + */ + data->access = ktime_get(); + zl6100_wait(data); + data->access = ktime_get(); + + info = &data->info; + + info->pages = 1; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + + info->read_word_data = zl6100_read_word_data; + info->read_byte_data = zl6100_read_byte_data; + info->write_word_data = zl6100_write_word_data; + info->write_byte = zl6100_write_byte; + + ret = pmbus_do_probe(client, mid, info); + if (ret) + goto err_mem; + return 0; + +err_mem: + kfree(data); + return ret; +} + +static int zl6100_remove(struct i2c_client *client) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct zl6100_data *data = to_zl6100_data(info); + + pmbus_do_remove(client); + kfree(data); + return 0; +} + +static struct i2c_driver zl6100_driver = { + .driver = { + .name = "zl6100", + }, + .probe = zl6100_probe, + .remove = zl6100_remove, + .id_table = zl6100_id, +}; + +static int __init zl6100_init(void) +{ + return i2c_add_driver(&zl6100_driver); +} + +static void __exit zl6100_exit(void) +{ + i2c_del_driver(&zl6100_driver); +} + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); +MODULE_LICENSE("GPL"); +module_init(zl6100_init); +module_exit(zl6100_exit); diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index 1c8c9812f244..15398780cc00 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -83,25 +83,6 @@ static inline int sht21_rh_ticks_to_per_cent_mille(int ticks) } /** - * sht21_read_word_data() - read word from register - * @client: I2C client device - * @reg: I2C command byte - * - * Returns value, negative errno on error. - */ -static inline int sht21_read_word_data(struct i2c_client *client, u8 reg) -{ - int ret = i2c_smbus_read_word_data(client, reg); - if (ret < 0) - return ret; - /* - * SMBus specifies low byte first, but the SHT21 returns MSB - * first, so we have to swab16 the values - */ - return swab16(ret); -} - -/** * sht21_update_measurements() - get updated measurements from device * @client: I2C client device * @@ -119,12 +100,13 @@ static int sht21_update_measurements(struct i2c_client *client) * maximum two measurements per second at 12bit accuracy shall be made. */ if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) { - ret = sht21_read_word_data(client, SHT21_TRIG_T_MEASUREMENT_HM); + ret = i2c_smbus_read_word_swapped(client, + SHT21_TRIG_T_MEASUREMENT_HM); if (ret < 0) goto out; sht21->temperature = sht21_temp_ticks_to_millicelsius(ret); - ret = sht21_read_word_data(client, - SHT21_TRIG_RH_MEASUREMENT_HM); + ret = i2c_smbus_read_word_swapped(client, + SHT21_TRIG_RH_MEASUREMENT_HM); if (ret < 0) goto out; sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret); diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index 425df5bccd45..411638181fd8 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -214,33 +214,26 @@ static int smm665_read_adc(struct smm665_data *data, int adc) * * Neither i2c_smbus_read_byte() nor * i2c_smbus_read_block_data() worked here, - * so use i2c_smbus_read_word_data() instead. + * so use i2c_smbus_read_word_swapped() instead. * We could also try to use i2c_master_recv(), * but that is not always supported. */ - rv = i2c_smbus_read_word_data(client, 0); + rv = i2c_smbus_read_word_swapped(client, 0); if (rv < 0) { dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv); return -1; } /* * Validate/verify readback adc channel (in bit 11..14). - * High byte is in lower 8 bit of rv, so only shift by 3. */ - radc = (rv >> 3) & 0x0f; + radc = (rv >> 11) & 0x0f; if (radc != adc) { dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d", adc, radc); return -EIO; } - /* - * Chip replies with H/L, while SMBus expects L/H. - * Thus, byte order is reversed, and we have to swap - * the result. - */ - rv = swab16(rv) & SMM665_ADC_MASK; - return rv; + return rv & SMM665_ADC_MASK; } static struct smm665_data *smm665_update_device(struct device *dev) diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index 9fb7516e6f45..65c88ff5645a 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -113,7 +113,7 @@ struct smsc47b397_data { u8 temp[4]; }; -static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg) +static int smsc47b397_read_value(struct smsc47b397_data *data, u8 reg) { int res; @@ -265,7 +265,8 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev) return -EBUSY; } - if (!(data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) { + data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL); + if (!data) { err = -ENOMEM; goto error_release; } @@ -276,7 +277,8 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); - if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group))) + err = sysfs_create_group(&dev->kobj, &smsc47b397_group); + if (err) goto error_free; data->hwmon_dev = hwmon_device_register(dev); @@ -345,7 +347,7 @@ static int __init smsc47b397_find(unsigned short *addr) superio_enter(); id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); - switch(id) { + switch (id) { case 0x81: name = "SCH5307-NS"; break; @@ -379,7 +381,8 @@ static int __init smsc47b397_init(void) unsigned short address; int ret; - if ((ret = smsc47b397_find(&address))) + ret = smsc47b397_find(&address); + if (ret) return ret; ret = platform_driver_register(&smsc47b397_driver); diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 5bd194968801..643aa8c94535 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -55,19 +55,6 @@ struct tmp102 { int temp[3]; }; -/* SMBus specifies low byte first, but the TMP102 returns high byte first, - * so we have to swab16 the values */ -static inline int tmp102_read_reg(struct i2c_client *client, u8 reg) -{ - int result = i2c_smbus_read_word_data(client, reg); - return result < 0 ? result : swab16(result); -} - -static inline int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val) -{ - return i2c_smbus_write_word_data(client, reg, swab16(val)); -} - /* convert left adjusted 13-bit TMP102 register value to milliCelsius */ static inline int tmp102_reg_to_mC(s16 val) { @@ -94,7 +81,8 @@ static struct tmp102 *tmp102_update_device(struct i2c_client *client) if (time_after(jiffies, tmp102->last_update + HZ / 3)) { int i; for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) { - int status = tmp102_read_reg(client, tmp102_reg[i]); + int status = i2c_smbus_read_word_swapped(client, + tmp102_reg[i]); if (status > -1) tmp102->temp[i] = tmp102_reg_to_mC(status); } @@ -130,8 +118,8 @@ static ssize_t tmp102_set_temp(struct device *dev, mutex_lock(&tmp102->lock); tmp102->temp[sda->index] = val; - status = tmp102_write_reg(client, tmp102_reg[sda->index], - tmp102_mC_to_reg(val)); + status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index], + tmp102_mC_to_reg(val)); mutex_unlock(&tmp102->lock); return status ? : count; } @@ -178,18 +166,19 @@ static int __devinit tmp102_probe(struct i2c_client *client, } i2c_set_clientdata(client, tmp102); - status = tmp102_read_reg(client, TMP102_CONF_REG); + status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); if (status < 0) { dev_err(&client->dev, "error reading config register\n"); goto fail_free; } tmp102->config_orig = status; - status = tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG); + status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, + TMP102_CONFIG); if (status < 0) { dev_err(&client->dev, "error writing config register\n"); goto fail_restore_config; } - status = tmp102_read_reg(client, TMP102_CONF_REG); + status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); if (status < 0) { dev_err(&client->dev, "error reading config register\n"); goto fail_restore_config; @@ -222,7 +211,8 @@ static int __devinit tmp102_probe(struct i2c_client *client, fail_remove_sysfs: sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group); fail_restore_config: - tmp102_write_reg(client, TMP102_CONF_REG, tmp102->config_orig); + i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, + tmp102->config_orig); fail_free: kfree(tmp102); @@ -240,10 +230,10 @@ static int __devexit tmp102_remove(struct i2c_client *client) if (tmp102->config_orig & TMP102_CONF_SD) { int config; - config = tmp102_read_reg(client, TMP102_CONF_REG); + config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); if (config >= 0) - tmp102_write_reg(client, TMP102_CONF_REG, - config | TMP102_CONF_SD); + i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, + config | TMP102_CONF_SD); } kfree(tmp102); @@ -257,12 +247,12 @@ static int tmp102_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); int config; - config = tmp102_read_reg(client, TMP102_CONF_REG); + config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); if (config < 0) return config; config |= TMP102_CONF_SD; - return tmp102_write_reg(client, TMP102_CONF_REG, config); + return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); } static int tmp102_resume(struct device *dev) @@ -270,12 +260,12 @@ static int tmp102_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); int config; - config = tmp102_read_reg(client, TMP102_CONF_REG); + config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); if (config < 0) return config; config &= ~TMP102_CONF_SD; - return tmp102_write_reg(client, TMP102_CONF_REG, config); + return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); } static const struct dev_pm_ops tmp102_dev_pm_ops = { diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c index 27a62711e0a6..3cd07bf42dca 100644 --- a/drivers/hwmon/ultra45_env.c +++ b/drivers/hwmon/ultra45_env.c @@ -6,6 +6,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/of_device.h> #include <linux/io.h> #include <linux/hwmon.h> diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 36d7f270b14d..93f5fc7d6059 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1,7 +1,7 @@ /* w83627ehf - Driver for the hardware monitoring functionality of the Winbond W83627EHF Super-I/O chip - Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> + Copyright (C) 2005-2011 Jean Delvare <khali@linux-fr.org> Copyright (C) 2006 Yuan Mu (Winbond), Rudolf Marek <r.marek@assembler.cz> David Hubbard <david.c.hubbard@gmail.com> @@ -39,6 +39,7 @@ 0x8860 0xa1 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 + w83627uhg 8 2 2 2 0xa230 0xc1 0x5ca3 w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3 nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3 @@ -61,14 +62,17 @@ #include <linux/io.h> #include "lm75.h" -enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775, - nct6776 }; +enum kinds { + w83627ehf, w83627dhg, w83627dhg_p, w83627uhg, + w83667hg, w83667hg_b, nct6775, nct6776, +}; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ static const char * const w83627ehf_device_names[] = { "w83627ehf", "w83627dhg", "w83627dhg", + "w83627uhg", "w83667hg", "w83667hg", "nct6775", @@ -104,6 +108,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_W83627EHG_ID 0x8860 #define SIO_W83627DHG_ID 0xa020 #define SIO_W83627DHG_P_ID 0xb070 +#define SIO_W83627UHG_ID 0xa230 #define SIO_W83667HG_ID 0xa510 #define SIO_W83667HG_B_ID 0xb350 #define SIO_NCT6775_ID 0xb470 @@ -197,6 +202,9 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 }; #define W83627EHF_REG_ALARM2 0x45A #define W83627EHF_REG_ALARM3 0x45B +#define W83627EHF_REG_CASEOPEN_DET 0x42 /* SMI STATUS #2 */ +#define W83627EHF_REG_CASEOPEN_CLR 0x46 /* SMI MASK #3 */ + /* SmartFan registers */ #define W83627EHF_REG_FAN_STEPUP_TIME 0x0f #define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e @@ -316,7 +324,7 @@ static const char *const nct6776_temp_label[] = { #define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP) -static inline int is_word_sized(u16 reg) +static int is_word_sized(u16 reg) { return ((((reg & 0xff00) == 0x100 || (reg & 0xff00) == 0x200) @@ -385,35 +393,23 @@ div_from_reg(u8 reg) return 1 << reg; } -static inline int -temp_from_reg(u16 reg, s16 regval) -{ - if (is_word_sized(reg)) - return LM75_TEMP_FROM_REG(regval); - return ((s8)regval) * 1000; -} - -static inline u16 -temp_to_reg(u16 reg, long temp) -{ - if (is_word_sized(reg)) - return LM75_TEMP_TO_REG(temp); - return (s8)DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), - 1000); -} - -/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ - -static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 }; +/* Some of the voltage inputs have internal scaling, the tables below + * contain 8 (the ADC LSB in mV) * scaling factor * 100 */ +static const u16 scale_in_common[10] = { + 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800 +}; +static const u16 scale_in_w83627uhg[9] = { + 800, 800, 3328, 3424, 800, 800, 0, 3328, 3400 +}; -static inline long in_from_reg(u8 reg, u8 nr) +static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in) { - return reg * scale_in[nr]; + return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); } -static inline u8 in_to_reg(u32 val, u8 nr) +static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in) { - return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, + return SENSORS_LIMIT(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); } @@ -444,6 +440,7 @@ struct w83627ehf_data { const u16 *REG_FAN_STOP_TIME; const u16 *REG_FAN_MAX_OUTPUT; const u16 *REG_FAN_STEP_OUTPUT; + const u16 *scale_in; unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); @@ -469,6 +466,7 @@ struct w83627ehf_data { s16 temp_max[9]; s16 temp_max_hyst[9]; u32 alarms; + u8 caseopen; u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ u8 pwm_enable[4]; /* 1->manual @@ -494,7 +492,8 @@ struct w83627ehf_data { u8 vrm; u16 have_temp; - u8 in6_skip; + u8 in6_skip:1; + u8 temp3_val_only:1; }; struct w83627ehf_sio_data { @@ -557,6 +556,26 @@ static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg, return 0; } +/* We left-align 8-bit temperature values to make the code simpler */ +static u16 w83627ehf_read_temp(struct w83627ehf_data *data, u16 reg) +{ + u16 res; + + res = w83627ehf_read_value(data, reg); + if (!is_word_sized(reg)) + res <<= 8; + + return res; +} + +static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg, + u16 value) +{ + if (!is_word_sized(reg)) + value >>= 8; + return w83627ehf_write_value(data, reg, value); +} + /* This function assumes that the caller holds data->update_lock */ static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr) { @@ -771,6 +790,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) /* Measured voltages and limits */ for (i = 0; i < data->in_num; i++) { + if ((i == 6) && data->in6_skip) + continue; + data->in[i] = w83627ehf_read_value(data, W83627EHF_REG_IN(i)); data->in_min[i] = w83627ehf_read_value(data, @@ -855,15 +877,15 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) for (i = 0; i < NUM_REG_TEMP; i++) { if (!(data->have_temp & (1 << i))) continue; - data->temp[i] = w83627ehf_read_value(data, + data->temp[i] = w83627ehf_read_temp(data, data->reg_temp[i]); if (data->reg_temp_over[i]) data->temp_max[i] - = w83627ehf_read_value(data, + = w83627ehf_read_temp(data, data->reg_temp_over[i]); if (data->reg_temp_hyst[i]) data->temp_max_hyst[i] - = w83627ehf_read_value(data, + = w83627ehf_read_temp(data, data->reg_temp_hyst[i]); } @@ -874,6 +896,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) (w83627ehf_read_value(data, W83627EHF_REG_ALARM3) << 16); + data->caseopen = w83627ehf_read_value(data, + W83627EHF_REG_CASEOPEN_DET); + data->last_updated = jiffies; data->valid = 1; } @@ -894,7 +919,8 @@ show_##reg(struct device *dev, struct device_attribute *attr, \ struct sensor_device_attribute *sensor_attr = \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \ + return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \ + data->scale_in)); \ } show_in_reg(in) show_in_reg(in_min) @@ -915,7 +941,7 @@ store_in_##reg(struct device *dev, struct device_attribute *attr, \ if (err < 0) \ return err; \ mutex_lock(&data->update_lock); \ - data->in_##reg[nr] = in_to_reg(val, nr); \ + data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \ w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \ data->in_##reg[nr]); \ mutex_unlock(&data->update_lock); \ @@ -1156,8 +1182,7 @@ show_##reg(struct device *dev, struct device_attribute *attr, \ struct sensor_device_attribute *sensor_attr = \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - return sprintf(buf, "%d\n", \ - temp_from_reg(data->addr[nr], data->reg[nr])); \ + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->reg[nr])); \ } show_temp_reg(reg_temp, temp); show_temp_reg(reg_temp_over, temp_max); @@ -1178,9 +1203,8 @@ store_##reg(struct device *dev, struct device_attribute *attr, \ if (err < 0) \ return err; \ mutex_lock(&data->update_lock); \ - data->reg[nr] = temp_to_reg(data->addr[nr], val); \ - w83627ehf_write_value(data, data->addr[nr], \ - data->reg[nr]); \ + data->reg[nr] = LM75_TEMP_TO_REG(val); \ + w83627ehf_write_temp(data, data->addr[nr], data->reg[nr]); \ mutex_unlock(&data->update_lock); \ return count; \ } @@ -1606,25 +1630,28 @@ static struct sensor_device_attribute sda_sf3_arrays_fan4[] = { store_fan_step_output, 3), }; +static struct sensor_device_attribute sda_sf3_arrays_fan3[] = { + SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 2), + SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, + store_fan_start_output, 2), + SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, + store_fan_stop_output, 2), +}; + static struct sensor_device_attribute sda_sf3_arrays[] = { SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, store_fan_stop_time, 0), SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, store_fan_stop_time, 1), - SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 2), SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, store_fan_start_output, 0), SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, store_fan_start_output, 1), - SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 2), SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, store_fan_stop_output, 0), SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, store_fan_stop_output, 1), - SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 2), }; @@ -1655,6 +1682,48 @@ show_vid(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +/* Case open detection */ + +static ssize_t +show_caseopen(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + + return sprintf(buf, "%d\n", + !!(data->caseopen & to_sensor_dev_attr_2(attr)->index)); +} + +static ssize_t +clear_caseopen(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w83627ehf_data *data = dev_get_drvdata(dev); + unsigned long val; + u16 reg, mask; + + if (strict_strtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mask = to_sensor_dev_attr_2(attr)->nr; + + mutex_lock(&data->update_lock); + reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR); + w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask); + w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg & ~mask); + data->valid = 0; /* Force cache refresh */ + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sensor_device_attribute_2 sda_caseopen[] = { + SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen, + clear_caseopen, 0x80, 0x10), + SENSOR_ATTR_2(intrusion1_alarm, S_IWUSR | S_IRUGO, show_caseopen, + clear_caseopen, 0x40, 0x40), +}; + /* * Driver and device management */ @@ -1675,6 +1744,8 @@ static void w83627ehf_device_remove_files(struct device *dev) data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) device_remove_file(dev, &attr->dev_attr); } + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) + device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr); for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); for (i = 0; i < data->in_num; i++) { @@ -1703,6 +1774,8 @@ static void w83627ehf_device_remove_files(struct device *dev) continue; device_remove_file(dev, &sda_temp_input[i].dev_attr); device_remove_file(dev, &sda_temp_label[i].dev_attr); + if (i == 2 && data->temp3_val_only) + continue; device_remove_file(dev, &sda_temp_max[i].dev_attr); device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); if (i > 2) @@ -1711,6 +1784,9 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_temp_type[i].dev_attr); } + device_remove_file(dev, &sda_caseopen[0].dev_attr); + device_remove_file(dev, &sda_caseopen[1].dev_attr); + device_remove_file(dev, &dev_attr_name); device_remove_file(dev, &dev_attr_cpu0_vid); } @@ -1752,11 +1828,24 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data, case w83627ehf: diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE); break; + case w83627uhg: + diode = 0x00; + break; default: diode = 0x70; } for (i = 0; i < 3; i++) { - if ((tmp & (0x02 << i))) + const char *label = NULL; + + if (data->temp_label) + label = data->temp_label[data->temp_src[i]]; + + /* Digital source overrides analog type */ + if (label && strncmp(label, "PECI", 4) == 0) + data->temp_type[i] = 6; + else if (label && strncmp(label, "AMD", 3) == 0) + data->temp_type[i] = 5; + else if ((tmp & (0x02 << i))) data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3; else data->temp_type[i] = 4; /* thermistor */ @@ -1789,13 +1878,98 @@ static void w82627ehf_swap_tempreg(struct w83627ehf_data *data, data->reg_temp_config[r2] = tmp; } +static void __devinit +w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp) +{ + int i; + + for (i = 0; i < n_temp; i++) { + data->reg_temp[i] = W83627EHF_REG_TEMP[i]; + data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i]; + data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i]; + data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i]; + } +} + +static void __devinit +w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, + struct w83627ehf_data *data) +{ + int fan3pin, fan4pin, fan4min, fan5pin, regval; + + /* The W83627UHG is simple, only two fan inputs, no config */ + if (sio_data->kind == w83627uhg) { + data->has_fan = 0x03; /* fan1 and fan2 */ + data->has_fan_min = 0x03; + return; + } + + superio_enter(sio_data->sioreg); + + /* fan4 and fan5 share some pins with the GPIO and serial flash */ + if (sio_data->kind == nct6775) { + /* On NCT6775, fan4 shares pins with the fdc interface */ + fan3pin = 1; + fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); + fan4min = 0; + fan5pin = 0; + } else if (sio_data->kind == nct6776) { + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); + fan4min = fan4pin; + } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { + fan3pin = 1; + fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; + fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; + fan4min = fan4pin; + } else { + fan3pin = 1; + fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); + fan4min = fan4pin; + } + + superio_exit(sio_data->sioreg); + + data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ + data->has_fan |= (fan3pin << 2); + data->has_fan_min |= (fan3pin << 2); + + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + /* + * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 + * register + */ + data->has_fan |= (fan4pin << 3) | (fan5pin << 4); + data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); + } else { + /* + * It looks like fan4 and fan5 pins can be alternatively used + * as fan on/off switches, but fan5 control is write only :/ + * We assume that if the serial interface is disabled, designers + * connected fan5 as input unless they are emitting log 1, which + * is not the default. + */ + regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); + if ((regval & (1 << 2)) && fan4pin) { + data->has_fan |= (1 << 3); + data->has_fan_min |= (1 << 3); + } + if (!(regval & (1 << 1)) && fan5pin) { + data->has_fan |= (1 << 4); + data->has_fan_min |= (1 << 4); + } + } +} + static int __devinit w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct w83627ehf_sio_data *sio_data = dev->platform_data; struct w83627ehf_data *data; struct resource *res; - u8 fan3pin, fan4pin, fan4min, fan5pin, en_vrm10; + u8 en_vrm10; int i, err = 0; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1821,23 +1995,24 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; - /* 667HG, NCT6775F, and NCT6776F have 3 pwms */ - data->pwm_num = (sio_data->kind == w83667hg - || sio_data->kind == w83667hg_b - || sio_data->kind == nct6775 - || sio_data->kind == nct6776) ? 3 : 4; + /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */ + switch (sio_data->kind) { + default: + data->pwm_num = 4; + break; + case w83667hg: + case w83667hg_b: + case nct6775: + case nct6776: + data->pwm_num = 3; + break; + case w83627uhg: + data->pwm_num = 2; + break; + } + /* Default to 3 temperature inputs, code below will adjust as needed */ data->have_temp = 0x07; - /* Check temp3 configuration bit for 667HG */ - if (sio_data->kind == w83667hg) { - u8 reg; - - reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]); - if (reg & 0x01) - data->have_temp &= ~(1 << 2); - else - data->in6_skip = 1; /* either temp3 or in6 */ - } /* Deal with temperature register setup first. */ if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { @@ -1914,16 +2089,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) } else if (sio_data->kind == w83667hg_b) { u8 reg; + w83627ehf_set_temp_reg_ehf(data, 4); + /* * Temperature sources are selected with bank 0, registers 0x49 * and 0x4a. */ - for (i = 0; i < ARRAY_SIZE(W83627EHF_REG_TEMP); i++) { - data->reg_temp[i] = W83627EHF_REG_TEMP[i]; - data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i]; - data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i]; - data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i]; - } reg = w83627ehf_read_value(data, 0x4a); data->temp_src[0] = reg >> 5; reg = w83627ehf_read_value(data, 0x49); @@ -1957,13 +2128,60 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->in6_skip = 1; data->temp_label = w83667hg_b_temp_label; + } else if (sio_data->kind == w83627uhg) { + u8 reg; + + w83627ehf_set_temp_reg_ehf(data, 3); + + /* + * Temperature sources for temp1 and temp2 are selected with + * bank 0, registers 0x49 and 0x4a. + */ + data->temp_src[0] = 0; /* SYSTIN */ + reg = w83627ehf_read_value(data, 0x49) & 0x07; + /* Adjust to have the same mapping as other source registers */ + if (reg == 0) + data->temp_src[1]++; + else if (reg >= 2 && reg <= 5) + data->temp_src[1] += 2; + else /* should never happen */ + data->have_temp &= ~(1 << 1); + reg = w83627ehf_read_value(data, 0x4a); + data->temp_src[2] = reg >> 5; + + /* + * Skip temp3 if source is invalid or the same as temp1 + * or temp2. + */ + if (data->temp_src[2] == 2 || data->temp_src[2] == 3 || + data->temp_src[2] == data->temp_src[0] || + ((data->have_temp & (1 << 1)) && + data->temp_src[2] == data->temp_src[1])) + data->have_temp &= ~(1 << 2); + else + data->temp3_val_only = 1; /* No limit regs */ + + data->in6_skip = 1; /* No VIN3 */ + + data->temp_label = w83667hg_b_temp_label; } else { + w83627ehf_set_temp_reg_ehf(data, 3); + /* Temperature sources are fixed */ - for (i = 0; i < 3; i++) { - data->reg_temp[i] = W83627EHF_REG_TEMP[i]; - data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i]; - data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i]; - data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i]; + + if (sio_data->kind == w83667hg) { + u8 reg; + + /* + * Chip supports either AUXTIN or VIN3. Try to find + * out which one. + */ + reg = w83627ehf_read_value(data, + W83627EHF_REG_TEMP_CONFIG[2]); + if (reg & 0x01) + data->have_temp &= ~(1 << 2); + else + data->in6_skip = 1; } } @@ -2023,6 +2241,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) W83627EHF_REG_FAN_STEP_OUTPUT_COMMON; } + /* Setup input voltage scaling factors */ + if (sio_data->kind == w83627uhg) + data->scale_in = scale_in_w83627uhg; + else + data->scale_in = scale_in_common; + /* Initialize the chip */ w83627ehf_init_device(data, sio_data->kind); @@ -2039,7 +2263,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) err = device_create_file(dev, &dev_attr_cpu0_vid); if (err) goto exit_release; - } else { + } else if (sio_data->kind != w83627uhg) { superio_select(sio_data->sioreg, W83627EHF_LD_HWM); if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { /* Set VID input sensibility if needed. In theory the @@ -2080,30 +2304,6 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) } } - /* fan4 and fan5 share some pins with the GPIO and serial flash */ - if (sio_data->kind == nct6775) { - /* On NCT6775, fan4 shares pins with the fdc interface */ - fan3pin = 1; - fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); - fan4min = 0; - fan5pin = 0; - } else if (sio_data->kind == nct6776) { - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); - fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); - fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); - fan4min = fan4pin; - } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { - fan3pin = 1; - fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; - fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; - fan4min = fan4pin; - } else { - fan3pin = 1; - fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); - fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); - fan4min = fan4pin; - } - if (fan_debounce && (sio_data->kind == nct6775 || sio_data->kind == nct6776)) { u8 tmp; @@ -2121,34 +2321,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) superio_exit(sio_data->sioreg); - /* It looks like fan4 and fan5 pins can be alternatively used - as fan on/off switches, but fan5 control is write only :/ - We assume that if the serial interface is disabled, designers - connected fan5 as input unless they are emitting log 1, which - is not the default. */ - - data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ - - data->has_fan |= (fan3pin << 2); - data->has_fan_min |= (fan3pin << 2); - - /* - * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 register - */ - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { - data->has_fan |= (fan4pin << 3) | (fan5pin << 4); - data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); - } else { - i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); - if ((i & (1 << 2)) && fan4pin) { - data->has_fan |= (1 << 3); - data->has_fan_min |= (1 << 3); - } - if (!(i & (1 << 1)) && fan5pin) { - data->has_fan |= (1 << 4); - data->has_fan_min |= (1 << 4); - } - } + w83627ehf_check_fan_inputs(sio_data, data); /* Read fan clock dividers immediately */ w83627ehf_update_fan_div_common(dev, data); @@ -2180,7 +2353,14 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit_remove; } } - /* if fan4 is enabled create the sf3 files for it */ + /* if fan3 and fan4 are enabled create the sf3 files for them */ + if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3) + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) { + err = device_create_file(dev, + &sda_sf3_arrays_fan3[i].dev_attr); + if (err) + goto exit_remove; + } if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { err = device_create_file(dev, @@ -2248,6 +2428,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if (err) goto exit_remove; } + if (i == 2 && data->temp3_val_only) + continue; if (data->reg_temp_over[i]) { err = device_create_file(dev, &sda_temp_max[i].dev_attr); @@ -2269,6 +2451,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit_remove; } + err = device_create_file(dev, &sda_caseopen[0].dev_attr); + if (err) + goto exit_remove; + + if (sio_data->kind == nct6776) { + err = device_create_file(dev, &sda_caseopen[1].dev_attr); + if (err) + goto exit_remove; + } + err = device_create_file(dev, &dev_attr_name); if (err) goto exit_remove; @@ -2321,6 +2513,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; + static const char __initdata sio_name_W83627UHG[] = "W83627UHG"; static const char __initdata sio_name_W83667HG[] = "W83667HG"; static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B"; static const char __initdata sio_name_NCT6775[] = "NCT6775F"; @@ -2353,6 +2546,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83627dhg_p; sio_name = sio_name_W83627DHG_P; break; + case SIO_W83627UHG_ID: + sio_data->kind = w83627uhg; + sio_name = sio_name_W83627UHG; + break; case SIO_W83667HG_ID: sio_data->kind = w83667hg; sio_name = sio_name_W83667HG; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index eed43a008be1..65b685e2c7b7 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1245,17 +1245,17 @@ w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) /* convert from ISA to LM75 I2C addresses */ switch (reg & 0xff) { case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); + res = i2c_smbus_read_word_swapped(cl, 0); break; case 0x52: /* CONFIG */ res = i2c_smbus_read_byte_data(cl, 1); break; case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); + res = i2c_smbus_read_word_swapped(cl, 2); break; case 0x55: /* OVER */ default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); + res = i2c_smbus_read_word_swapped(cl, 3); break; } } @@ -1289,10 +1289,10 @@ w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) i2c_smbus_write_byte_data(cl, 1, value & 0xff); break; case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); + i2c_smbus_write_word_swapped(cl, 2, value); break; case 0x55: /* OVER */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); + i2c_smbus_write_word_swapped(cl, 3, value); break; } } |