diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx new file mode 100644 index 000000000000..f50a6cc27616 --- /dev/null +++ b/Documentation/hwmon/ina2xx @@ -0,0 +1,29 @@ +Kernel driver ina2xx +==================== + +Supported chips: + * Texas Instruments INA219 + Prefix: 'ina219' + Addresses: I2C 0x40 - 0x4f + Datasheet: Publicly available at the Texas Instruments website + http://www.ti.com/ + + * Texas Instruments INA226 + Prefix: 'ina226' + Addresses: I2C 0x40 - 0x4f + Datasheet: Publicly available at the Texas Instruments website + http://www.ti.com/ + +Author: Lothar Felten + +Description +----------- + +The INA219 is a high-side current shunt and power monitor with an I2C +interface. The INA219 monitors both shunt drop and supply voltage, with +programmable conversion times and filtering. + +The INA226 is a current shunt and power monitor with an I2C interface. +The INA226 monitors both a shunt voltage drop and bus supply voltage. + +The shunt value in micro-ohms can be set via platform data. diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index fba745571684..87850d86c559 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -30,6 +30,14 @@ Supported chips: Prefix: 'it8728' Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Not publicly available + * IT8782F + Prefix: 'it8782' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available + * IT8783E/F + Prefix: 'it8783' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available * SiS950 [clone of IT8705F] Prefix: 'it87' Addresses scanned: from Super I/O config space (8 I/O ports) @@ -75,7 +83,8 @@ Description ----------- This driver implements support for the IT8705F, IT8712F, IT8716F, -IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E and SiS950 chips. +IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8781F, IT8782F, +IT8783E/F, and SiS950 chips. These chips are 'Super I/O chips', supporting floppy disks, infrared ports, joysticks and other miscellaneous stuff. For hardware monitoring, they @@ -99,11 +108,11 @@ The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E and later IT8712F revisions have support for 2 additional fans. The additional fans are supported by the driver. -The IT8716F, IT8718F, IT8720F and IT8721F/IT8758E, and late IT8712F and -IT8705F also have optional 16-bit tachometer counters for fans 1 to 3. This -is better (no more fan clock divider mess) but not compatible with the older -chips and revisions. The 16-bit tachometer mode is enabled by the driver when -one of the above chips is detected. +The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E, IT8782F, IT8783E/F, and late +IT8712F and IT8705F also have optional 16-bit tachometer counters for fans 1 to +3. This is better (no more fan clock divider mess) but not compatible with the +older chips and revisions. The 16-bit tachometer mode is enabled by the driver +when one of the above chips is detected. The IT8726F is just bit enhanced IT8716F with additional hardware for AMD power sequencing. Therefore the chip will appear as IT8716F @@ -131,9 +140,10 @@ inputs can measure voltages between 0 and 4.08 volts, with a resolution of 0.016 volt (except IT8721F/IT8758E and IT8728F: 0.012 volt.) The battery voltage in8 does not have limit registers. -On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside -the chip (in7, in8 and optionally in3). The driver handles this transparently -so user-space doesn't have to care. +On the IT8721F/IT8758E, IT8782F, and IT8783E/F, some voltage inputs are +internal and scaled inside the chip (in7 (optional for IT8782F and IT8783E/F), +in8 and optionally in3). The driver handles this transparently so user-space +doesn't have to care. The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value: the voltage level your processor should work with. This is hardcoded by diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e466ecba8dc1..7cd9bf42108b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1102,6 +1102,19 @@ config SENSORS_AMC6821 This driver can also be build as a module. If so, the module will be called amc6821. +config SENSORS_INA2XX + tristate "Texas Instruments INA219, INA226" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for INA219 and INA226 power + monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. + Default value for Rshunt is 10 mOhms. + This driver can also be built as a module. If so, the module + will be called ina2xx. + config SENSORS_THMC50 tristate "Texas Instruments THMC50 / Analog Devices ADM1022" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6d3f11f71815..e1eeac13b851 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o +obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 9140236a0182..34ad5a27a7e9 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -107,15 +107,7 @@ struct acpi_power_meter_resource { struct kobject *holders_dir; }; -struct ro_sensor_template { - char *label; - ssize_t (*show)(struct device *dev, - struct device_attribute *devattr, - char *buf); - int index; -}; - -struct rw_sensor_template { +struct sensor_template { char *label; ssize_t (*show)(struct device *dev, struct device_attribute *devattr, @@ -469,52 +461,67 @@ static ssize_t show_name(struct device *dev, return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME); } +#define RO_SENSOR_TEMPLATE(_label, _show, _index) \ + { \ + .label = _label, \ + .show = _show, \ + .index = _index, \ + } + +#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \ + { \ + .label = _label, \ + .show = _show, \ + .set = _set, \ + .index = _index, \ + } + /* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */ -static struct ro_sensor_template meter_ro_attrs[] = { -{POWER_AVERAGE_NAME, show_power, 0}, -{"power1_accuracy", show_accuracy, 0}, -{"power1_average_interval_min", show_val, 0}, -{"power1_average_interval_max", show_val, 1}, -{"power1_is_battery", show_val, 5}, -{NULL, NULL, 0}, +static struct sensor_template meter_attrs[] = { + RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0), + RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0), + RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0), + RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1), + RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5), + RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval, + set_avg_interval, 0), + {}, }; -static struct rw_sensor_template meter_rw_attrs[] = { -{POWER_AVG_INTERVAL_NAME, show_avg_interval, set_avg_interval, 0}, -{NULL, NULL, NULL, 0}, +static struct sensor_template misc_cap_attrs[] = { + RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2), + RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3), + RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4), + RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6), + {}, }; -static struct ro_sensor_template misc_cap_attrs[] = { -{"power1_cap_min", show_val, 2}, -{"power1_cap_max", show_val, 3}, -{"power1_cap_hyst", show_val, 4}, -{POWER_ALARM_NAME, show_val, 6}, -{NULL, NULL, 0}, +static struct sensor_template ro_cap_attrs[] = { + RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0), + {}, }; -static struct ro_sensor_template ro_cap_attrs[] = { -{POWER_CAP_NAME, show_cap, 0}, -{NULL, NULL, 0}, +static struct sensor_template rw_cap_attrs[] = { + RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0), + {}, }; -static struct rw_sensor_template rw_cap_attrs[] = { -{POWER_CAP_NAME, show_cap, set_cap, 0}, -{NULL, NULL, NULL, 0}, +static struct sensor_template trip_attrs[] = { + RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7), + RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8), + {}, }; -static struct rw_sensor_template trip_attrs[] = { -{"power1_average_min", show_val, set_trip, 7}, -{"power1_average_max", show_val, set_trip, 8}, -{NULL, NULL, NULL, 0}, +static struct sensor_template misc_attrs[] = { + RO_SENSOR_TEMPLATE("name", show_name, 0), + RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0), + RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2), + RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1), + {}, }; -static struct ro_sensor_template misc_attrs[] = { -{"name", show_name, 0}, -{"power1_model_number", show_str, 0}, -{"power1_oem_info", show_str, 2}, -{"power1_serial_number", show_str, 1}, -{NULL, NULL, 0}, -}; +#undef RO_SENSOR_TEMPLATE +#undef RW_SENSOR_TEMPLATE /* Read power domain data */ static void remove_domain_devices(struct acpi_power_meter_resource *resource) @@ -619,19 +626,24 @@ end: } /* Registration and deregistration */ -static int register_ro_attrs(struct acpi_power_meter_resource *resource, - struct ro_sensor_template *ro) +static int register_attrs(struct acpi_power_meter_resource *resource, + struct sensor_template *attrs) { struct device *dev = &resource->acpi_dev->dev; struct sensor_device_attribute *sensors = &resource->sensors[resource->num_sensors]; int res = 0; - while (ro->label) { - sensors->dev_attr.attr.name = ro->label; + while (attrs->label) { + sensors->dev_attr.attr.name = attrs->label; sensors->dev_attr.attr.mode = S_IRUGO; - sensors->dev_attr.show = ro->show; - sensors->index = ro->index; + sensors->dev_attr.show = attrs->show; + sensors->index = attrs->index; + + if (attrs->set) { + sensors->dev_attr.attr.mode |= S_IWUSR; + sensors->dev_attr.store = attrs->set; + } sysfs_attr_init(&sensors->dev_attr.attr); res = device_create_file(dev, &sensors->dev_attr); @@ -641,37 +653,7 @@ static int register_ro_attrs(struct acpi_power_meter_resource *resource, } sensors++; resource->num_sensors++; - ro++; - } - -error: - return res; -} - -static int register_rw_attrs(struct acpi_power_meter_resource *resource, - struct rw_sensor_template *rw) -{ - struct device *dev = &resource->acpi_dev->dev; - struct sensor_device_attribute *sensors = - &resource->sensors[resource->num_sensors]; - int res = 0; - - while (rw->label) { - sensors->dev_attr.attr.name = rw->label; - sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR; - sensors->dev_attr.show = rw->show; - sensors->dev_attr.store = rw->set; - sensors->index = rw->index; - - sysfs_attr_init(&sensors->dev_attr.attr); - res = device_create_file(dev, &sensors->dev_attr); - if (res) { - sensors->dev_attr.attr.name = NULL; - goto error; - } - sensors++; - resource->num_sensors++; - rw++; + attrs++; } error: @@ -703,10 +685,7 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) return res; if (resource->caps.flags & POWER_METER_CAN_MEASURE) { - res = register_ro_attrs(resource, meter_ro_attrs); - if (res) - goto error; - res = register_rw_attrs(resource, meter_rw_attrs); + res = register_attrs(resource, meter_attrs); if (res) goto error; } @@ -718,28 +697,27 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) goto skip_unsafe_cap; } - if (resource->caps.configurable_cap) { - res = register_rw_attrs(resource, rw_cap_attrs); - if (res) - goto error; - } else { - res = register_ro_attrs(resource, ro_cap_attrs); - if (res) - goto error; - } - res = register_ro_attrs(resource, misc_cap_attrs); + if (resource->caps.configurable_cap) + res = register_attrs(resource, rw_cap_attrs); + else + res = register_attrs(resource, ro_cap_attrs); + + if (res) + goto error; + + res = register_attrs(resource, misc_cap_attrs); if (res) goto error; } + skip_unsafe_cap: - if (resource->caps.flags & POWER_METER_CAN_TRIP) { - res = register_rw_attrs(resource, trip_attrs); + res = register_attrs(resource, trip_attrs); if (res) goto error; } - res = register_ro_attrs(resource, misc_attrs); + res = register_attrs(resource, misc_attrs); if (res) goto error; diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c index f85ce70d9677..cfec802cf9ca 100644 --- a/drivers/hwmon/ad7314.c +++ b/drivers/hwmon/ad7314.c @@ -17,22 +17,15 @@ #include #include -/* - * 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 +#define AD7314_TEMP_SHIFT 5 /* * ADT7301 and ADT7302 temperature masks */ -#define ADT7301_TEMP_SIGN 0x2000 #define ADT7301_TEMP_MASK 0x3FFF enum ad7314_variant { @@ -73,7 +66,7 @@ static ssize_t ad7314_show_temperature(struct device *dev, return ret; switch (spi_get_device_id(chip->spi_dev)->driver_data) { case ad7314: - data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET; + data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT; data = (data << 6) >> 6; return sprintf(buf, "%d\n", 250 * data); diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index e8e18cab1fb8..6b13f1a4dc27 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -257,15 +257,4 @@ static struct pci_driver fam15h_power_driver = { .remove = __devexit_p(fam15h_power_remove), }; -static int __init fam15h_power_init(void) -{ - return pci_register_driver(&fam15h_power_driver); -} - -static void __exit fam15h_power_exit(void) -{ - pci_unregister_driver(&fam15h_power_driver); -} - -module_init(fam15h_power_init) -module_exit(fam15h_power_exit) +module_pci_driver(fam15h_power_driver); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c new file mode 100644 index 000000000000..7f3f4a385729 --- /dev/null +++ b/drivers/hwmon/ina2xx.c @@ -0,0 +1,368 @@ +/* + * Driver for Texas Instruments INA219, INA226 power monitor chips + * + * INA219: + * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: http://www.ti.com/product/ina219 + * + * INA226: + * Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: http://www.ti.com/product/ina226 + * + * Copyright (C) 2012 Lothar Felten + * Thanks to Jan Volkering + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* common register definitions */ +#define INA2XX_CONFIG 0x00 +#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ +#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */ +#define INA2XX_POWER 0x03 /* readonly */ +#define INA2XX_CURRENT 0x04 /* readonly */ +#define INA2XX_CALIBRATION 0x05 + +/* INA226 register definitions */ +#define INA226_MASK_ENABLE 0x06 +#define INA226_ALERT_LIMIT 0x07 +#define INA226_DIE_ID 0xFF + + +/* register count */ +#define INA219_REGISTERS 6 +#define INA226_REGISTERS 8 + +#define INA2XX_MAX_REGISTERS 8 + +/* settings - depend on use case */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ + +/* worst case is 68.10 ms (~14.6Hz, ina219) */ +#define INA2XX_CONVERSION_RATE 15 + +enum ina2xx_ids { ina219, ina226 }; + +struct ina2xx_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; + + int kind; + int registers; + u16 regs[INA2XX_MAX_REGISTERS]; +}; + +int ina2xx_read_word(struct i2c_client *client, int reg) +{ + int val = i2c_smbus_read_word_data(client, reg); + if (unlikely(val < 0)) { + dev_dbg(&client->dev, + "Failed to read register: %d\n", reg); + return val; + } + return be16_to_cpu(val); +} + +void ina2xx_write_word(struct i2c_client *client, int reg, int data) +{ + i2c_smbus_write_word_data(client, reg, cpu_to_be16(data)); +} + +static struct ina2xx_data *ina2xx_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina2xx_data *data = i2c_get_clientdata(client); + struct ina2xx_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + + HZ / INA2XX_CONVERSION_RATE) || !data->valid) { + + int i; + + dev_dbg(&client->dev, "Starting ina2xx update\n"); + + /* Read all registers */ + for (i = 0; i < data->registers; i++) { + int rv = ina2xx_read_word(client, i); + if (rv < 0) { + ret = ERR_PTR(rv); + goto abort; + } + data->regs[i] = rv; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int ina219_get_value(struct ina2xx_data *data, u8 reg) +{ + /* + * calculate exact value for the given register + * we assume default power-on reset settings: + * bus voltage range 32V + * gain = /8 + * adc 1 & 2 -> conversion time 532uS + * mode is continuous shunt and bus + * calibration value is INA219_CALIBRATION_VALUE + */ + int val = data->regs[reg]; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* LSB=10uV. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val, 100); + break; + case INA2XX_BUS_VOLTAGE: + /* LSB=4mV. Register is not right aligned, convert to mV. */ + val = (val >> 3) * 4; + break; + case INA2XX_POWER: + /* LSB=20mW. Convert to uW */ + val = val * 20 * 1000; + break; + case INA2XX_CURRENT: + /* LSB=1mA (selected). Is in mA */ + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static int ina226_get_value(struct ina2xx_data *data, u8 reg) +{ + /* + * calculate exact value for the given register + * we assume default power-on reset settings: + * bus voltage range 32V + * gain = /8 + * adc 1 & 2 -> conversion time 532uS + * mode is continuous shunt and bus + * calibration value is INA226_CALIBRATION_VALUE + */ + int val = data->regs[reg]; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* LSB=2.5uV. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val, 400); + break; + case INA2XX_BUS_VOLTAGE: + /* LSB=1.25mV. Convert to mV. */ + val = val + DIV_ROUND_CLOSEST(val, 4); + break; + case INA2XX_POWER: + /* LSB=25mW. Convert to uW */ + val = val * 25 * 1000; + break; + case INA2XX_CURRENT: + /* LSB=1mA (selected). Is in mA */ + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ina2xx_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = ina2xx_update_device(dev); + int value = 0; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (data->kind) { + case ina219: + value = ina219_get_value(data, attr->index); + break; + case ina226: + value = ina226_get_value(data, attr->index); + break; + default: + WARN_ON_ONCE(1); + break; + } + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +/* shunt voltage */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \ + ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE); + +/* bus voltage */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \ + ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE); + +/* calculated current */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \ + ina2xx_show_value, NULL, INA2XX_CURRENT); + +/* calculated power */ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \ + ina2xx_show_value, NULL, INA2XX_POWER); + +/* pointers to created device attributes */ +static struct attribute *ina2xx_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ina2xx_group = { + .attrs = ina2xx_attributes, +}; + +static int ina2xx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ina2xx_data *data; + struct ina2xx_platform_data *pdata; + int ret = 0; + long shunt = 10000; /* default shunt value 10mOhms */ + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (client->dev.platform_data) { + pdata = + (struct ina2xx_platform_data *)client->dev.platform_data; + shunt = pdata->shunt_uohms; + } + + if (shunt <= 0) + return -ENODEV; + + /* set the device type */ + data->kind = id->driver_data; + + switch (data->kind) { + case ina219: + /* device configuration */ + ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT); + + /* set current LSB to 1mA, shunt is in uOhms */ + /* (equation 13 in datasheet) */ + ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt); + dev_info(&client->dev, + "power monitor INA219 (Rshunt = %li uOhm)\n", shunt); + data->registers = INA219_REGISTERS; + break; + case ina226: + /* device configuration */ + ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT); + + /* set current LSB to 1mA, shunt is in uOhms */ + /* (equation 1 in datasheet)*/ + ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt); + dev_info(&client->dev, + "power monitor INA226 (Rshunt = %li uOhm)\n", shunt); + data->registers = INA226_REGISTERS; + break; + default: + /* unknown device id */ + return -ENODEV; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + ret = sysfs_create_group(&client->dev.kobj, &ina2xx_group); + if (ret) + return ret; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_err_hwmon; + } + + return 0; + +out_err_hwmon: + sysfs_remove_group(&client->dev.kobj, &ina2xx_group); + return ret; +} + +static int ina2xx_remove(struct i2c_client *client) +{ + struct ina2xx_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ina2xx_group); + + return 0; +} + +static const struct i2c_device_id ina2xx_id[] = { + { "ina219", ina219 }, + { "ina226", ina226 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina2xx_id); + +static struct i2c_driver ina2xx_driver = { + .driver = { + .name = "ina2xx", + }, + .probe = ina2xx_probe, + .remove = ina2xx_remove, + .id_table = ina2xx_id, +}; + +static int __init ina2xx_init(void) +{ + return i2c_add_driver(&ina2xx_driver); +} + +static void __exit ina2xx_exit(void) +{ + i2c_del_driver(&ina2xx_driver); +} + +MODULE_AUTHOR("Lothar Felten "); +MODULE_DESCRIPTION("ina2xx driver"); +MODULE_LICENSE("GPL"); + +module_init(ina2xx_init); +module_exit(ina2xx_exit); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0b204e4cf51c..e7701d99f8e8 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -19,6 +19,8 @@ * IT8726F Super I/O chip w/LPC interface * IT8728F Super I/O chip w/LPC interface * IT8758E Super I/O chip w/LPC interface + * IT8782F Super I/O chip w/LPC interface + * IT8783E/F Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -59,7 +61,8 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8782, + it8783 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -137,13 +140,18 @@ static inline void superio_exit(void) #define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT8728F_DEVID 0x8728 +#define IT8782F_DEVID 0x8782 +#define IT8783E_DEVID 0x8783 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 /* Logical device 7 registers (IT8712F and later) */ +#define IT87_SIO_GPIO1_REG 0x25 #define IT87_SIO_GPIO3_REG 0x27 #define IT87_SIO_GPIO5_REG 0x29 +#define IT87_SIO_PINX1_REG 0x2a /* Pin selection */ #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ +#define IT87_SIO_SPI_REG 0xef /* SPI function pin select */ #define IT87_SIO_VID_REG 0xfc /* VID value */ #define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */ @@ -210,6 +218,7 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_VIN_ENABLE 0x50 #define IT87_REG_TEMP_ENABLE 0x51 +#define IT87_REG_TEMP_EXTRA 0x55 #define IT87_REG_BEEP_ENABLE 0x5c #define IT87_REG_CHIPID 0x58 @@ -226,9 +235,11 @@ struct it87_sio_data { u8 beep_pin; u8 internal; /* Internal sensors can be labeled */ /* Features skipped based on config or DMI */ + u16 skip_in; u8 skip_vid; u8 skip_fan; u8 skip_pwm; + u8 skip_temp; }; /* @@ -253,6 +264,7 @@ struct it87_data { u8 has_fan; /* Bitfield, fans enabled */ u16 fan[5]; /* Register values, possibly combined */ u16 fan_min[5]; /* Register values, possibly combined */ + u8 has_temp; /* Bitfield, temp sensors enabled */ s8 temp[3]; /* Register value */ s8 temp_high[3]; /* Register value */ s8 temp_low[3]; /* Register value */ @@ -304,31 +316,23 @@ static inline int has_newer_autopwm(const struct it87_data *data) || data->type == it8728; } +static int adc_lsb(const struct it87_data *data, int nr) +{ + int lsb = has_12mv_adc(data) ? 12 : 16; + if (data->in_scaled & (1 << nr)) + lsb <<= 1; + return lsb; +} + static u8 in_to_reg(const struct it87_data *data, int nr, long val) { - long lsb; - - if (has_12mv_adc(data)) { - if (data->in_scaled & (1 << nr)) - lsb = 24; - else - lsb = 12; - } else - lsb = 16; - - val = DIV_ROUND_CLOSEST(val, lsb); + val = DIV_ROUND_CLOSEST(val, adc_lsb(data, nr)); return SENSORS_LIMIT(val, 0, 255); } static int in_from_reg(const struct it87_data *data, int nr, int val) { - if (has_12mv_adc(data)) { - if (data->in_scaled & (1 << nr)) - return val * 24; - else - return val * 12; - } else - return val * 16; + return val * adc_lsb(data, nr); } static inline u8 FAN_TO_REG(long rpm, int div) @@ -407,7 +411,9 @@ static inline int has_16bit_fans(const struct it87_data *data) || data->type == it8718 || data->type == it8720 || data->type == it8721 - || data->type == it8728; + || data->type == it8728 + || data->type == it8782 + || data->type == it8783; } static inline int has_old_autopwm(const struct it87_data *data) @@ -1369,57 +1375,103 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static struct attribute *it87_attributes[] = { +static struct attribute *it87_attributes_in[9][5] = { +{ &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in3_min.dev_attr.attr, - &sensor_dev_attr_in4_min.dev_attr.attr, - &sensor_dev_attr_in5_min.dev_attr.attr, - &sensor_dev_attr_in6_min.dev_attr.attr, - &sensor_dev_attr_in7_min.dev_attr.attr, &sensor_dev_attr_in0_max.dev_attr.attr, - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in3_max.dev_attr.attr, - &sensor_dev_attr_in4_max.dev_attr.attr, - &sensor_dev_attr_in5_max.dev_attr.attr, - &sensor_dev_attr_in6_max.dev_attr.attr, - &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in8_input.dev_attr.attr, + NULL +} }; +static const struct attribute_group it87_group_in[9] = { + { .attrs = it87_attributes_in[0] }, + { .attrs = it87_attributes_in[1] }, + { .attrs = it87_attributes_in[2] }, + { .attrs = it87_attributes_in[3] }, + { .attrs = it87_attributes_in[4] }, + { .attrs = it87_attributes_in[5] }, + { .attrs = it87_attributes_in[6] }, + { .attrs = it87_attributes_in[7] }, + { .attrs = it87_attributes_in[8] }, +}; + +static struct attribute *it87_attributes_temp[3][6] = { +{ &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp1_type.dev_attr.attr, - &sensor_dev_attr_temp2_type.dev_attr.attr, - &sensor_dev_attr_temp3_type.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, + NULL +} , { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, + NULL +} , { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr, + NULL +} }; +static const struct attribute_group it87_group_temp[3] = { + { .attrs = it87_attributes_temp[0] }, + { .attrs = it87_attributes_temp[1] }, + { .attrs = it87_attributes_temp[2] }, +}; + +static struct attribute *it87_attributes[] = { &dev_attr_alarms.attr, &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_name.attr, @@ -1430,7 +1482,7 @@ static const struct attribute_group it87_group = { .attrs = it87_attributes, }; -static struct attribute *it87_attributes_beep[] = { +static struct attribute *it87_attributes_in_beep[] = { &sensor_dev_attr_in0_beep.dev_attr.attr, &sensor_dev_attr_in1_beep.dev_attr.attr, &sensor_dev_attr_in2_beep.dev_attr.attr, @@ -1439,15 +1491,13 @@ static struct attribute *it87_attributes_beep[] = { &sensor_dev_attr_in5_beep.dev_attr.attr, &sensor_dev_attr_in6_beep.dev_attr.attr, &sensor_dev_attr_in7_beep.dev_attr.attr, - - &sensor_dev_attr_temp1_beep.dev_attr.attr, - &sensor_dev_attr_temp2_beep.dev_attr.attr, - &sensor_dev_attr_temp3_beep.dev_attr.attr, NULL }; -static const struct attribute_group it87_group_beep = { - .attrs = it87_attributes_beep, +static struct attribute *it87_attributes_temp_beep[] = { + &sensor_dev_attr_temp1_beep.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, }; static struct attribute *it87_attributes_fan16[5][3+1] = { { @@ -1651,6 +1701,12 @@ static int __init it87_find(unsigned short *address, case IT8728F_DEVID: sio_data->type = it8728; break; + case IT8782F_DEVID: + sio_data->type = it8782; + break; + case IT8783E_DEVID: + sio_data->type = it8783; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1686,16 +1742,86 @@ static int __init it87_find(unsigned short *address, /* The IT8705F has a different LD number for GPIO */ superio_select(5); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + } else if (sio_data->type == it8783) { + int reg25, reg27, reg2A, reg2C, regEF; + + sio_data->skip_vid = 1; /* No VID */ + + superio_select(GPIO); + + reg25 = superio_inb(IT87_SIO_GPIO1_REG); + reg27 = superio_inb(IT87_SIO_GPIO3_REG); + reg2A = superio_inb(IT87_SIO_PINX1_REG); + reg2C = superio_inb(IT87_SIO_PINX2_REG); + regEF = superio_inb(IT87_SIO_SPI_REG); + + /* Check if fan3 is there or not */ + if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2))) + sio_data->skip_fan |= (1 << 2); + if ((reg25 & (1 << 4)) + || (!(reg2A & (1 << 1)) && (regEF & (1 << 0)))) + sio_data->skip_pwm |= (1 << 2); + + /* Check if fan2 is there or not */ + if (reg27 & (1 << 7)) + sio_data->skip_fan |= (1 << 1); + if (reg27 & (1 << 3)) + sio_data->skip_pwm |= (1 << 1); + + /* VIN5 */ + if ((reg27 & (1 << 0)) || (reg2C & (1 << 2))) + sio_data->skip_in |= (1 << 5); /* No VIN5 */ + + /* VIN6 */ + if (reg27 & (1 << 1)) + sio_data->skip_in |= (1 << 6); /* No VIN6 */ + + /* + * VIN7 + * Does not depend on bit 2 of Reg2C, contrary to datasheet. + */ + if (reg27 & (1 << 2)) { + /* + * The data sheet is a bit unclear regarding the + * internal voltage divider for VCCH5V. It says + * "This bit enables and switches VIN7 (pin 91) to the + * internal voltage divider for VCCH5V". + * This is different to other chips, where the internal + * voltage divider would connect VIN7 to an internal + * voltage source. Maybe that is the case here as well. + * + * Since we don't know for sure, re-route it if that is + * not the case, and ask the user to report if the + * resulting voltage is sane. + */ + if (!(reg2C & (1 << 1))) { + reg2C |= (1 << 1); + superio_outb(IT87_SIO_PINX2_REG, reg2C); + pr_notice("Routing internal VCCH5V to in7.\n"); + } + pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n"); + pr_notice("Please report if it displays a reasonable voltage.\n"); + } + + if (reg2C & (1 << 0)) + sio_data->internal |= (1 << 0); + if (reg2C & (1 << 1)) + sio_data->internal |= (1 << 1); + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + } else { int reg; + bool uart6; superio_select(GPIO); reg = superio_inb(IT87_SIO_GPIO3_REG); - if (sio_data->type == it8721 || sio_data->type == it8728) { + if (sio_data->type == it8721 || sio_data->type == it8728 || + sio_data->type == it8782) { /* - * The IT8721F/IT8758E doesn't have VID pins at all, - * not sure about the IT8728F. + * IT8721F/IT8758E, and IT8782F don't have VID pins + * at all, not sure about the IT8728F. */ sio_data->skip_vid = 1; } else { @@ -1724,6 +1850,9 @@ static int __init it87_find(unsigned short *address, sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); + + uart6 = sio_data->type == it8782 && (reg & (1 << 2)); + /* * The IT8720F has no VIN7 pin, so VCCH should always be * routed internally to VIN7 with an internal divider. @@ -1733,8 +1862,12 @@ static int __init it87_find(unsigned short *address, * configured, even though the IT8720F datasheet claims * that the internal routing of VCCH to VIN7 is the default * setting. So we force the internal routing in this case. + * + * On IT8782F, VIN7 is multiplexed with one of the UART6 pins. + * If UART6 is enabled, re-route VIN7 to the internal divider + * if that is not already the case. */ - if (sio_data->type == it8720 && !(reg & (1 << 1))) { + if ((sio_data->type == it8720 || uart6) && !(reg & (1 << 1))) { reg |= (1 << 1); superio_outb(IT87_SIO_PINX2_REG, reg); pr_notice("Routing internal VCCH to in7\n"); @@ -1745,6 +1878,20 @@ static int __init it87_find(unsigned short *address, sio_data->type == it8728) sio_data->internal |= (1 << 1); + /* + * On IT8782F, UART6 pins overlap with VIN5, VIN6, and VIN7. + * While VIN7 can be routed to the internal voltage divider, + * VIN5 and VIN6 are not available if UART6 is enabled. + * + * Also, temp3 is not available if UART6 is enabled and TEMPIN3 + * is the temperature source. Since we can not read the + * temperature source here, skip_temp is preliminary. + */ + if (uart6) { + sio_data->skip_in |= (1 << 5) | (1 << 6); + sio_data->skip_temp |= (1 << 2); + } + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } if (sio_data->beep_pin) @@ -1782,8 +1929,22 @@ static void it87_remove_files(struct device *dev) int i; sysfs_remove_group(&dev->kobj, &it87_group); - if (sio_data->beep_pin) - sysfs_remove_group(&dev->kobj, &it87_group_beep); + for (i = 0; i < 9; i++) { + if (sio_data->skip_in & (1 << i)) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_in[i]); + if (it87_attributes_in_beep[i]) + sysfs_remove_file(&dev->kobj, + it87_attributes_in_beep[i]); + } + for (i = 0; i < 3; i++) { + if (!(data->has_temp & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_temp[i]); + if (sio_data->beep_pin) + sysfs_remove_file(&dev->kobj, + it87_attributes_temp_beep[i]); + } for (i = 0; i < 5; i++) { if (!(data->has_fan & (1 << i))) continue; @@ -1823,22 +1984,22 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8720", "it8721", "it8728", + "it8782", + "it8783", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, IT87_EC_EXTENT, DRVNAME)) { + if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT, + DRVNAME)) { dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)res->start, (unsigned long)(res->start + IT87_EC_EXTENT - 1)); - err = -EBUSY; - goto ERROR0; + return -EBUSY; } - data = kzalloc(sizeof(struct it87_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto ERROR1; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct it87_data), GFP_KERNEL); + if (!data) + return -ENOMEM; data->addr = res->start; data->type = sio_data->type; @@ -1847,10 +2008,8 @@ static int __devinit it87_probe(struct platform_device *pdev) /* Now, we do the remaining detection. */ if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) - || it87_read_value(data, IT87_REG_CHIPID) != 0x90) { - err = -ENODEV; - goto ERROR2; - } + || it87_read_value(data, IT87_REG_CHIPID) != 0x90) + return -ENODEV; platform_set_drvdata(pdev, data); @@ -1867,6 +2026,18 @@ static int __devinit it87_probe(struct platform_device *pdev) data->in_scaled |= (1 << 7); /* in7 is VSB */ if (sio_data->internal & (1 << 2)) data->in_scaled |= (1 << 8); /* in8 is Vbat */ + } else if (sio_data->type == it8782 || sio_data->type == it8783) { + if (sio_data->internal & (1 << 0)) + data->in_scaled |= (1 << 3); /* in3 is VCC5V */ + if (sio_data->internal & (1 << 1)) + data->in_scaled |= (1 << 7); /* in7 is VCCH5V */ + } + + data->has_temp = 0x07; + if (sio_data->skip_temp & (1 << 2)) { + if (sio_data->type == it8782 + && !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80)) + data->has_temp &= ~(1 << 2); } /* Initialize the IT87 chip */ @@ -1875,12 +2046,34 @@ static int __devinit it87_probe(struct platform_device *pdev) /* Register sysfs hooks */ err = sysfs_create_group(&dev->kobj, &it87_group); if (err) - goto ERROR2; + return err; - if (sio_data->beep_pin) { - err = sysfs_create_group(&dev->kobj, &it87_group_beep); + for (i = 0; i < 9; i++) { + if (sio_data->skip_in & (1 << i)) + continue; + err = sysfs_create_group(&dev->kobj, &it87_group_in[i]); if (err) - goto ERROR4; + goto error; + if (sio_data->beep_pin && it87_attributes_in_beep[i]) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_in_beep[i]); + if (err) + goto error; + } + } + + for (i = 0; i < 3; i++) { + if (!(data->has_temp & (1 << i))) + continue; + err = sysfs_create_group(&dev->kobj, &it87_group_temp[i]); + if (err) + goto error; + if (sio_data->beep_pin) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_temp_beep[i]); + if (err) + goto error; + } } /* Do not create fan files for disabled fans */ @@ -1891,13 +2084,13 @@ static int __devinit it87_probe(struct platform_device *pdev) continue; err = sysfs_create_group(&dev->kobj, &fan_group[i]); if (err) - goto ERROR4; + goto error; if (sio_data->beep_pin) { err = sysfs_create_file(&dev->kobj, it87_attributes_fan_beep[i]); if (err) - goto ERROR4; + goto error; if (!fan_beep_need_rw) continue; @@ -1922,14 +2115,14 @@ static int __devinit it87_probe(struct platform_device *pdev) err = sysfs_create_group(&dev->kobj, &it87_group_pwm[i]); if (err) - goto ERROR4; + goto error; if (!has_old_autopwm(data)) continue; err = sysfs_create_group(&dev->kobj, &it87_group_autopwm[i]); if (err) - goto ERROR4; + goto error; } } @@ -1939,7 +2132,7 @@ static int __devinit it87_probe(struct platform_device *pdev) data->vid = sio_data->vid_value; err = sysfs_create_group(&dev->kobj, &it87_group_vid); if (err) - goto ERROR4; + goto error; } /* Export labels for internal sensors */ @@ -1949,25 +2142,19 @@ static int __devinit it87_probe(struct platform_device *pdev) err = sysfs_create_file(&dev->kobj, it87_attributes_label[i]); if (err) - goto ERROR4; + goto error; } data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR4; + goto error; } return 0; -ERROR4: +error: it87_remove_files(dev); -ERROR2: - platform_set_drvdata(pdev, NULL); - kfree(data); -ERROR1: - release_region(res->start, IT87_EC_EXTENT); -ERROR0: return err; } @@ -1978,10 +2165,6 @@ static int __devexit it87_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); it87_remove_files(&pdev->dev); - release_region(data->addr, IT87_EC_EXTENT); - platform_set_drvdata(pdev, NULL); - kfree(data); - return 0; } @@ -2143,8 +2326,9 @@ static void __devinit it87_init_device(struct platform_device *pdev) it87_write_value(data, IT87_REG_FAN_16BIT, tmp | 0x07); } - /* IT8705F only supports three fans. */ - if (data->type != it87) { + /* IT8705F, IT8782F, and IT8783E/F only support three fans. */ + if (data->type != it87 && data->type != it8782 && + data->type != it8783) { if (tmp & (1 << 4)) data->has_fan |= (1 << 3); /* fan4 enabled */ if (tmp & (1 << 5)) @@ -2233,6 +2417,8 @@ static struct it87_data *it87_update_device(struct device *dev) } } for (i = 0; i < 3; i++) { + if (!(data->has_temp & (1 << i))) + continue; data->temp[i] = it87_read_value(data, IT87_REG_TEMP(i)); data->temp_high[i] = diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 307bb325dde9..7356b5ec8f67 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -225,15 +225,4 @@ static struct pci_driver k10temp_driver = { .remove = __devexit_p(k10temp_remove), }; -static int __init k10temp_init(void) -{ - return pci_register_driver(&k10temp_driver); -} - -static void __exit k10temp_exit(void) -{ - pci_unregister_driver(&k10temp_driver); -} - -module_init(k10temp_init) -module_exit(k10temp_exit) +module_pci_driver(k10temp_driver); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 575101988751..35aac82ee8eb 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -339,19 +339,8 @@ static struct pci_driver k8temp_driver = { .remove = __devexit_p(k8temp_remove), }; -static int __init k8temp_init(void) -{ - return pci_register_driver(&k8temp_driver); -} - -static void __exit k8temp_exit(void) -{ - pci_unregister_driver(&k8temp_driver); -} +module_pci_driver(k8temp_driver); MODULE_AUTHOR("Rudolf Marek "); MODULE_DESCRIPTION("AMD K8 core temperature monitor"); MODULE_LICENSE("GPL"); - -module_init(k8temp_init) -module_exit(k8temp_exit) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 9b382ec2c3bd..6da9696e1827 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -134,8 +134,7 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor) return div64_u64(dividend, divisor); } -static unsigned int get_ohm_of_thermistor(struct ntc_data *data, - unsigned int uV) +static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV) { struct ntc_thermistor_platform_data *pdata = data->pdata; u64 mV = uV / 1000; @@ -146,12 +145,12 @@ static unsigned int get_ohm_of_thermistor(struct ntc_data *data, if (mV == 0) { if (pdata->connect == NTC_CONNECTED_POSITIVE) - return UINT_MAX; + return INT_MAX; return 0; } if (mV >= pmV) return (pdata->connect == NTC_CONNECTED_POSITIVE) ? - 0 : UINT_MAX; + 0 : INT_MAX; if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) N = div64_u64_safe(pdO * (pmV - mV), mV); @@ -163,113 +162,109 @@ static unsigned int get_ohm_of_thermistor(struct ntc_data *data, else N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); - return (unsigned int) N; + if (N > INT_MAX) + N = INT_MAX; + return N; } -static int lookup_comp(struct ntc_data *data, - unsigned int ohm, int *i_low, int *i_high) +static void lookup_comp(struct ntc_data *data, unsigned int ohm, + int *i_low, int *i_high) { - int start, end, mid = -1; + int start, end, mid; + + /* + * Handle special cases: Resistance is higher than or equal to + * resistance in first table entry, or resistance is lower or equal + * to resistance in last table entry. + * In these cases, return i_low == i_high, either pointing to the + * beginning or to the end of the table depending on the condition. + */ + if (ohm >= data->comp[0].ohm) { + *i_low = 0; + *i_high = 0; + return; + } + if (ohm <= data->comp[data->n_comp - 1].ohm) { + *i_low = data->n_comp - 1; + *i_high = data->n_comp - 1; + return; + } /* Do a binary search on compensation table */ start = 0; end = data->n_comp; - - while (end > start) { + while (start < end) { mid = start + (end - start) / 2; - if (data->comp[mid].ohm < ohm) + /* + * start <= mid < end + * data->comp[start].ohm > ohm >= data->comp[end].ohm + * + * We could check for "ohm == data->comp[mid].ohm" here, but + * that is a quite unlikely condition, and we would have to + * check again after updating start. Check it at the end instead + * for simplicity. + */ + if (ohm >= data->comp[mid].ohm) { end = mid; - else if (data->comp[mid].ohm > ohm) + } else { start = mid + 1; - else - break; - } - - if (mid == 0) { - if (data->comp[mid].ohm > ohm) { - *i_high = mid; - *i_low = mid + 1; - return 0; - } else { - *i_low = mid; - *i_high = -1; - return -EINVAL; + /* + * ohm >= data->comp[start].ohm might be true here, + * since we set start to mid + 1. In that case, we are + * done. We could keep going, but the condition is quite + * likely to occur, so it is worth checking for it. + */ + if (ohm >= data->comp[start].ohm) + end = start; } + /* + * start <= end + * data->comp[start].ohm >= ohm >= data->comp[end].ohm + */ } - if (mid == (data->n_comp - 1)) { - if (data->comp[mid].ohm <= ohm) { - *i_low = mid; - *i_high = mid - 1; - return 0; - } else { - *i_low = -1; - *i_high = mid; - return -EINVAL; - } - } - - if (data->comp[mid].ohm <= ohm) { - *i_low = mid; - *i_high = mid - 1; - } else { - *i_low = mid + 1; - *i_high = mid; - } - - return 0; + /* + * start == end + * ohm >= data->comp[end].ohm + */ + *i_low = end; + if (ohm == data->comp[end].ohm) + *i_high = end; + else + *i_high = end - 1; } -static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp) +static int get_temp_mC(struct ntc_data *data, unsigned int ohm) { int low, high; - int ret; + int temp; - ret = lookup_comp(data, ohm, &low, &high); - if (ret) { + lookup_comp(data, ohm, &low, &high); + if (low == high) { /* Unable to use linear approximation */ - if (low != -1) - *temp = data->comp[low].temp_C * 1000; - else if (high != -1) - *temp = data->comp[high].temp_C * 1000; - else - return ret; + temp = data->comp[low].temp_C * 1000; } else { - *temp = data->comp[low].temp_C * 1000 + + temp = data->comp[low].temp_C * 1000 + ((data->comp[high].temp_C - data->comp[low].temp_C) * 1000 * ((int)ohm - (int)data->comp[low].ohm)) / ((int)data->comp[high].ohm - (int)data->comp[low].ohm); } - - return 0; + return temp; } -static int ntc_thermistor_read(struct ntc_data *data, int *temp) +static int ntc_thermistor_get_ohm(struct ntc_data *data) { - int ret; - int read_ohm, read_uV; - unsigned int ohm = 0; + int read_uV; - if (data->pdata->read_ohm) { - read_ohm = data->pdata->read_ohm(); - if (read_ohm < 0) - return read_ohm; - ohm = (unsigned int)read_ohm; - } + if (data->pdata->read_ohm) + return data->pdata->read_ohm(); if (data->pdata->read_uV) { read_uV = data->pdata->read_uV(); if (read_uV < 0) return read_uV; - ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV); + return get_ohm_of_thermistor(data, read_uV); } - - ret = get_temp_mC(data, ohm, temp); - if (ret) { - dev_dbg(data->dev, "Sensor reading function not available.\n"); - return ret; - } - - return 0; + return -EINVAL; } static ssize_t ntc_show_name(struct device *dev, @@ -290,12 +285,13 @@ static ssize_t ntc_show_temp(struct device *dev, struct device_attribute *attr, char *buf) { struct ntc_data *data = dev_get_drvdata(dev); - int temp, ret; + int ohm; - ret = ntc_thermistor_read(data, &temp); - if (ret) - return ret; - return sprintf(buf, "%d\n", temp); + ohm = ntc_thermistor_get_ohm(data); + if (ohm < 0) + return ohm; + + return sprintf(buf, "%d\n", get_temp_mC(data, ohm)); } static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); @@ -326,14 +322,14 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) /* Either one of the two is required. */ if (!pdata->read_uV && !pdata->read_ohm) { - dev_err(&pdev->dev, "Both read_uV and read_ohm missing." - "Need either one of the two.\n"); + dev_err(&pdev->dev, + "Both read_uV and read_ohm missing. Need either one of the two.\n"); return -EINVAL; } if (pdata->read_uV && pdata->read_ohm) { - dev_warn(&pdev->dev, "Only one of read_uV and read_ohm " - "is needed; ignoring read_uV.\n"); + dev_warn(&pdev->dev, + "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n"); pdata->read_uV = NULL; } @@ -344,12 +340,12 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) NTC_CONNECTED_POSITIVE) || (pdata->connect != NTC_CONNECTED_POSITIVE && pdata->connect != NTC_CONNECTED_GROUND))) { - dev_err(&pdev->dev, "Required data to use read_uV not " - "supplied.\n"); + dev_err(&pdev->dev, + "Required data to use read_uV not supplied.\n"); return -EINVAL; } - data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -370,8 +366,7 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", pdev->id_entry->driver_data, pdev->id_entry->name); - ret = -EINVAL; - goto err; + return -EINVAL; } platform_set_drvdata(pdev, data); @@ -379,13 +374,13 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); if (ret) { dev_err(data->dev, "unable to create sysfs files\n"); - goto err; + return ret; } data->hwmon_dev = hwmon_device_register(data->dev); - if (IS_ERR_OR_NULL(data->hwmon_dev)) { + if (IS_ERR(data->hwmon_dev)) { dev_err(data->dev, "unable to register as hwmon device.\n"); - ret = -EINVAL; + ret = PTR_ERR(data->hwmon_dev); goto err_after_sysfs; } @@ -395,8 +390,6 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev) return 0; err_after_sysfs: sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); -err: - kfree(data); return ret; } @@ -408,8 +401,6 @@ static int __devexit ntc_thermistor_remove(struct platform_device *pdev) sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); platform_set_drvdata(pdev, NULL); - kfree(data); - return 0; } diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h new file mode 100644 index 000000000000..9abc0ca7259b --- /dev/null +++ b/include/linux/platform_data/ina2xx.h @@ -0,0 +1,19 @@ +/* + * Driver for Texas Instruments INA219, INA226 power monitor chips + * + * Copyright (C) 2012 Lothar Felten + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * For further information, see the Documentation/hwmon/ina2xx file. + */ + +/** + * struct ina2xx_platform_data - ina2xx info + * @shunt_uohms shunt resistance in microohms + */ +struct ina2xx_platform_data { + long shunt_uohms; +};