diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-23 14:37:52 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-23 14:37:52 -0700 |
commit | f37ab0fba271e43edab0e3ae9fe644fcda455402 (patch) | |
tree | 9389e993a99c7ad87c0e8643d464c48f3ac97eca | |
parent | 0b0c9d3a5872e8a02a071c6f0775ee6bf00a1206 (diff) | |
parent | 5510e62a66bad22b104d5d854445523d7f5754f7 (diff) |
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
Pull hwmon updates from Jean Delvare:
"We have support for the MCP3021, MC13892 and GMT G781, automatic fan
speed control for LM63/LM64 chips, and a few clean-ups."
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging:
hwmon: Add MCP3021 ADC driver
hwmon: (mc13783-adc) Add support for the MC13892 PMIC
hwmon: (mc13783-adc) Remove space before tab
hwmon: (lm63) Let the user adjust the lookup table
hwmon: (lm63) Make fan speed control strategy changeable
hwmon: (lm63) Reorganize the code
hwmon: (lm90) Restore original configuration if probe function fails
hwmon: (lm90) Add support for GMT G781
hwmon: (lm90) Fix multi-line comments
hwmon: (w83795) Fix multi-line comments
hwmon: (w83795) Unconditionally support manual fan speed control
hwmon: (fam15h_power) Increase output resolution
hwmon: (fam15h_power) Correct sign extension of running_avg_capture
-rw-r--r-- | Documentation/hwmon/lm90 | 4 | ||||
-rw-r--r-- | Documentation/hwmon/mc13783-adc | 50 | ||||
-rw-r--r-- | Documentation/hwmon/mcp3021 | 22 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 28 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/fam15h_power.c | 10 | ||||
-rw-r--r-- | drivers/hwmon/lm63.c | 586 | ||||
-rw-r--r-- | drivers/hwmon/lm90.c | 98 | ||||
-rw-r--r-- | drivers/hwmon/mc13783-adc.c | 103 | ||||
-rw-r--r-- | drivers/hwmon/mcp3021.c | 171 | ||||
-rw-r--r-- | drivers/hwmon/w83795.c | 132 |
11 files changed, 826 insertions, 379 deletions
diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 9cd14cfe651..b466974e142 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -118,6 +118,10 @@ Supported chips: Addresses scanned: I2C 0x48 through 0x4F Datasheet: Publicly available at NXP website http://ics.nxp.com/products/interface/datasheet/sa56004x.pdf + * GMT G781 + Prefix: 'g781' + Addresses scanned: I2C 0x4c, 0x4d + Datasheet: Not publicly available from GMT Author: Jean Delvare <khali@linux-fr.org> diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc index 044531a8640..d0e7b3fa9e7 100644 --- a/Documentation/hwmon/mc13783-adc +++ b/Documentation/hwmon/mc13783-adc @@ -3,8 +3,11 @@ Kernel driver mc13783-adc Supported chips: * Freescale Atlas MC13783 - Prefix: 'mc13783_adc' + Prefix: 'mc13783' Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1 + * Freescale Atlas MC13892 + Prefix: 'mc13892' + Datasheet: http://cache.freescale.com/files/analog/doc/data_sheet/MC13892.pdf?fsrch=1&sr=1 Authors: Sascha Hauer <s.hauer@pengutronix.de> @@ -13,20 +16,21 @@ Authors: Description ----------- -The Freescale MC13783 is a Power Management and Audio Circuit. Among -other things it contains a 10-bit A/D converter. The converter has 16 -channels which can be used in different modes. -The A/D converter has a resolution of 2.25mV. Channels 0-4 have -a dedicated meaning with chip internal scaling applied. Channels 5-7 -can be used as general purpose inputs or alternatively in a dedicated -mode. Channels 12-15 are occupied by the touchscreen if it's active. +The Freescale MC13783 and MC13892 are Power Management and Audio Circuits. +Among other things they contain a 10-bit A/D converter. The converter has 16 +(MC13783) resp. 12 (MC13892) channels which can be used in different modes. The +A/D converter has a resolution of 2.25mV. -Currently the driver only supports channels 2 and 5-15 with no alternative -modes for channels 5-7. +Some channels can be used as General Purpose inputs or in a dedicated mode with +a chip internal scaling applied . -See this table for the meaning of the different channels and their chip -internal scaling: +Currently the driver only supports the Application Supply channel (BP / BPSNS), +the General Purpose inputs and touchscreen. +See the following tables for the meaning of the different channels and their +chip internal scaling: + +MC13783: Channel Signal Input Range Scaling ------------------------------------------------------------------------------- 0 Battery Voltage (BATT) 2.50 - 4.65V -2.40V @@ -34,7 +38,7 @@ Channel Signal Input Range Scaling 2 Application Supply (BP) 2.50 - 4.65V -2.40V 3 Charger Voltage (CHRGRAW) 0 - 10V / /5 0 - 20V /10 -4 Charger Current (CHRGISNSP-CHRGISNSN) -0.25V - 0.25V x4 +4 Charger Current (CHRGISNSP-CHRGISNSN) -0.25 - 0.25V x4 5 General Purpose ADIN5 / Battery Pack Thermistor 0 - 2.30V No 6 General Purpose ADIN6 / Backup Voltage (LICELL) 0 - 2.30V / No / 1.50 - 3.50V -1.20V @@ -48,3 +52,23 @@ Channel Signal Input Range Scaling 13 General Purpose TSX2 / Touchscreen X-plate 2 0 - 2.30V No 14 General Purpose TSY1 / Touchscreen Y-plate 1 0 - 2.30V No 15 General Purpose TSY2 / Touchscreen Y-plate 2 0 - 2.30V No + +MC13892: +Channel Signal Input Range Scaling +------------------------------------------------------------------------------- +0 Battery Voltage (BATT) 0 - 4.8V /2 +1 Battery Current (BATT - BATTISNSCC) -60 - 60 mV x20 +2 Application Supply (BPSNS) 0 - 4.8V /2 +3 Charger Voltage (CHRGRAW) 0 - 12V / /5 + 0 - 20V /10 +4 Charger Current (CHRGISNS-BPSNS) / -0.3 - 0.3V / x4 / + Touchscreen X-plate 1 0 - 2.4V No +5 General Purpose ADIN5 / Battery Pack Thermistor 0 - 2.4V No +6 General Purpose ADIN6 / Backup Voltage (LICELL) 0 - 2.4V / No + Backup Voltage (LICELL) 0 - 3.6V x2/3 +7 General Purpose ADIN7 / UID / Die Temperature 0 - 2.4V / No / + 0 - 4.8V /2 +12 General Purpose TSX1 / Touchscreen X-plate 1 0 - 2.4V No +13 General Purpose TSX2 / Touchscreen X-plate 2 0 - 2.4V No +14 General Purpose TSY1 / Touchscreen Y-plate 1 0 - 2.4V No +15 General Purpose TSY2 / Touchscreen Y-plate 2 0 - 2.4V No diff --git a/Documentation/hwmon/mcp3021 b/Documentation/hwmon/mcp3021 new file mode 100644 index 00000000000..325fd87e81b --- /dev/null +++ b/Documentation/hwmon/mcp3021 @@ -0,0 +1,22 @@ +Kernel driver MCP3021 +====================== + +Supported chips: + * Microchip Technology MCP3021 + Prefix: 'mcp3021' + Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21805a.pdf + +Author: Mingkai Hu + +Description +----------- + +This driver implements support for the Microchip Technology MCP3021 chip. + +The Microchip Technology Inc. MCP3021 is a successive approximation A/D +converter (ADC) with 10-bit resolution. +This device provides one single-ended input with very low power consumption. +Communication to the MCP3021 is performed using a 2-wire I2C compatible +interface. Standard (100 kHz) and Fast (400 kHz) I2C modes are available. +The default I2C device address is 0x4d (contact the Microchip factory for +additional address options). diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 811e6c47e7e..5b32d56dbb4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -648,7 +648,8 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips. + Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781 + sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -812,6 +813,16 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_MCP3021 + tristate "Microchip MCP3021" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the MCP3021 chip + that is a A/D converter (ADC) with 10-bit resolution. + + This driver can also be built as a module. If so, the module + will be called mcp3021. + config SENSORS_NTC_THERMISTOR tristate "NTC thermistor support" depends on EXPERIMENTAL @@ -1229,18 +1240,19 @@ config SENSORS_W83795 depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Winbond W83795G and - W83795ADG hardware monitoring chip. + W83795ADG hardware monitoring chip, including manual fan speed + control. This driver can also be built as a module. If so, the module will be called w83795. config SENSORS_W83795_FANCTRL - boolean "Include fan control support (DANGEROUS)" + boolean "Include automatic fan control support (DANGEROUS)" depends on SENSORS_W83795 && EXPERIMENTAL default n help - If you say yes here, support for the both manual and automatic - fan control features will be included in the driver. + If you say yes here, support for automatic fan speed control + will be included in the driver. This part of the code wasn't carefully reviewed and tested yet, so enabling this option is strongly discouraged on production @@ -1358,10 +1370,10 @@ config SENSORS_APPLESMC the awesome power of applesmc. config SENSORS_MC13783_ADC - tristate "Freescale MC13783 ADC" - depends on MFD_MC13783 + tristate "Freescale MC13783/MC13892 ADC" + depends on MFD_MC13XXX help - Support for the A/D converter on MC13783 PMIC. + Support for the A/D converter on MC13783 and MC13892 PMIC. if ACPI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8251ce8cd03..6d3f11f7181 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 523f8fb9e7d..b7494af1e4a 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -60,15 +60,15 @@ static ssize_t show_power(struct device *dev, pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_RUNNING_AVERAGE, &val); running_avg_capture = (val >> 4) & 0x3fffff; - running_avg_capture = sign_extend32(running_avg_capture, 22); - running_avg_range = val & 0xf; + running_avg_capture = sign_extend32(running_avg_capture, 21); + running_avg_range = (val & 0xf) + 1; pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_LIMIT3, &val); tdp_limit = val >> 16; - curr_pwr_watts = tdp_limit + data->base_tdp - - (s32)(running_avg_capture >> (running_avg_range + 1)); + curr_pwr_watts = (tdp_limit + data->base_tdp) << running_avg_range; + curr_pwr_watts -= running_avg_capture; curr_pwr_watts *= data->tdp_to_watts; /* @@ -78,7 +78,7 @@ static ssize_t show_power(struct device *dev, * scaling factor 1/(2^16). For conversion we use * (10^6)/(2^16) = 15625/(2^10) */ - curr_pwr_watts = (curr_pwr_watts * 15625) >> 10; + curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range); return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts); } static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 15c05cc83e2..602a0f0b0de 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -148,46 +148,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define UPDATE_INTERVAL(max, rate) \ ((1000 << (LM63_MAX_CONVRATE - (rate))) / (max)) -/* - * Functions declaration - */ - -static int lm63_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm63_remove(struct i2c_client *client); - -static struct lm63_data *lm63_update_device(struct device *dev); - -static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm63_init_client(struct i2c_client *client); - enum chips { lm63, lm64, lm96163 }; /* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id lm63_id[] = { - { "lm63", lm63 }, - { "lm64", lm64 }, - { "lm96163", lm96163 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm63_id); - -static struct i2c_driver lm63_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm63", - }, - .probe = lm63_probe, - .remove = lm63_remove, - .id_table = lm63_id, - .detect = lm63_detect, - .address_list = normal_i2c, -}; - -/* * Client data (each client gets its own) */ @@ -242,6 +205,145 @@ static inline int lut_temp_from_reg(struct lm63_data *data, int nr) return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); } +static inline int lut_temp_to_reg(struct lm63_data *data, long val) +{ + val -= data->temp2_offset; + if (data->lut_temp_highres) + return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127500), 500); + else + return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127000), 1000); +} + +/* + * Update the lookup table register cache. + * client->update_lock must be held when calling this function. + */ +static void lm63_update_lut(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + int i; + + if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || + !data->lut_valid) { + for (i = 0; i < data->lut_size; i++) { + data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_PWM(i)); + data->temp8[3 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP(i)); + } + data->lut_temp_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP_HYST); + + data->lut_last_updated = jiffies; + data->lut_valid = 1; + } +} + +static struct lm63_data *lm63_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long next_update; + + mutex_lock(&data->update_lock); + + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + + if (time_after(jiffies, next_update) || !data->valid) { + if (data->config & 0x04) { /* tachometer enabled */ + /* order matters for fan1_input */ + data->fan[0] = i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_LSB) & 0xFC; + data->fan[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_MSB) << 8; + data->fan[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_LSB) & 0xFC) + | (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_MSB) << 8); + } + + data->pwm1_freq = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_FREQ); + if (data->pwm1_freq == 0) + data->pwm1_freq = 1; + data->pwm1[0] = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_VALUE); + + data->temp8[0] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_TEMP); + data->temp8[1] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_HIGH); + + /* order matters for temp2_input */ + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_MSB) << 8; + data->temp11[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_LSB); + data->temp11[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_LSB); + data->temp11[2] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_LSB); + data->temp11[3] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_LSB); + + if (data->kind == lm96163) + data->temp11u = (i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_LSB); + + data->temp8[2] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT); + data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT_HYST); + + data->alarms = i2c_smbus_read_byte_data(client, + LM63_REG_ALERT_STATUS) & 0x7F; + + data->last_updated = jiffies; + data->valid = 1; + } + + lm63_update_lut(client); + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Trip points in the lookup table should be in ascending order for both + * temperatures and PWM output values. + */ +static int lm63_lut_looks_bad(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + int i; + + mutex_lock(&data->update_lock); + lm63_update_lut(client); + + for (i = 1; i < data->lut_size; i++) { + if (data->pwm1[1 + i - 1] > data->pwm1[1 + i] + || data->temp8[3 + i - 1] > data->temp8[3 + i]) { + dev_warn(&client->dev, + "Lookup table doesn't look sane (check entries %d and %d)\n", + i, i + 1); + break; + } + } + mutex_unlock(&data->update_lock); + + return i == data->lut_size ? 0 : 1; +} + /* * Sysfs callback functions and files */ @@ -294,13 +396,16 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", pwm); } -static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, +static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); + int nr = attr->index; unsigned long val; int err; + u8 reg; if (!(data->config_fan & 0x20)) /* register is read-only */ return -EPERM; @@ -309,11 +414,13 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, if (err) return err; + reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE; val = SENSORS_LIMIT(val, 0, 255); + mutex_lock(&data->update_lock); - data->pwm1[0] = data->pwm_highres ? val : + data->pwm1[nr] = data->pwm_highres ? val : (val * data->pwm1_freq * 2 + 127) / 255; - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]); + i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]); mutex_unlock(&data->update_lock); return count; } @@ -325,6 +432,41 @@ static ssize_t show_pwm1_enable(struct device *dev, return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); } +static ssize_t set_pwm1_enable(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val < 1 || val > 2) + return -EINVAL; + + /* + * Only let the user switch to automatic mode if the lookup table + * looks sane. + */ + if (val == 2 && lm63_lut_looks_bad(client)) + return -EPERM; + + mutex_lock(&data->update_lock); + data->config_fan = i2c_smbus_read_byte_data(client, + LM63_REG_CONFIG_FAN); + if (val == 1) + data->config_fan |= 0x20; + else + data->config_fan &= ~0x20; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, + data->config_fan); + mutex_unlock(&data->update_lock); + return count; +} + /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, @@ -367,23 +509,31 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); int nr = attr->index; - int reg = nr == 2 ? LM63_REG_REMOTE_TCRIT : LM63_REG_LOCAL_HIGH; long val; int err; int temp; + u8 reg; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); - if (nr == 2) { + switch (nr) { + case 2: + reg = LM63_REG_REMOTE_TCRIT; if (data->remote_unsigned) temp = TEMP8U_TO_REG(val - data->temp2_offset); else temp = TEMP8_TO_REG(val - data->temp2_offset); - } else { + break; + case 1: + reg = LM63_REG_LOCAL_HIGH; temp = TEMP8_TO_REG(val); + break; + default: /* lookup table */ + reg = LM63_REG_LUT_TEMP(nr - 3); + temp = lut_temp_to_reg(data, val); } data->temp8[nr] = temp; i2c_smbus_write_byte_data(client, reg, temp); @@ -613,65 +763,78 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, set_fan, 1); static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); -static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1); -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO, - show_lut_temp, NULL, 3); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwm1_enable, set_pwm1_enable); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 1); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 3); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2); -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, - show_lut_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 2); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 4); static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO, - show_lut_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 5); static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO, - show_lut_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 6); static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 6); -static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO, - show_lut_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 7); static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 7); -static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6); -static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO, - show_lut_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 8); static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 8); -static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7); -static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO, - show_lut_temp, NULL, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 9); static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 9); -static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8); -static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO, - show_lut_temp, NULL, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 10); static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 10); -static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IRUGO, show_pwm1, NULL, 9); -static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IRUGO, - show_lut_temp, NULL, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 11); static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 11); -static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IRUGO, show_pwm1, NULL, 10); -static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IRUGO, - show_lut_temp, NULL, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 12); static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 12); -static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IRUGO, show_pwm1, NULL, 11); -static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IRUGO, - show_lut_temp, NULL, 13); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 13); static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 13); -static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IRUGO, show_pwm1, NULL, 12); -static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IRUGO, - show_lut_temp, NULL, 14); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 14); static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 14); @@ -817,28 +980,25 @@ static const struct attribute_group lm63_group_fan1 = { */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm63_detect(struct i2c_client *new_client, +static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; u8 man_id, chip_id, reg_config1, reg_config2; u8 reg_alert_status, reg_alert_mask; - int address = new_client->addr; + int address = client->addr; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID); + man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG1); - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG2); - reg_alert_status = i2c_smbus_read_byte_data(new_client, + reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); + reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2); + reg_alert_status = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_STATUS); - reg_alert_mask = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_MASK); + reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK); if (man_id != 0x01 /* National Semiconductor */ || (reg_config1 & 0x18) != 0x00 @@ -863,74 +1023,6 @@ static int lm63_detect(struct i2c_client *new_client, return 0; } -static int lm63_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct lm63_data *data; - int err; - - data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Set the device type */ - data->kind = id->driver_data; - if (data->kind == lm64) - data->temp2_offset = 16000; - - /* Initialize chip */ - lm63_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm63_group); - if (err) - goto exit_free; - if (data->config & 0x04) { /* tachometer enabled */ - err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_fan1); - if (err) - goto exit_remove_files; - } - if (data->kind == lm96163) { - err = device_create_file(&new_client->dev, - &dev_attr_temp2_type); - if (err) - goto exit_remove_files; - - err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_extra_lut); - if (err) - goto exit_remove_files; - } - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm63_group); - sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1); - if (data->kind == lm96163) { - device_remove_file(&new_client->dev, &dev_attr_temp2_type); - sysfs_remove_group(&new_client->dev.kobj, - &lm63_group_extra_lut); - } -exit_free: - kfree(data); -exit: - return err; -} - /* * Ideally we shouldn't have to initialize anything, since the BIOS * should have taken care of everything @@ -1010,114 +1102,110 @@ static void lm63_init_client(struct i2c_client *client) (data->config_fan & 0x20) ? "manual" : "auto"); } -static int lm63_remove(struct i2c_client *client) +static int lm63_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct lm63_data *data = i2c_get_clientdata(client); + struct lm63_data *data; + int err; - hwmon_device_unregister(data->hwmon_dev); + data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + /* Set the device type */ + data->kind = id->driver_data; + if (data->kind == lm64) + data->temp2_offset = 16000; + + /* Initialize chip */ + lm63_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &lm63_group); + if (err) + goto exit_free; + if (data->config & 0x04) { /* tachometer enabled */ + err = sysfs_create_group(&client->dev.kobj, &lm63_group_fan1); + if (err) + goto exit_remove_files; + } + if (data->kind == lm96163) { + err = device_create_file(&client->dev, &dev_attr_temp2_type); + if (err) + goto exit_remove_files; + + err = sysfs_create_group(&client->dev.kobj, + &lm63_group_extra_lut); + if (err) + goto exit_remove_files; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: sysfs_remove_group(&client->dev.kobj, &lm63_group); sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); if (data->kind == lm96163) { device_remove_file(&client->dev, &dev_attr_temp2_type); sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut); } - +exit_free: kfree(data); - return 0; +exit: + return err; } -static struct lm63_data *lm63_update_device(struct device *dev) +static int lm63_remove(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); - unsigned long next_update; - int i; - - mutex_lock(&data->update_lock); - - next_update = data->last_updated - + msecs_to_jiffies(data->update_interval) + 1; - - if (time_after(jiffies, next_update) || !data->valid) { - if (data->config & 0x04) { /* tachometer enabled */ - /* order matters for fan1_input */ - data->fan[0] = i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_LSB) & 0xFC; - data->fan[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_MSB) << 8; - data->fan[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_LSB) & 0xFC) - | (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_MSB) << 8); - } - - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); - if (data->pwm1_freq == 0) - data->pwm1_freq = 1; - data->pwm1[0] = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_VALUE); - - data->temp8[0] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_TEMP); - data->temp8[1] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_HIGH); - - /* order matters for temp2_input */ - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_MSB) << 8; - data->temp11[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_LSB); - data->temp11[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_LSB); - data->temp11[2] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_LSB); - data->temp11[3] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_OFFSET_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_OFFSET_LSB); - - if (data->kind == lm96163) - data->temp11u = (i2c_smbus_read_byte_data(client, - LM96163_REG_REMOTE_TEMP_U_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM96163_REG_REMOTE_TEMP_U_LSB); - - data->temp8[2] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT); - data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT_HYST); - - data->alarms = i2c_smbus_read_byte_data(client, - LM63_REG_ALERT_STATUS) & 0x7F; |