diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 8 | ||||
-rw-r--r-- | drivers/hwmon/adm1026.c | 17 | ||||
-rw-r--r-- | drivers/hwmon/adm1029.c | 5 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-core.c | 82 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-i2c.c | 60 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-input.c | 63 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-pmu.c | 18 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams.h | 4 | ||||
-rw-r--r-- | drivers/hwmon/applesmc.c | 109 | ||||
-rw-r--r-- | drivers/hwmon/ibmaem.c | 7 | ||||
-rw-r--r-- | drivers/hwmon/ibmpex.c | 6 | ||||
-rw-r--r-- | drivers/hwmon/it87.c | 11 | ||||
-rw-r--r-- | drivers/hwmon/lm78.c | 270 | ||||
-rw-r--r-- | drivers/hwmon/lm85.c | 352 | ||||
-rw-r--r-- | drivers/hwmon/lm87.c | 33 | ||||
-rw-r--r-- | drivers/hwmon/lm90.c | 444 | ||||
-rw-r--r-- | drivers/hwmon/max1619.c | 17 | ||||
-rw-r--r-- | drivers/hwmon/pc87360.c | 248 | ||||
-rw-r--r-- | drivers/hwmon/w83781d.c | 976 | ||||
-rw-r--r-- | drivers/hwmon/w83791d.c | 324 |
20 files changed, 1961 insertions, 1093 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ebacc0af40f..6de1e0ffd39 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -510,11 +510,9 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657, - MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. - - The Analog Devices ADT7461 sensor chip is also supported, but only - if found in ADM1032 compatibility mode. + LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim + MAX6646, MAX6647, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680 and + MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 7fe2441fc84..ff7de40b6e3 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -279,7 +279,6 @@ struct adm1026_data { u8 fan_min[8]; /* Register value */ u8 fan_div[8]; /* Decoded value */ struct pwm_data pwm1; /* Pwm control values */ - int vid; /* Decoded value */ u8 vrm; /* VRM version */ u8 analog_out; /* Register value (DAC) */ long alarms; /* Register encoding, combined */ @@ -455,7 +454,7 @@ static void adm1026_print_gpio(struct i2c_client *client) struct adm1026_data *data = i2c_get_clientdata(client); int i; - dev_dbg(&client->dev, "GPIO config is:"); + dev_dbg(&client->dev, "GPIO config is:\n"); for (i = 0;i <= 7;++i) { if (data->config2 & (1 << i)) { dev_dbg(&client->dev, "\t%sGP%s%d\n", @@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->last_config = jiffies; }; /* last_config */ - dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n"); - data->vid = (data->gpio >> 11) & 0x1f; data->valid = 1; mutex_unlock(&data->update_lock); return data; @@ -1215,7 +1212,10 @@ static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid & 0x3f, data->vrm)); + int vid = (data->gpio >> 11) & 0x1f; + + dev_dbg(dev, "Setting VID from GPIO11-15.\n"); + return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); @@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind, kind = adm1026; } else if (company == ADM1026_COMPANY_ANALOG_DEV && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, ": Unrecognized stepping " + dev_err(&adapter->dev, "Unrecognized stepping " "0x%02x. Defaulting to ADM1026.\n", verstep); kind = adm1026; } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, ": Found version/stepping " + dev_err(&adapter->dev, "Found version/stepping " "0x%02x. Assuming generic ADM1026.\n", verstep); kind = any_chip; } else { - dev_dbg(&adapter->dev, ": Autodetection " - "failed\n"); + dev_dbg(&adapter->dev, "Autodetection failed\n"); /* Not an ADM1026 ... */ if (kind == 0) { /* User used force=x,y */ dev_err(&adapter->dev, "Generic ADM1026 not " diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index ba84ca5923f..36718150b47 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -179,7 +179,8 @@ show_fan(struct device *dev, struct device_attribute *devattr, char *buf) struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); u16 val; - if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0 + if (data->fan[attr->index] == 0 + || (data->fan_div[attr->index] & 0xC0) == 0 || data->fan[attr->index] == 255) { return sprintf(buf, "0\n"); } @@ -194,7 +195,7 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); - if (data->fan_div[attr->index] == 0) + if ((data->fan_div[attr->index] & 0xC0) == 0) return sprintf(buf, "0\n"); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index])); } diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index fbefa82a015..6c9ace1b76f 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -99,39 +99,31 @@ static struct pmf_irq_client ams_shock_client = { */ static void ams_worker(struct work_struct *work) { - mutex_lock(&ams_info.lock); - - if (ams_info.has_device) { - unsigned long flags; + unsigned long flags; + u8 irqs_to_clear; - spin_lock_irqsave(&ams_info.irq_lock, flags); + mutex_lock(&ams_info.lock); - if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { - if (verbose) - printk(KERN_INFO "ams: freefall detected!\n"); + spin_lock_irqsave(&ams_info.irq_lock, flags); + irqs_to_clear = ams_info.worker_irqs; - ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; + if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { + if (verbose) + printk(KERN_INFO "ams: freefall detected!\n"); - /* we must call this with interrupts enabled */ - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - ams_info.clear_irq(AMS_IRQ_FREEFALL); - spin_lock_irqsave(&ams_info.irq_lock, flags); - } + ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; + } - if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { - if (verbose) - printk(KERN_INFO "ams: shock detected!\n"); + if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { + if (verbose) + printk(KERN_INFO "ams: shock detected!\n"); - ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; + ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; + } - /* we must call this with interrupts enabled */ - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - ams_info.clear_irq(AMS_IRQ_SHOCK); - spin_lock_irqsave(&ams_info.irq_lock, flags); - } + spin_unlock_irqrestore(&ams_info.irq_lock, flags); - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - } + ams_info.clear_irq(irqs_to_clear); mutex_unlock(&ams_info.lock); } @@ -223,34 +215,28 @@ int __init ams_init(void) void ams_exit(void) { - mutex_lock(&ams_info.lock); - - if (ams_info.has_device) { - /* Remove input device */ - ams_input_exit(); + /* Remove input device */ + ams_input_exit(); - /* Shut down implementation */ - ams_info.exit(); - - /* Flush interrupt worker - * - * We do this after ams_info.exit(), because an interrupt might - * have arrived before disabling them. - */ - flush_scheduled_work(); + /* Remove attributes */ + device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); - /* Remove attributes */ - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); + /* Shut down implementation */ + ams_info.exit(); - /* Remove device */ - of_device_unregister(ams_info.of_dev); + /* Flush interrupt worker + * + * We do this after ams_info.exit(), because an interrupt might + * have arrived before disabling them. + */ + flush_scheduled_work(); - /* Remove handler */ - pmf_unregister_irq_client(&ams_shock_client); - pmf_unregister_irq_client(&ams_freefall_client); - } + /* Remove device */ + of_device_unregister(ams_info.of_dev); - mutex_unlock(&ams_info.lock); + /* Remove handler */ + pmf_unregister_irq_client(&ams_shock_client); + pmf_unregister_irq_client(&ams_freefall_client); } MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index 957760536a4..2cbf8a6506c 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -60,26 +60,34 @@ enum ams_i2c_cmd { AMS_CMD_START, }; -static int ams_i2c_attach(struct i2c_adapter *adapter); -static int ams_i2c_detach(struct i2c_adapter *adapter); +static int ams_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int ams_i2c_remove(struct i2c_client *client); + +static const struct i2c_device_id ams_id[] = { + { "ams", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ams_id); static struct i2c_driver ams_i2c_driver = { .driver = { .name = "ams", .owner = THIS_MODULE, }, - .attach_adapter = ams_i2c_attach, - .detach_adapter = ams_i2c_detach, + .probe = ams_i2c_probe, + .remove = ams_i2c_remove, + .id_table = ams_id, }; static s32 ams_i2c_read(u8 reg) { - return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg); + return i2c_smbus_read_byte_data(ams_info.i2c_client, reg); } static int ams_i2c_write(u8 reg, u8 value) { - return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value); + return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value); } static int ams_i2c_cmd(enum ams_i2c_cmd cmd) @@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) *z = ams_i2c_read(AMS_DATAZ); } -static int ams_i2c_attach(struct i2c_adapter *adapter) +static int ams_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - unsigned long bus; int vmaj, vmin; int result; @@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) if (unlikely(ams_info.has_device)) return -ENODEV; - if (strncmp(adapter->name, "uni-n", 5)) - return -ENODEV; - - bus = simple_strtoul(adapter->name + 6, NULL, 10); - if (bus != ams_info.i2c_bus) - return -ENODEV; - - ams_info.i2c_client.addr = ams_info.i2c_address; - ams_info.i2c_client.adapter = adapter; - ams_info.i2c_client.driver = &ams_i2c_driver; - strcpy(ams_info.i2c_client.name, "Apple Motion Sensor"); + ams_info.i2c_client = client; if (ams_i2c_cmd(AMS_CMD_RESET)) { printk(KERN_INFO "ams: Failed to reset the device\n"); @@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) return 0; } -static int ams_i2c_detach(struct i2c_adapter *adapter) +static int ams_i2c_remove(struct i2c_client *client) { if (ams_info.has_device) { /* Disable interrupts */ @@ -261,11 +259,7 @@ static void ams_i2c_exit(void) int __init ams_i2c_init(struct device_node *np) { - char *tmp_bus; int result; - const u32 *prop; - - mutex_lock(&ams_info.lock); /* Set implementation stuff */ ams_info.of_node = np; @@ -275,25 +269,7 @@ int __init ams_i2c_init(struct device_node *np) ams_info.clear_irq = ams_i2c_clear_irq; ams_info.bustype = BUS_I2C; - /* look for bus either using "reg" or by path */ - prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) { - result = -ENODEV; - - goto exit; - } - - tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@"); - if (tmp_bus) - ams_info.i2c_bus = *(tmp_bus + 9) - '0'; - else - ams_info.i2c_bus = ((*prop) >> 8) & 0x0f; - ams_info.i2c_address = ((*prop) & 0xff) >> 1; - result = i2c_add_driver(&ams_i2c_driver); -exit: - mutex_unlock(&ams_info.lock); - return result; } diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 7b81e0c2c2d..8a712392cd3 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c @@ -20,13 +20,15 @@ #include "ams.h" static unsigned int joystick; -module_param(joystick, bool, 0644); +module_param(joystick, bool, S_IRUGO); MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); static unsigned int invert; -module_param(invert, bool, 0644); +module_param(invert, bool, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); +static DEFINE_MUTEX(ams_input_mutex); + static void ams_idev_poll(struct input_polled_dev *dev) { struct input_dev *idev = dev->input; @@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev) } /* Call with ams_info.lock held! */ -static void ams_input_enable(void) +static int ams_input_enable(void) { struct input_dev *input; s8 x, y, z; - - if (ams_info.idev) - return; + int error; ams_sensors(&x, &y, &z); ams_info.xcalib = x; @@ -65,7 +65,7 @@ static void ams_input_enable(void) ams_info.idev = input_allocate_polled_device(); if (!ams_info.idev) - return; + return -ENOMEM; ams_info.idev->poll = ams_idev_poll; ams_info.idev->poll_interval = 25; @@ -84,14 +84,18 @@ static void ams_input_enable(void) set_bit(EV_KEY, input->evbit); set_bit(BTN_TOUCH, input->keybit); - if (input_register_polled_device(ams_info.idev)) { + error = input_register_polled_device(ams_info.idev); + if (error) { input_free_polled_device(ams_info.idev); ams_info.idev = NULL; - return; + return error; } + + joystick = 1; + + return 0; } -/* Call with ams_info.lock held! */ static void ams_input_disable(void) { if (ams_info.idev) { @@ -99,6 +103,8 @@ static void ams_input_disable(void) input_free_polled_device(ams_info.idev); ams_info.idev = NULL; } + + joystick = 0; } static ssize_t ams_input_show_joystick(struct device *dev, @@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev, static ssize_t ams_input_store_joystick(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (sscanf(buf, "%d\n", &joystick) != 1) + unsigned long enable; + int error = 0; + + if (strict_strtoul(buf, 0, &enable) || enable > 1) return -EINVAL; - mutex_lock(&ams_info.lock); + mutex_lock(&ams_input_mutex); - if (joystick) - ams_input_enable(); - else - ams_input_disable(); + if (enable != joystick) { + if (enable) + error = ams_input_enable(); + else + ams_input_disable(); + } - mutex_unlock(&ams_info.lock); + mutex_unlock(&ams_input_mutex); - return count; + return error ? error : count; } static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, ams_input_show_joystick, ams_input_store_joystick); -/* Call with ams_info.lock held! */ int ams_input_init(void) { - int result; - - result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); - - if (!result && joystick) + if (joystick) ams_input_enable(); - return result; + + return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); } -/* Call with ams_info.lock held! */ void ams_input_exit(void) { - ams_input_disable(); device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); + + mutex_lock(&ams_input_mutex); + ams_input_disable(); + mutex_unlock(&ams_input_mutex); } diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c index 9463e9768f6..fb18b3d3162 100644 --- a/drivers/hwmon/ams/ams-pmu.c +++ b/drivers/hwmon/ams/ams-pmu.c @@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np) const u32 *prop; int result; - mutex_lock(&ams_info.lock); - /* Set implementation stuff */ ams_info.of_node = np; ams_info.exit = ams_pmu_exit; @@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np) /* Get PMU command, should be 0x4e, but we can never know */ prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) { - result = -ENODEV; - goto exit; - } + if (!prop) + return -ENODEV; + ams_pmu_cmd = ((*prop) >> 8) & 0xff; /* Disable interrupts */ @@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np) result = ams_sensor_attach(); if (result < 0) - goto exit; + return result; /* Set default values */ ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); @@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np) printk(KERN_INFO "ams: Found PMU based motion sensor\n"); - result = 0; - -exit: - mutex_unlock(&ams_info.lock); - - return result; + return 0; } diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h index 221ef6915a5..5ed387b0bd9 100644 --- a/drivers/hwmon/ams/ams.h +++ b/drivers/hwmon/ams/ams.h @@ -46,9 +46,7 @@ struct ams { #ifdef CONFIG_SENSORS_AMS_I2C /* I2C properties */ - int i2c_bus; - int i2c_address; - struct i2c_client i2c_client; + struct i2c_client *i2c_client; #endif /* Joystick emulation */ diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b06b8e090a2..bc011da79e1 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -49,6 +49,9 @@ #define APPLESMC_MAX_DATA_LENGTH 32 +#define APPLESMC_MIN_WAIT 0x0040 +#define APPLESMC_MAX_WAIT 0x8000 + #define APPLESMC_STATUS_MASK 0x0f #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 @@ -57,8 +60,8 @@ #define KEY_COUNT_KEY "#KEY" /* r-o ui32 */ -#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6 bytes) */ -#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */ +#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */ +#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */ #define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */ #define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */ @@ -104,6 +107,15 @@ static const char* temperature_sensors_sets[][36] = { /* Set 6: Macbook3 set */ { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H", "Th0S", "Th1H", NULL }, +/* Set 7: Macbook Air */ + { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP", + "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL }, +/* Set 8: Macbook Pro 4,1 (Penryn) */ + { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", + "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, +/* Set 9: Macbook Pro 3,1 (Santa Rosa) */ + { "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", + "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, }; /* List of keys used to read/write fan speeds */ @@ -163,25 +175,25 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * __wait_status - Wait up to 2ms for the status port to get a certain value + * __wait_status - Wait up to 32ms for the status port to get a certain value * (masked with 0x0f), returning zero if the value is obtained. Callers must * hold applesmc_lock. */ static int __wait_status(u8 val) { - unsigned int i; + int us; val = val & APPLESMC_STATUS_MASK; - for (i = 0; i < 200; i++) { + for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { + udelay(us); if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { if (debug) printk(KERN_DEBUG - "Waited %d us for status %x\n", - i*10, val); + "Waited %d us for status %x\n", + 2 * us - APPLESMC_MIN_WAIT, val); return 0; } - udelay(10); } printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", @@ -191,6 +203,25 @@ static int __wait_status(u8 val) } /* + * special treatment of command port - on newer macbooks, it seems necessary + * to resend the command byte before polling the status again. Callers must + * hold applesmc_lock. + */ +static int send_command(u8 cmd) +{ + int us; + for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { + outb(cmd, APPLESMC_CMD_PORT); + udelay(us); + if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) + return 0; + } + printk(KERN_WARNING "applesmc: command failed: %x -> %x\n", + cmd, inb(APPLESMC_CMD_PORT)); + return -EIO; +} + +/* * applesmc_read_key - reads len bytes from a given key, and put them in buffer. * Returns zero on success or a negative error on failure. Callers must * hold applesmc_lock. @@ -205,8 +236,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len) return -EINVAL; } - outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_READ_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -249,8 +279,7 @@ static int applesmc_write_key(const char* key, u8* buffer, u8 len) return -EINVAL; } - outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_WRITE_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -284,8 +313,7 @@ static int applesmc_get_key_at_index(int index, char* key) readkey[2] = index >> 8; readkey[3] = index; - outb(APPLESMC_GET_KEY_BY_INDEX_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -315,8 +343,7 @@ static int applesmc_get_key_type(char* key, char* type) { int i; - outb(APPLESMC_GET_KEY_TYPE_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_GET_KEY_TYPE_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -325,7 +352,7 @@ static int applesmc_get_key_type(char* key, char* type) return -EIO; } - outb(5, APPLESMC_DATA_PORT); + outb(6, APPLESMC_DATA_PORT); for (i = 0; i < 6; i++) { if (__wait_status(0x05)) @@ -527,17 +554,27 @@ out: static ssize_t applesmc_light_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { + static int data_length; int ret; u8 left = 0, right = 0; - u8 buffer[6]; + u8 buffer[10], query[6]; mutex_lock(&applesmc_lock); - ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6); + if (!data_length) { + ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query); + if (ret) + goto out; + data_length = clamp_val(query[0], 0, 10); + printk(KERN_INFO "applesmc: light sensor data length set to " + "%d\n", data_length); + } + + ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); left = buffer[2]; if (ret) goto out; - ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6); + ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); right = buffer[2]; out: @@ -1233,39 +1270,57 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 0, .light = 0, .temperature_set = 5 }, /* MacBook3: accelerometer and temperature set 6 */ { .accelerometer = 1, .light = 0, .temperature_set = 6 }, +/* MacBook Air: accelerometer, backlight and temperature set 7 */ + { .accelerometer = 1, .light = 1, .temperature_set = 7 }, +/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */ + { .accelerometer = 1, .light = 1, .temperature_set = 8 }, +/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */ + { .accelerometer = 1, .light = 1, .temperature_set = 9 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ static __initdata struct dmi_system_id applesmc_whitelist[] = { + { applesmc_dmi_match, "Apple MacBook Air", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, + &applesmc_dmi_data[7]}, + { applesmc_dmi_match, "Apple MacBook Pro 4", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, + &applesmc_dmi_data[8]}, + { applesmc_dmi_match, "Apple MacBook Pro 3", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") }, + &applesmc_dmi_data[9]}, { applesmc_dmi_match, "Apple MacBook Pro", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, - (void*)&applesmc_dmi_data[0]}, + &applesmc_dmi_data[0]}, { applesmc_dmi_match, "Apple MacBook (v2)", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") }, - (void*)&applesmc_dmi_data[1]}, + &applesmc_dmi_data[1]}, { applesmc_dmi_match, "Apple MacBook (v3)", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, - (void*)&applesmc_dmi_data[6]}, + &applesmc_dmi_data[6]}, { applesmc_dmi_match, "Apple MacBook", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, - (void*)&applesmc_dmi_data[2]}, + &applesmc_dmi_data[2]}, { applesmc_dmi_match, "Apple Macmini", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") }, - (void*)&applesmc_dmi_data[3]}, + &applesmc_dmi_data[3]}, { applesmc_dmi_match, "Apple MacPro2", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, - (void*)&applesmc_dmi_data[4]}, + &applesmc_dmi_data[4]}, { applesmc_dmi_match, "Apple iMac", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, - (void*)&applesmc_dmi_data[5]}, + &applesmc_dmi_data[5]}, { .ident = NULL } }; diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 0f70dc20410..7b0ed5dea39 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL"); module_init(aem_init); module_exit(aem_exit); + +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*"); diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 4e9b19c6732..537d9fb2ff8 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -608,3 +608,9 @@ MODULE_LICENSE("GPL"); module_init(ibmpex_init); module_exit(ibmpex_exit); + +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index d793cc01199..b74c95735f9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ if (reg & (8 << nr)) - return sprintf(buf, "2\n"); /* thermistor */ + return sprintf(buf, "4\n"); /* thermistor */ return sprintf(buf, "0\n"); /* disabled */ } static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, @@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, data->sensor &= ~(1 << nr); data->sensor &= ~(8 << nr); - /* 3 = thermal diode; 2 = thermistor; 0 = disabled */ + if (val == 2) { /* backwards compatibility */ + dev_warn(dev, "Sensor type 2 is deprecated, please use 4 " + "instead\n"); + val = 4; + } + /* 3 = thermal diode; 4 = thermistor; 0 = disabled */ if (val == 3) data->sensor |= 1 << nr; - else if (val == 2) + else if (val == 4) data->sensor |= 8 << nr; else if (val != 0) { mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index ed7859f0e16..b5e3b285169 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -114,25 +114,16 @@ static inline int TEMP_FROM_REG(s8 val) #define DIV_FROM_REG(val) (1 << (val)) -/* There are some complications in a module like this. First off, LM78 chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - LM78 chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the LM78 at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ - -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct lm78_data { - struct i2c_client client; + struct i2c_client *client; struct device *hwmon_dev; struct mutex lock; enum chips type; + /* For ISA device only */ + const char *name; + int isa_addr; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -151,9 +142,11 @@ struct lm78_data { }; -static int lm78_attach_adapter(struct i2c_adapter *adapter); -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); -static int lm78_detach_client(struct i2c_client *client); +static int lm78_i2c_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int lm78_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int lm78_i2c_remove(struct i2c_client *client); static int __devinit lm78_isa_probe(struct platform_device *pdev); static int __devexit lm78_isa_remove(struct platform_device *pdev); @@ -164,12 +157,23 @@ static struct lm78_data *lm78_update_device(struct device *dev); static void lm78_init_device(struct lm78_data *data); +static const struct i2c_device_id lm78_i2c_id[] = { + { "lm78", lm78 }, + { "lm79", lm79 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); + static struct i2c_driver lm78_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "lm78", }, - .attach_adapter = lm78_attach_adapter, - .detach_client = lm78_detach_client, + .probe = lm78_i2c_probe, + .remove = lm78_i2c_remove, + .id_table = lm78_i2c_id, + .detect = lm78_i2c_detect, + .address_data = &addr_data, }; static struct platform_driver lm78_isa_driver = { @@ -454,17 +458,6 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); -/* This function is called when: - * lm78_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and lm78_driver is still present) */ -static int lm78_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_probe(adapter, &addr_data, lm78_detect); -} - static struct attribute *lm78_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, @@ -527,54 +520,77 @@ static ssize_t show_name(struct device *dev, struct device_attribute { struct lm78_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); + return sprintf(buf, "%s\n", data->name); } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -/* This function is called by i2c_probe */ -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) +/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ +static int lm78_alias_detect(struct i2c_client *client, u8 chipid) { - int i, err; - struct i2c_client *new_client; - struct lm78_data *data; - const char *client_name = ""; + struct lm78_data *isa; + int i; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -ENODEV; - goto ERROR1; + if (!pdev) /* No ISA chip */ + return 0; + isa = platform_get_drvdata(pdev); + + if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe)) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (lm78_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) + return 0; } + if (lm78_read_value(isa, LM78_REG_CONFIG) != + i2c_smbus_read_byte_data(client, LM78_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (lm78_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) + return 0; + } + + return 1; +} - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm78_{read,write}_value. */ +static int lm78_i2c_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + int i; + struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; + const char *client_name; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; - if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm78_driver; + /* We block updates of the ISA device to minimize the risk of + concurrent access to the same LM78 chip through different + interfaces. */ + if (isa) + mutex_lock(&isa->update_lock); - /* Now, we do the remaining detection. */ if (kind < 0) { - if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) { - err = -ENODEV; - goto ERROR2; - } - if (lm78_read_value(data, LM78_REG_I2C_ADDR) != - address) { - err = -ENODEV; - goto ERROR2; - } + if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) + || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) + != address) + goto err_nodev; + + /* Explicitly prevent the misdetection of Winbond chips */ + i = i2c_smbus_read_byte_data(client, 0x4f); + if (i == 0xa3 || i == 0x5c) + goto err_nodev; } /* Determine the chip type. */ if (kind <= 0) { - i = lm78_read_value(data, LM78_REG_CHIPID); + i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); if (i == 0x00 || i == 0x20 /* LM78 */ || i == 0x40) /* LM78-J */ kind = lm78; @@ -586,33 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) "parameter for unknown chip at " "adapter %d, address 0x%02x\n", i2c_adapter_id(adapter), address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; + } + + if (lm78_alias_detect(client, i)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + goto err_nodev; } } - if (kind == lm78) { - client_name = "lm78"; - } else if (kind == lm79) { + if (isa) + mutex_unlock(&isa->update_lock); + + switch (kind) { + case lm79: client_name = "lm79"; + break; + default: + client_name = "lm78"; } + strlcpy(info->type, client_name, I2C_NAME_SIZE); + + return 0; + + err_nodev: + if (isa) + mutex_unlock(&isa->update_lock); + return -ENODEV; +} + +static int lm78_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm78_data *data; + int err; - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; + data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; + i2c_set_clientdata(client, data); + data->client = client; + data->type = id->driver_data; /* Initialize the LM78 chip */ lm78_init_device(data); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group))) + err = sysfs_create_group(&client->dev.kobj, &lm78_group); + if (err) goto ERROR3; - data->hwmon_dev = hwmon_device_register(&new_client->dev); + data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto ERROR4; @@ -621,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) return 0; ERROR4: - sysfs_remove_group(&new_client->dev.kobj, &lm78_group); + sysfs_remove_group(&client->dev.kobj, &lm78_group); ERROR3: - i2c_detach_client(new_client); -ERROR2: kfree(data); -ERROR1: return err; } -static int lm78_detach_client(struct i2c_client *client) +static int lm78_i2c_remove(struct i2c_client *client) { struct lm78_data *data = i2c_get_clientdata(client); - int err; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm78_group); - - if ((err = i2c_detach_client(client))) - return err; - kfree(data); return 0; @@ -651,11 +685,10 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) int err; struct lm78_data *data; struct resource *res; - const char *name; /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, LM78_EXTENT, "lm78")) { + if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { err = -EBUSY; goto exit; } @@ -665,18 +698,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) goto exit_release_region; } mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); + data->isa_addr = res->start; platform_set_drvdata(pdev, data); if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { data->type = lm79; - name = "lm79"; + data->name = "lm79"; } else { data->type = lm78; - name = "lm78"; + data->name = "lm78"; } - strlcpy(data->client.name, name, I2C_NAME_SIZE); /* Initialize the LM78 chip */ lm78_init_device(data); @@ -699,7 +730,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_name); kfree(data); exit_release_region: - release_region(res->start, LM78_EXTENT); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); exit: return err; } @@ -707,13 +738,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) static int __devexit lm78_isa_remove(struct platform_device *pdev) { struct lm78_data *data = platform_get_drvdata(pdev); + struct resource *res; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &lm78_group); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr, LM78_EXTENT); kfree(data); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + return 0; } @@ -724,13 +758,13 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev) would slow down the LM78 access and should not be necessary. */ static int lm78_read_value(struct lm78_data *data, u8 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; - if (!client->driver) { /* ISA device */ + if (!client) { /* ISA device */ int res; mutex_lock(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - res = inb_p(client->addr + LM78_DATA_REG_OFFSET); + outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); + res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET); mutex_unlock(&data->lock); return res; } else @@ -746,12 +780,12 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) nowhere else be necessary! */ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; - if (!client->driver) { /* ISA device */ + if (!client) { /* ISA device */ mutex_lock(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - outb_p(value, client->addr + LM78_DATA_REG_OFFSET); + outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); + outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET); mutex_unlock(&data->lock); return 0; } else @@ -837,8 +871,17 @@ static int __init lm78_isa_found(unsigned short address) { int val, save, found = 0; - if (!request_region(address, LM78_EXTENT, "lm78")) + /* We have to request the region in two parts because some + boards declare base+4 to base+7 as a PNP device */ + if (!request_region(address, 4, "lm78")) { + pr_debug("lm78: Failed to request low part of region\n"); return 0; + } + if (!request_region(address + 4, 4, "lm78")) { + pr_debug("lm78: Failed to request high part of region\n"); + release_region(address, 4); + return 0; + } #define REALLY_SLOW_IO /* We need the timeouts for at least some LM78-like @@ -901,7 +944,8 @@ static int __init lm78_isa_found(unsigned short address) val & 0x80 ? "LM79" : "LM78", (int)address); release: - release_region(address, LM78_EXTENT); + release_region(address + 4, 4); + release_region(address, 4); return found; } @@ -949,14 +993,12 @@ static int __init sm_lm78_init(void) { int res; - res = i2c_add_driver(&lm78_driver); - if (res) - goto exit; - + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ if (lm78_isa_found(isa_address)) { res = platform_driver_register(&lm78_isa_driver); if (res) - goto exit_unreg_i2c_driver; + goto exit; /* Sets global pdev as a side effect */ res = lm78_isa_device_add(isa_address); @@ -964,12 +1006,16 @@ static int __init sm_lm78_init(void) goto exit_unreg_isa_driver; } + res = i2c_add_driver(&lm78_driver); + if (res) + goto exit_unreg_isa_device; + return 0; + exit_unreg_isa_device: + platform_device_unregister(pdev); exit_unreg_isa_driver: platform_driver_unregister(&lm78_isa_driver); - exit_unreg_i2c_driver: - i2c_del_driver(&lm78_driver); exit: return res; } diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 12d446f54f9..3ff0285396f 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -5,6 +5,7 @@ Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com> + Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org> Chip details at <http://www.national.com/ds/LM/LM85.pdf> @@ -173,40 +174,39 @@ static int RANGE_TO_REG(int range) { int i; - if (range >= lm85_range_map[15]) - return 15; - /* Find the closest match */ - for (i = 14; i >= 0; --i) { - if (range >= lm85_range_map[i]) { - if ((lm85_range_map[i + 1] - range) < - (range - lm85_range_map[i])) - return i + 1; - return i; - } + for (i = 0; i < 15; ++i) { + if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2) + break; } - return 0; + return i; } #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f] /* These are the PWM frequency encodings */ -static const int lm85_freq_map[] = { /* .1 Hz */ - 100, 150, 230, 300, 380, 470, 620, 940 +static const int lm85_freq_map[8] = { /* 1 Hz */ + 10, 15, 23, 30, 38, 47, 61, 94 +}; +static const int adm1027_freq_map[8] = { /* 1 Hz */ + 11, 15, 22, 29, 35, 44, 59, 88 }; -static int FREQ_TO_REG(int freq) +static int FREQ_TO_REG(const int *map, int freq) { int i; - if (freq >= lm85_freq_map[7]) - return 7; + /* Find the closest match */ for (i = 0; i < 7; ++i) - if (freq <= lm85_freq_map[i]) + if (freq <= (map[i] + map[i + 1]) / 2) break; return i; } -#define FREQ_FROM_REG(val) lm85_freq_map[(val) & 0x07] + +static int FREQ_FROM_REG(const int *map, u8 reg) +{ + return map[reg & 0x07]; +} /* Since we can't use strings, I'm abusing these numbers * to stand in for the following meanings: @@ -275,7 +275,6 @@ struct lm85_zone { struct lm85_autofan { u8 config; /* Register value */ - u8 freq; /* PWM frequency, encoded */ u8 min_pwm; /* Minimum PWM value, encoded */ u8 min_off; /* Min PWM or OFF below "limit", flag */ }; @@ -283,8 +282,8 @@ struct lm85_autofan { /* For each registered chip, we need to keep some data in memory. The structure is dynamically allocated. */ struct lm85_data { - struct i2c_client client; struct device *hwmon_dev; + const int *freq_map; enum chips type; struct mutex update_lock; @@ -301,6 +300,7 @@ struct lm85_data { u16 fan[4]; /* Register value */ u16 fan_min[4]; /* Register value */ u8 pwm[3]; /* Register value */ + u8 pwm_freq[3]; /* Register encoding */ u8 temp_ext[3]; /* Decoded values */ u8 in_ext[8]; /* Decoded values */ u8 vid; /* Register value */ @@ -310,22 +310,40 @@ struct lm85_data { struct lm85_zone zone[3]; }; -static int lm85_attach_adapter(struct i2c_adapter *adapter); -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind); -static int lm85_detach_client(struct i2c_client *client); +static int lm85_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int lm85_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int lm85_remove(struct i2c_client *client); static int lm85_read_value(struct i2c_client *client, u8 reg); static void lm85_write_value(struct i2c_client *client, u8 reg, int value); static struct lm85_data *lm85_update_device(struct device *dev); +static const struct i2c_device_id lm85_id[] = { + { "adm1027", adm1027 }, + { "adt7463", adt7463 }, + { "lm85", any_chip }, + { "lm85b", lm85b }, + { "lm85c", lm85c }, + { "emc6d100", emc6d100 }, + { "emc6d101", emc6d100 }, + { "emc6d102", emc6d102 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm85_id); + static struct i2c_driver lm85_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "lm85", }, - .attach_adapter = lm85_attach_adapter, - .detach_client = lm85_detach_client, + .probe = lm85_probe, + .remove = lm85_remove, + .id_table = lm85_id, + .detect = lm85_detect, + .address_data = &addr_data, }; @@ -528,11 +546,39 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute return count; } +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map, + data->pwm_freq[nr])); +} + +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + mutex_unlock(&data->update_lock); + return count; +} + #define show_pwm_reg(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_pwm_enable, set_pwm_enable, offset - 1) + show_pwm_enable, set_pwm_enable, offset - 1); \ +static SENSOR_DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \ + show_pwm_freq, set_pwm_freq, offset - 1) show_pwm_reg(1); show_pwm_reg(2); @@ -761,31 +807,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, return count; } -static ssize_t show_pwm_auto_pwm_freq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->autofan[nr].freq)); -} - -static ssize_t set_pwm_auto_pwm_freq(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - mutex_lock(&data->update_lock); - data->autofan[nr].freq = FREQ_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->autofan[nr].freq); - mutex_unlock(&data->update_lock); - return count; -} - #define pwm_auto(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \ S_IRUGO | S_IWUSR, show_pwm_auto_channels, \ @@ -795,10 +816,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \ set_pwm_auto_pwm_min, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \ S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \ - set_pwm_auto_pwm_minctl, offset - 1); \ -static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_freq, \ - S_IRUGO | S_IWUSR, show_pwm_auto_pwm_freq, \ - set_pwm_auto_pwm_freq, offset - 1); + set_pwm_auto_pwm_minctl, offset - 1) pwm_auto(1); pwm_auto(2); @@ -867,7 +885,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, TEMP_FROM_REG(data->zone[nr].limit)); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); /* Update temp_auto_hyst and temp_auto_off */ data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( @@ -910,7 +928,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev, val - min); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); mutex_unlock(&data->update_lock); return count; } @@ -957,13 +975,6 @@ temp_auto(1); temp_auto(2); temp_auto(3); -static int lm85_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_probe(adapter, &addr_data, lm85_detect); -} - static struct attribute *lm85_attributes[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, @@ -984,6 +995,9 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, @@ -1026,9 +1040,6 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_pwm_freq.dev_attr.attr, &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, @@ -1103,109 +1114,74 @@ static void lm85_init_client(struct i2c_client *client) dev_warn(&client->dev, "Device is not ready\n"); } -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind) +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm85_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { - int company, verstep; - struct i2c_client *client; - struct lm85_data *data; - int err = 0; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ - goto ERROR0; + return -ENODEV; } - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm85_{read,write}_value. */ + /* If auto-detecting, determine the chip type */ + if (kind < 0) { + int company = lm85_read_value(client, LM85_REG_COMPANY); + int verstep = lm85_read_value(client, LM85_REG_VERSTEP); - if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR0; - } + dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " + "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + address, company, verstep); - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - client->adapter = adapter; - client->driver = &lm85_driver; - - /* Now, we do the remaining detection. */ - - company = lm85_read_value(client, LM85_REG_COMPANY); - verstep = lm85_read_value(client, LM85_REG_VERSTEP); - - dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - i2c_adapter_id(client->adapter), client->addr, - company, verstep); - - /* If auto-detecting, Determine the chip type. */ - if (kind <= 0) { - dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x ...\n", - i2c_adapter_id(adapter), address); - if (company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85C) { - kind = lm85c; - } else if (company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85B) { - kind = lm85b; - } else if (company == LM85_COMPANY_NATIONAL - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to LM85.\n", verstep); - kind = any_chip; - } else if (company == LM85_COMPANY_ANALOG_DEV - && verstep == LM85_VERSTEP_ADM1027) { - kind = adm1027; - } else if (company == LM85_COMPANY_ANALOG_DEV - && (verstep == LM85_VERSTEP_ADT7463 - || verstep == LM85_VERSTEP_ADT7463C)) { - kind = adt7463; - } else if (company == LM85_COMPANY_ANALOG_DEV - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep); - kind = any_chip; - } else if (company == LM85_COMPANY_SMSC - && (verstep == LM85_VERSTEP_EMC6D100_A0 - || verstep == LM85_VERSTEP_EMC6D100_A1)) { - /* Unfortunately, we can't tell a '100 from a '101 - * from the registers. Since a '101 is a '100 - * in a package with fewer pins and therefore no - * 3.3V, 1.5V or 1.8V inputs, perhaps if those - * inputs read 0, then it's a '101. - */ - kind = emc6d100; - } else if (company == LM85_COMPANY_SMSC - && verstep == LM85_VERSTEP_EMC6D102) { - kind = emc6d102; - } else if (company == LM85_COMPANY_SMSC - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "lm85: Detected SMSC chip\n"); - dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep); - kind = any_chip; - } else if (kind == any_chip - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n"); - /* Leave kind as "any_chip" */ - } else { - dev_dbg(&adapter->dev, "Autodetection failed\n"); - /* Not an LM85... */ - if (kind == any_chip) { /* User used force=x,y */ - dev_err(&adapter->dev, "Generic LM85 Version 6 not" - " found at %d,0x%02x. Try force_lm85c.\n", - i2c_adapter_id(adapter), address); + /* All supported chips have the version in common */ + if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) { + dev_dbg(&adapter->dev, "Autodetection failed: " + "unsupported version\n"); + return -ENODEV; + } + kind = any_chip; + + /* Now, refine the detection */ + if (company == LM85_COMPANY_NATIONAL) { + switch (verstep) { + case LM85_VERSTEP_LM85C: + kind = lm85c; + break; + case LM85_VERSTEP_LM85B: + kind = lm85b; + break; + } + } else if (company == LM85_COMPANY_ANALOG_DEV) { + switch (verstep) { + case LM85_VERSTEP_ADM1027: + kind = adm1027; + break; + case LM85_VERSTEP_ADT7463: + case LM85_VERSTEP_ADT7463C: + kind = adt7463; + break; + } + } else if (company == LM85_COMPANY_SMSC) { + switch (verstep) { + case LM85_VERSTEP_EMC6D100_A0: + case LM85_VERSTEP_EMC6D100_A1: + /* Note: we can't tell a '100 from a '101 */ + kind = emc6d100; + break; + case LM85_VERSTEP_EMC6D102: + kind = emc6d102; + break; } - err = 0; - goto ERROR1; + } else { + dev_dbg(&adapter->dev, "Autodetection failed: " + "unknown vendor\n"); + return -ENODEV; } } - /* Fill in the chip specific driver values */ switch (kind) { case lm85b: type_name = "lm85b"; @@ -1228,16 +1204,36 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, default: type_name = "lm85"; } - strlcpy(client->name, type_name, I2C_NAME_SIZE); + strlcpy(info->type, type_name, I2C_NAME_SIZE); + + return 0; +} - /* Fill in the remaining client fields */ - data->type = kind; +static int lm85_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm85_data *data; + int err; + + data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->type = id->driver_data; mutex_init(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ - err = i2c_attach_client(client); - if (err) - goto ERROR1; + /* Fill in the chip specific driver values */ + switch (data->type) { + case adm1027: + case adt7463: + case emc6d100: + case emc6d102: + data->freq_map = adm1027_freq_map; + break; + default: + data->freq_map = lm85_freq_map; + } /* Set the VRM version */ data->vrm = vid_which_vrm(); @@ -1248,45 +1244,42 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm85_group); if (err) - goto ERROR2; + goto err_kfree; /* The ADT7463 has an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ data->vid = lm85_read_value(client, LM85_REG_VID); - if (!(kind == adt7463 && (data->vid & 0x80))) + if (!(data->type == adt7463 && (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) - goto ERROR3; + goto err_remove_files; /* The EMC6D100 has 3 additional voltage inputs */ - if (kind == emc6d100) + if (data->type == emc6d100) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in567))) - goto ERROR3; + goto err_remove_files; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR3; + goto err_remove_files; } return 0; /* Error out and cleanup code */ - ERROR3: + err_remove_files: sysfs_remove_group(&client->dev.kobj, &lm85_group); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); - if (kind == emc6d100) + if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - ERROR2: - i2c_detach_client(client); - ERROR1: + err_kfree: kfree(data); - ERROR0: return err; } -static int lm85_detach_client(struct i2c_client *client) +static int lm85_remove(struct i2c_client *client) { struct lm85_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); @@ -1294,7 +1287,6 @@ static int lm85_detach_client(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - i2c_detach_client(client); kfree(data); return 0; } @@ -1481,7 +1473,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) data->autofan[i].config = lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); - data->autofan[i].freq = val & 0x07; + data->pwm_freq[i] = val & 0x07; data->zone[i].range = val >> 4; data->autofan[i].min_pwm = lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 21970f0d53a..2e4a3cea95f 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -21,11 +21,10 @@ * http://www.national.com/pf/LM/LM87.html * * Some functions share pins, so not all functions are available at the same - * time. Which are depends on the hardware setup. This driver assumes that - * the BIOS configured the chip correctly. In that respect, it differs from - * the original driver (from lm_sensors for Linux 2.4), which would force the - * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual - * chipset wiring. + * time. Which are depends on the hardware setup. This driver normally + * assumes that firmware configured the chip correctly. Where this is not + * the case, platform code must set the I2C client's platform_data to point + * to a u8 value to be written to the channel register. * For reference, here is the list of exclusive functions: * - in0+in5 (default) or temp3 * - fan1 (default) or in6 @@ -199,6 +198,7 @@ struct lm87_data { unsigned long last_updated; /* In jiffies */ u8 channel; /* register value */ + u8 config; /* original register value */ u8 in[8]; /* register value */ u8 in_max[8]; /* register value */ @@ -832,6 +832,7 @@ exit_remove: sysfs_remove_group(&new_client->dev.kobj, &lm87_group); sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); exit_free: + lm87_write_value(new_client, LM87_REG_CONFIG, data->config); kfree(data); exit: return err; @@ -840,12 +841,17 @@ exit: static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - u8 config; - data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + if (client->dev.platform_data) { + data->channel = *(u8 *)client->dev.platform_data; + lm87_write_value(client, + LM87_REG_CHANNEL_MODE, data->channel); + } else { + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + } + data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; - config = lm87_read_value(client, LM87_REG_CONFIG); - if (!(config & 0x01)) { + if (!(data->config & 0x01)) { int i; /* Limits are left uninitialized after power-up */ @@ -867,11 +873,11 @@ static void lm87_init_client(struct i2c_client *client) lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); } } - if ((config & 0x81) != 0x01) { - /* Start monitoring */ + + /* Make sure Start is set and INT#_Clear is clear */ + if ((data->config & 0x09) != 0x01) lm87_write_value(client, LM87_REG_CONFIG, - (config & 0xF7) | 0x01); - } + (data->config & 0x77) | 0x01); } static int lm87_remove(struct i2c_client *client) @@ -882,6 +888,7 @@ static int lm87_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &lm87_group); sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); + lm87_write_value(client, LM87_REG_CONFIG, data->config); kfree(data); return 0; } diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c24fe36ac78..3edeebc0b83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,14 +1,12 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org> * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to * one external one) with a 0.125 deg resolution (1 deg for local - * temperature) and a 3-4 deg accuracy. Complete datasheet can be - * obtained from National's website at: - * http://www.national.com/pf/LM/LM90.html + * temperature) and a 3-4 deg accuracy. * * This driver also supports the LM89 and LM99, two other sensor chips * made by National Semiconductor. Both have an increased remote @@ -16,48 +14,38 @@ * additionally shifts remote temperatures (measured and limits) by 16 * degrees, which allows for higher temperatures measurement. The * driver doesn't handle it since it can be done easily in user-space. - * Complete datasheets can be obtained from National's website at: - * http://www.national.com/pf/LM/LM89.html - * http://www.national.com/pf/LM/LM99.html * Note that there is no way to differentiate between both chips. * * This driver also supports the LM86, another sensor chip made by * National Semiconductor. It is exactly similar to the LM90 except it * has a higher accuracy. - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/pf/LM/LM86.html * * This driver also supports the ADM1032, a sensor chip made by Analog * Devices. That chip is similar to the LM90, with a few differences - * that are not handled by this driver. Complete datasheet can be - * obtained from Analog's website at: - * http://www.analog.com/en/prod/0,2877,ADM1032,00.html - * Among others, it has a higher accuracy than the LM90, much like the - * LM86 does. + * that are not handled by this driver. Among others, it has a higher + * accuracy than the LM90, much like the LM86 does. * * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor - * chips made by Maxim. These chips are similar to the LM86. Complete - * datasheet can be obtained at Maxim's website at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three * variants. The extra address and features of the MAX6659 are not * supported by this driver. These chips lack the remote temperature * offset feature. * + * This driver also supports the MAX6646, MAX6647 and MAX6649 chips + * made by Maxim. These are again similar to the LM86, but they use + * unsigned temperature values and can report temperatures from 0 to + * 145 degrees. + * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim - * chips. Complete datasheet can be obtained at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 - * The MAX6680 and MAX6681 only differ in the pinout so they can be - * treated identically. + * chips. The MAX6680 and MAX6681 only differ in the pinout so they can + * be treated identically. * - * This driver also supports the ADT7461 chip from Analog Devices but - * only in its "compatability mode". If an ADT7461 chip is found but - * is configured in non-compatible mode (where its temperature - * register values are decoded differently) it is ignored by this - * driver. Complete datasheet can be obtained from Analog's website - * at: - * http://www.analog.com/en/prod/0,2877,ADT7461,00.html + * This driver also supports the ADT7461 chip from Analog Devices. + * It's supported in both compatibility and extended mode. It is mostly + * compatible with LM90 except for a data format difference for the + * temperature value registers. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and @@ -93,9 +81,10 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658 - * have address 0x4c. - * ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 + * and MAX6658 have address 0x4c. + * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. + * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. @@ -108,7 +97,8 @@ static const unsigned short normal_i2c[] = { * Insmod parameters */ -I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); +I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, + max6646); /* * The LM90 registers @@ -149,39 +139,14 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* - * Conversions and various macros - * For local temperatures and limits, critical limits and the hysteresis - * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. - */ +/* MAX6646/6647/6649/6657/6658/6659 registers */ -#define TEMP1_FROM_REG(val) ((val) * 1000) -#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) -#define TEMP2_FROM_REG(val) ((val) / 32 * 125) -#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ - (val) >= 127875 ? 0x7FE0 : \ - (val) < 0 ? ((val) - 62) / 125 * 32 : \ - ((val) + 62) / 125 * 32) -#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \ - ((val) + 500) / 1000) - -/* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. - */ +#define MAX6657_REG_R_LOCAL_TEMPL 0x11 -#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127000 ? 127 : \ - ((val) + 500) / 1000) -#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127750 ? 0x7FC0 : \ - ((val) + 125) / 250 * 64) +/* + * Device flags + */ +#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ /* * Functions declaration @@ -206,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = { { "lm86", lm86 }, { "lm89", lm99 }, { "lm99", lm99 }, /* Missing temperature offset */ + { "max6646", max6646 }, + { "max6647", max6646 }, + { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, { "max6659", max6657 }, @@ -237,22 +205,150 @@ struct lm90_data { char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; + int flags; /* registers values */ - s8 temp8[5]; /* 0: local input - 1: local low limit - 2: local high limit - 3: local critical limit - 4: remote critical limit */ - s16 temp11[4]; /* 0: remote input + s8 temp8[4]; /* 0: local low limit + 1: local high limit + 2: local critical limit + 3: remote critical limit */ + s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) */ + 3: remote offset (except max6646 and max6657) + 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ }; /* + * Conversions + * For local temperatures and limits, critical limits and the hysteresis + * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. + * For remote temperatures and limits, it uses signed 11-bit values with + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some + * Maxim chips use unsigned values. + */ + +static inline int temp_from_s8(s8 val) +{ + return val * 1000; +} + +static inline int temp_from_u8(u8 val) +{ + return val * 1000; +} + +static inline int temp_from_s16(s16 val) +{ + return val / 32 * 125; +} + +static inline int temp_from_u16(u16 val) +{ + return val / 32 * 125; +} + +static s8 temp_to_s8(long val) +{ + if (val <= -128000) + return -128; + if (val >= 127000) + return 127; + if (val < 0) + return (val - 500) / 1000; + return (val + 500) / 1000; +} + +static u8 temp_to_u8(long val) +{ + if (val <= 0) + return 0; + if (val >= 255000) + return 255; + return (val + 500) / 1000; +} + +static s16 temp_to_s16(long val) +{ + if (val <= -128000) + return 0x8000; + if (val >= 127875) + return 0x7FE0; + if (val < 0) + return (val - 62) / 125 * 32; + return (val + 62) / 125 * 32; +} + +static u8 hyst_to_reg(long val) +{ + if (val <= 0) + return 0; + if (val >= 30500) + return 31; + return (val + 500) / 1000; +} + +/* + * ADT7461 in compatibility mode is almost identical to LM90 except that + * attempts to write values that are outside the range 0 < temp < 127 are + * treated as the boundary value. + * + * ADT7461 in "extended mode" operation uses unsigned integers offset by + * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. + */ +static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 64) * 1000; + else + return temp_from_s8(val); +} + +static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 0x4000) / 64 * 250; + else + return temp_from_s16(val); +} + +static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191000) + return 0xFF; + return (val + 500 + 64000) / 1000; + } else { + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; + } +} + +static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191750) + return 0xFFC0; + return (val + 64000 + 125) / 250 * 64; + } else { + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; + } +} + +/* * Sysfs stuff */ @@ -261,7 +357,16 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[attr->index]); + else + temp = temp_from_s8(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, @@ -282,10 +387,12 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); + data->temp8[nr] = temp_to_u8_adt7461(data, val); + else if (data->kind == max6646) + data->temp8[nr] = temp_to_u8(val); else - data->temp8[nr] = TEMP1_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); + data->temp8[nr] = temp_to_s8(val); + i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; } @@ -295,7 +402,16 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u16(data->temp11[attr->index]); + else + temp = temp_from_s16(data->temp11[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -318,13 +434,20 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); + data->temp11[nr] = temp_to_u16_adt7461(data, val); + else if (data->kind == max6657 || data->kind == max6680) + data->temp11[nr] = temp_to_s8(val) << 8; + else if (data->kind == max6646) + data->temp11[nr] = temp_to_u8(val) << 8; else - data->temp11[nr] = TEMP2_TO_REG(val); + data->temp11[nr] = temp_to_s16(val); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646) + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], + data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); return count; } @@ -334,8 +457,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]) - - TEMP1_FROM_REG(data->temp_hyst)); + int temp; + + if (data->kind == adt7461) + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else + temp = temp_from_s8(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -347,9 +476,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[3]) - val; + hyst = temp_from_s8(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - HYST_TO_REG(hyst)); + hyst_to_reg(hyst)); mutex_unlock(&data->update_lock); return count; } @@ -371,23 +500,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); + set_temp8, 0); static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); + set_temp8, 1); static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 2); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); + set_temp8, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 4); + set_temp8, 3); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, 3); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); + set_temphyst, 2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 3); @@ -568,7 +697,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind, kind = adm1032; } else if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ + && (reg_config1 & 0x1B) == 0x00 && reg_convrate <= 0x0A) { kind = adt7461; } @@ -599,13 +728,23 @@ static int lm90_detect(struct i2c_client *new_client, int kind, && (reg_config1 & 0x03) == 0x00 && reg_convrate <= 0x07) { kind = max6680; + } else + /* The chip_id register of the MAX6646/6647/6649 + * holds the revision of the chip. + * The lowest 6 bits of the config1 register are + * unused and should return zero when read. + */ + if (chip_id == 0x59 + && (reg_config1 & 0x3f) == 0x00 + && reg_convrate <= 0x07) { + kind = max6646; } } if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); + dev_dbg(&adapter->dev, + "Unsupported chip at 0x%02x (man_id=0x%02X, " + "chip_id=0x%02X)\n", address, man_id, chip_id); return -ENODEV; } } @@ -629,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind, name = "max6680"; } else if (kind == adt7461) { name = "adt7461"; + } else if (kind == max6646) { + name = "max6646"; } strlcpy(info->type, name, I2C_NAME_SIZE); @@ -668,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client, &dev_attr_pec))) goto exit_remove_files; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if ((err = device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr))) goto exit_remove_files; @@ -707,6 +848,12 @@ static void lm90_init_client(struct i2c_client *client) } config_orig = config; + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + /* * Put MAX6680/MAX8881 into extended resolution (bit 0x10, * 0.125 degree resolution) and range (0x08, extend range @@ -728,7 +875,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm90_group); device_remove_file(&client->dev, &dev_attr_pec); - if (data->kind != max6657) + if (data->kind != max6657 && data->kind != max6646) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -736,6 +883,38 @@ static int lm90_remove(struct i2c_client *client) return 0; } +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +{ + int err; + u8 oldh, newh, l; + + /* + * There is a trick here. We have to read two registers to have the + * sensor temperature, but we have to beware a conversion could occur + * inbetween the readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * byte once, then the low byte, then the high byte again. If the new + * high byte matches the old one, then we have a valid reading. Else + * we have to read the low byte again, and now we believe we have a + * correct reading. + */ + if ((err = lm90_read_reg(client, regh, &oldh)) + || (err = lm90_read_reg(client, regl, &l)) + || (err = lm90_read_reg(client, regh, &newh))) + return err; + if (oldh != newh) { + err = lm90_read_reg(client, regl, &l); + if (err) + return err; + } + *value = (newh << 8) | l; + + return 0; +} + static struct lm90_data *lm90_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -744,49 +923,50 @@ static struct lm90_data *lm90_update_device(struct device *dev) mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - u8 oldh, newh, l; + u8 h, l; dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - /* - * There is a trick here. We have to read two registers to - * have the remote sensor temperature, but we have to beware - * a conversion could occur inbetween the readings. The - * datasheet says we should either use the one-shot - * conversion register, which we don't want to do (disables - * hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit - * at the exact same time). So the solution used here is to - * read the high byte once, then the low byte, then the high - * byte again. If the new high byte matches the old one, - * then we have a valid reading. Else we have to read the low - * byte again, and now we believe we have a correct reading. - */ - if (lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &oldh) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &newh) == 0 - && (newh == oldh - || lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0)) - data->temp11[0] = (newh << 8) | l; - - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &newh) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) - data->temp11[1] = (newh << 8) | l; - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &newh) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) - data->temp11[2] = (newh << 8) | l; - if (data->kind != max6657) { + if (data->kind == max6657 || data->kind == max6646) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[1] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[1] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[2] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[2] |= l; + } + + if (data->kind != max6657 && data->kind != max6646) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, - &newh) == 0 + &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, &l) == 0) - data->temp11[3] = (newh << 8) | l; + data->temp11[3] = (h << 8) | l; } lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 1ab1cacad59..7897754f3a5 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619); #define MAX1619_REG_W_TCRIT_HYST 0x13 /* - * Conversions and various macros + * Conversions */ -#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) -#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) +static int temp_from_reg(int val) +{ + return (val & 0x80 ? val-0x100 : val) * 1000; +} + +static int temp_to_reg(int val) +{ + return (val < 0 ? val+0x100*1000 : val) / 1000; +} /* * Functions declaration @@ -135,7 +142,7 @@ struct max1619_data { static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct max1619_data *data = max1619_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ + return sprintf(buf, "%d\n", temp_from_reg(data->value)); \ } show_temp(temp_input1); show_temp(temp_input2); @@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ - data->value = TEMP_TO_REG(val); \ + data->value = temp_to_reg(val); \ i2c_smbus_write_byte_data(client, reg, data->value); \ mutex_unlock(&data->update_lock); \ return count; \ diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 9b462bb13fa..5fbfa34c110 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -75,7 +75,8 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define FSCM 0x09 /* Logical device: fans */ #define VLM 0x0d /* Logical device: voltages */ #define TMS 0x0e /* Logical device: temperatures */ -static const u8 logdev[3] = { FSCM, VLM, TMS }; +#define LDNI_MAX 3 +static const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS }; #define LD_FAN 0 #define LD_IN 1 @@ -489,11 +490,66 @@ static struct sensor_device_attribute in_max[] = { SENSOR_ATTR(in10_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 10), }; +/* (temp & vin) channel status register alarm bits (pdf sec.11.5.12) */ +#define CHAN_ALM_MIN 0x02 /* min limit crossed */ +#define CHAN_ALM_MAX 0x04 /* max limit exceeded */ +#define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */ + +/* show_in_min/max_alarm() reads data from the per-channel status + register (sec 11.5.12), not the vin event status registers (sec + 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms) */ + +static ssize_t show_in_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); +} +static ssize_t show_in_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} + +static struct sensor_device_attribute in_min_alarm[] = { + SENSOR_ATTR(in0_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 0), + SENSOR_ATTR(in1_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 1), + SENSOR_ATTR(in2_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 2), + SENSOR_ATTR(in3_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 3), + SENSOR_ATTR(in4_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 4), + SENSOR_ATTR(in5_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 5), + SENSOR_ATTR(in6_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 6), + SENSOR_ATTR(in7_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 7), + SENSOR_ATTR(in8_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 8), + SENSOR_ATTR(in9_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 9), + SENSOR_ATTR(in10_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 10), +}; +static struct sensor_device_attribute in_max_alarm[] = { + SENSOR_ATTR(in0_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 0), + SENSOR_ATTR(in1_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 1), + SENSOR_ATTR(in2_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 2), + SENSOR_ATTR(in3_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 3), + SENSOR_ATTR(in4_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 4), + SENSOR_ATTR(in5_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 5), + SENSOR_ATTR(in6_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 6), + SENSOR_ATTR(in7_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 7), + SENSOR_ATTR(in8_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 8), + SENSOR_ATTR(in9_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 9), + SENSOR_ATTR(in10_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 10), +}; + #define VIN_UNIT_ATTRS(X) \ &in_input[X].dev_attr.attr, \ &in_status[X].dev_attr.attr, \ &in_min[X].dev_attr.attr, \ - &in_max[X].dev_attr.attr + &in_max[X].dev_attr.attr, \ + &in_min_alarm[X].dev_attr.attr, \ + &in_max_alarm[X].dev_attr.attr static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { @@ -658,12 +714,68 @@ static struct sensor_device_attribute therm_crit[] = { show_therm_crit, set_therm_crit, 2+11), }; +/* show_therm_min/max_alarm() reads data from the per-channel voltage + status register (sec 11.5.12) */ + +static ssize_t show_therm_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); +} +static ssize_t show_therm_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} +static ssize_t show_therm_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT)); +} + +static struct sensor_device_attribute therm_min_alarm[] = { + SENSOR_ATTR(temp4_min_alarm, S_IRUGO, + show_therm_min_alarm, NULL, 0+11), + SENSOR_ATTR(temp5_min_alarm, S_IRUGO, + show_therm_min_alarm, NULL, 1+11), + SENSOR_ATTR(temp6_min_alarm, S_IRUGO, + show_therm_min_alarm, NULL, 2+11), +}; +static struct sensor_device_attribute therm_max_alarm[] = { + SENSOR_ATTR(temp4_max_alarm, S_IRUGO, + show_therm_max_alarm, NULL, 0+11), + SENSOR_ATTR(temp5_max_alarm, S_IRUGO, + show_therm_max_alarm, NULL, 1+11), + SENSOR_ATTR(temp6_max_alarm, S_IRUGO, + show_therm_max_alarm, NULL, 2+11), +}; +static struct sensor_device_attribute therm_crit_alarm[] = { + SENSOR_ATTR(temp4_crit_alarm, S_IRUGO, + show_therm_crit_alarm, NULL, 0+11), + SENSOR_ATTR(temp5_crit_alarm, S_IRUGO, + show_therm_crit_alarm, NULL, 1+11), + SENSOR_ATTR(temp6_crit_alarm, S_IRUGO, + show_therm_crit_alarm, NULL, 2+11), +}; + #define THERM_UNIT_ATTRS(X) \ &therm_input[X].dev_attr.attr, \ &therm_status[X].dev_attr.attr, \ &therm_min[X].dev_attr.attr, \ &therm_max[X].dev_attr.attr, \ - &therm_crit[X].dev_attr.attr + &therm_crit[X].dev_attr.attr, \ + &therm_min_alarm[X].dev_attr.attr, \ + &therm_max_alarm[X].dev_attr.attr, \ + &therm_crit_alarm[X].dev_attr.attr static struct attribute * pc8736x_therm_attr_array[] = { THERM_UNIT_ATTRS(0), @@ -790,12 +902,76 @@ static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *att } static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL); +/* show_temp_min/max_alarm() reads data from the per-channel status + register (sec 12.3.7), not the temp event status registers (sec + 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) */ + +static ssize_t show_temp_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); +} +static ssize_t show_temp_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); +} +static ssize_t show_temp_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT)); +} + +static struct sensor_device_attribute temp_min_alarm[] = { + SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 0), + SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 1), + SENSOR_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 2), +}; +static struct sensor_device_attribute temp_max_alarm[] = { + SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 0), + SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 1), + SENSOR_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 2), +}; +static struct sensor_device_attribute temp_crit_alarm[] = { + SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 0), + SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 1), + SENSOR_ATTR(temp3_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 2), +}; + +#define TEMP_FAULT 0x40 /* open diode */ +static ssize_t show_temp_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT)); +} +static struct sensor_device_attribute temp_fault[] = { + SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0), + SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1), + SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2), +}; + #define TEMP_UNIT_ATTRS(X) \ &temp_input[X].dev_attr.attr, \ &temp_status[X].dev_attr.attr, \ &temp_min[X].dev_attr.attr, \ &temp_max[X].dev_attr.attr, \ - &temp_crit[X].dev_attr.attr + &temp_crit[X].dev_attr.attr, \ + &temp_min_alarm[X].dev_attr.attr, \ + &temp_max_alarm[X].dev_attr.attr, \ + &temp_crit_alarm[X].dev_attr.attr, \ + &temp_fault[X].dev_attr.attr static struct attribute * pc8736x_temp_attr_array[] = { TEMP_UNIT_ATTRS(0), @@ -809,8 +985,8 @@ static const struct attribute_group pc8736x_temp_group = { .attrs = pc8736x_temp_attr_array, }; -static ssize_t show_name(struct device *dev, struct device_attribute - *devattr, char *buf) +static ssize_t show_name(struct device *dev, + struct device_attribute *devattr, char *buf) { struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%s\n", data->name); @@ -955,7 +1131,7 @@ static int __devinit pc87360_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); - for (i = 0; i < 3; i++) { + for (i = 0; i < LDNI_MAX; i++) { if (((data->address[i] = extra_isa[i])) && !request_region(extra_isa[i], PC87360_EXTENT, pc87360_driver.driver.name)) { @@ -1031,7 +1207,15 @@ static int __devinit pc87360_probe(struct platform_device *pdev) || (err = device_create_file(dev, &temp_crit[i].dev_attr)) || (err = device_create_file(dev, - &temp_status[i].dev_attr))) + &temp_status[i].dev_attr)) + || (err = device_create_file(dev, + &temp_min_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &temp_max_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &temp_crit_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &temp_fault[i].dev_attr))) goto ERROR3; } if ((err = device_create_file(dev, &dev_attr_alarms_temp))) @@ -1131,6 +1315,16 @@ static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, mutex_unlock(&(data->lock)); } +/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ +#define CHAN_CNVRTD 0x80 /* new data ready */ +#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ +#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ +#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + +#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ +#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ +#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + static void pc87360_init_device(struct platform_device *pdev, int use_thermistors) { @@ -1152,11 +1346,12 @@ static void pc87360_init_device(struct platform_device *pdev, nr = data->innr < 11 ? data->innr : 11; for (i = 0; i < nr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); if (init >= init_in[i]) { /* Forcibly enable voltage channel */ - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - if (!(reg & 0x01)) { + if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling in%d\n", i); pc87360_write_value(data, LD_IN, i, @@ -1168,19 +1363,24 @@ static void pc87360_init_device(struct platform_device *pdev, /* We can't blindly trust the Super-I/O space configuration bit, most BIOS won't set it properly */ + dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); for (i = 11; i < data->innr; i++) { reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_TEMP_STATUS); - use_thermistors = use_thermistors || (reg & 0x01); + use_thermistors = use_thermistors || (reg & CHAN_ENA); + /* thermistors are temp[4-6], measured on vin[11-14] */ + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); } + dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); i = use_thermistors ? 2 : 0; for (; i < data->tempnr; i++) { + reg = pc87360_read_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i+1, reg); if (init >= init_temp[i]) { /* Forcibly enable temperature channel */ - reg = pc87360_read_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - if (!(reg & 0x01)) { + if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling temp%d\n", i+1); pc87360_write_value(data, LD_TEMP, i, @@ -1197,7 +1397,7 @@ static void pc87360_init_device(struct platform_device *pdev, diodes */ reg = pc87360_read_value(data, LD_TEMP, (i-11)/2, PC87365_REG_TEMP_STATUS); - if (reg & 0x01) { + if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Skipping " "temp%d, pin already in use " "by temp%d\n", i-7, (i-11)/2); @@ -1207,7 +1407,7 @@ static void pc87360_init_device(struct platform_device *pdev, /* Forcibly enable thermistor channel */ reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_STATUS); - if (!(reg & 0x01)) { + if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling temp%d\n", i-7); pc87360_write_value(data, LD_IN, i, @@ -1221,7 +1421,8 @@ static void pc87360_init_device(struct platform_device *pdev, if (data->innr) { reg = pc87360_read_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONFIG); - if (reg & 0x01) { + dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); + if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Forcibly " "enabling monitoring (VLM)\n"); pc87360_write_value(data, LD_IN, NO_BANK, @@ -1233,7 +1434,8 @@ static void pc87360_init_device(struct platform_device *pdev, if (data->tempnr) { reg = pc87360_read_value(data, LD_TEMP, NO_BANK, PC87365_REG_TEMP_CONFIG); - if (reg & 0x01) { + dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); + if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Forcibly enabling " "monitoring (TMS)\n"); pc87360_write_value(data, LD_TEMP, NO_BANK, @@ -1336,11 +1538,11 @@ static struct pc87360_data *pc87360_update_device(struct device *dev) pc87360_write_value(data, LD_IN, i, PC87365_REG_IN_STATUS, data->in_status[i]); - if ((data->in_status[i] & 0x81) == 0x81) { + if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { data->in[i] = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN); } - if (data->in_status[i] & 0x01) { + if (data->in_status[i] & CHAN_ENA) { data->in_min[i] = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_MIN); @@ -1373,12 +1575,12 @@ static struct pc87360_data *pc87360_update_device(struct device *dev) pc87360_write_value(data, LD_TEMP, i, PC87365_REG_TEMP_STATUS, data->temp_status[i]); - if ((data->temp_status[i] & 0x81) == 0x81) { + if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { data->temp[i] = pc87360_read_value(data, LD_TEMP, i, PC87365_REG_TEMP); } - if (data->temp_status[i] & 0x01) { + if (data->temp_status[i] & CHAN_ENA) { data->temp_min[i] = pc87360_read_value(data, LD_TEMP, i, PC87365_REG_TEMP_MIN); diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index f942ecdd47c..d4d1b859d4f 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -4,7 +4,7 @@ Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark Studebaker <mdsxyz123@yahoo.com> - Copyright (c) 2007 Jean Delvare <khali@linux-fr.org> + Copyright (c) 2007 - 2008 Jean Delvare <khali@linux-fr.org> 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 @@ -38,25 +38,24 @@ #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> -#include <linux/platform_device.h> -#include <linux/ioport.h> #include <linux/hwmon.h> #include <linux/hwmon-vid.h> #include <linux/hwmon-sysfs.h> #include <linux/sysfs.h> #include <linux/err.h> #include <linux/mutex.h> + +#ifdef CONFIG_ISA +#include <linux/platform_device.h> +#include <linux/ioport.h> #include <asm/io.h> -#include "lm75.h" +#endif -/* ISA device, if found */ -static struct platform_device *pdev; +#include "lm75.h" /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned short isa_address = 0x290; - /* Insmod parameters */ I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " @@ -178,9 +177,9 @@ FAN_FROM_REG(u8 val, int div) #define TEMP_FROM_REG(val) ((val) * 1000) #define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ - (val) ^ 0x7fff : (val)) + (~(val)) & 0x7fff : (val) & 0xff7fff) #define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ - (~(val)) & 0x7fff : (val) & 0xffffff) + (~(val)) & 0x7fff : (val) & 0xff7fff) #define DIV_FROM_REG(val) (1 << (val)) @@ -199,25 +198,16 @@ DIV_TO_REG(long val, enum chips type) return i; } -/* There are some complications in a module like this. First off, W83781D chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - W83781D chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the W83781D at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ - -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct w83781d_data { - struct i2c_client client; + struct i2c_client *client; struct device *hwmon_dev; struct mutex lock; enum chips type; + /* For ISA device only */ + const char *name; + int isa_addr; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -240,7 +230,6 @@ struct w83781d_data { u8 vid; /* Register encoding, combined */ u32 alarms; /* Register encoding, combined */ u32 beep_mask; /* Register encoding, combined */ - u8 beep_enable; /* Boolean */ u8 pwm[4]; /* Register value */ u8 pwm2_enable; /* Boolean */ u16 sens[3]; /* 782D/783S only. @@ -249,36 +238,14 @@ struct w83781d_data { u8 vrm; }; -static int w83781d_attach_adapter(struct i2c_adapter *adapter); -static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); -static int w83781d_detach_client(struct i2c_client *client); - -static int __devinit w83781d_isa_probe(struct platform_device *pdev); -static int __devexit w83781d_isa_remove(struct platform_device *pdev); +static struct w83781d_data *w83781d_data_if_isa(void); +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid); static int w83781d_read_value(struct w83781d_data *data, u16 reg); static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); static struct w83781d_data *w83781d_update_device(struct device *dev); static void w83781d_init_device(struct device *dev); -static struct i2c_driver w83781d_driver = { - .driver = { - .name = "w83781d", - }, - .attach_adapter = w83781d_attach_adapter, - .detach_client = w83781d_detach_client, -}; - -static struct platform_driver w83781d_isa_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "w83781d", - }, - .probe = w83781d_isa_probe, - .remove = w83781d_isa_remove, -}; - - /* following are the sysfs callback functions */ #define show_in_reg(reg) \ static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ @@ -513,11 +480,6 @@ static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr return sprintf(buf, "%ld\n", (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); } -static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long)data->beep_enable); -} static ssize_t store_beep_mask(struct device *dev, struct device_attribute *attr, @@ -529,12 +491,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); - data->beep_mask = BEEP_MASK_TO_REG(val, data->type); + data->beep_mask &= 0x8000; /* preserve beep enable */ + data->beep_mask |= BEEP_MASK_TO_REG(val, data->type); w83781d_write_value(data, W83781D_REG_BEEP_INTS1, data->beep_mask & 0xff); w83781d_write_value(data, W83781D_REG_BEEP_INTS2, - ((data->beep_mask >> 8) & 0x7f) - | data->beep_enable << 7); + (data->beep_mask >> 8) & 0xff); if (data->type != w83781d && data->type != as99127f) { w83781d_write_value(data, W83781D_REG_BEEP_INTS3, ((data->beep_mask) >> 16) & 0xff); @@ -544,31 +506,8 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t -store_beep_enable(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83781d_data *data = dev_get_drvdata(dev); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - if (val != 0 && val != 1) - return -EINVAL; - - mutex_lock(&data->update_lock); - data->beep_enable = val; - val = w83781d_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f; - val |= data->beep_enable << 7; - w83781d_write_value(data, W83781D_REG_BEEP_INTS2, val); - mutex_unlock(&data->update_lock); - - return count; -} - static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR, show_beep_mask, store_beep_mask); -static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, - show_beep_enable, store_beep_enable); static ssize_t show_beep(struct device *dev, struct device_attribute *attr, char *buf) @@ -663,6 +602,8 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, 5); static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_temp3_beep, store_beep, 13); +static SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, + show_beep, store_beep, 15); static ssize_t show_fan_div(struct device *dev, struct device_attribute *da, char *buf) @@ -866,45 +807,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_sensor, store_sensor, 2); -/* I2C devices get this name attribute automatically, but for ISA devices - we must create it by ourselves. */ -static ssize_t -show_name(struct device *dev, struct device_attribute *devattr, char *buf) -{ - struct w83781d_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); -} -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - -/* This function is called when: - * w83781d_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and w83781d_driver is still present) */ -static int -w83781d_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_probe(adapter, &addr_data, w83781d_detect); -} - /* Assumes that adapter is of I2C, not ISA variety. * OTHERWISE DON'T CALL THIS */ static int -w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, - struct i2c_client *new_client) +w83781d_detect_subclients(struct i2c_client *new_client) { int i, val1 = 0, id; int err; - const char *client_name = ""; + int address = new_client->addr; + unsigned short sc_addr[2]; + struct i2c_adapter *adapter = new_client->adapter; struct w83781d_data *data = i2c_get_clientdata(new_client); - - data->lm75[0] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[0])) { - err = -ENOMEM; - goto ERROR_SC_0; - } + enum chips kind = data->type; id = i2c_adapter_id(adapter); @@ -922,55 +837,35 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, w83781d_write_value(data, W83781D_REG_I2C_SUBADDR, (force_subclients[2] & 0x07) | ((force_subclients[3] & 0x07) << 4)); - data->lm75[0]->addr = force_subclients[2]; + sc_addr[0] = force_subclients[2]; } else { val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR); - data->lm75[0]->addr = 0x48 + (val1 & 0x07); + sc_addr[0] = 0x48 + (val1 & 0x07); } if (kind != w83783s) { - data->lm75[1] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[1])) { - err = -ENOMEM; - goto ERROR_SC_1; - } - if (force_subclients[0] == id && force_subclients[1] == address) { - data->lm75[1]->addr = force_subclients[3]; + sc_addr[1] = force_subclients[3]; } else { - data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); + sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07); } - if (data->lm75[0]->addr == data->lm75[1]->addr) { + if (sc_addr[0] == sc_addr[1]) { dev_err(&new_client->dev, "Duplicate addresses 0x%x for subclients.\n", - data->lm75[0]->addr); + sc_addr[0]); err = -EBUSY; goto ERROR_SC_2; } } - if (kind == w83781d) - client_name = "w83781d subclient"; - else if (kind == w83782d) - client_name = "w83782d subclient"; - else if (kind == w83783s) - client_name = "w83783s subclient"; - else if (kind == as99127f) - client_name = "as99127f subclient"; - for (i = 0; i <= 1; i++) { - /* store all data in w83781d */ - i2c_set_clientdata(data->lm75[i], NULL); - data->lm75[i]->adapter = adapter; - data->lm75[i]->driver = &w83781d_driver; - data->lm75[i]->flags = 0; - strlcpy(data->lm75[i]->name, client_name, - I2C_NAME_SIZE); - if ((err = i2c_attach_client(data->lm75[i]))) { + data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); + if (!data->lm75[i]) { dev_err(&new_client->dev, "Subclient %d " "registration at address 0x%x " - "failed.\n", i, data->lm75[i]->addr); + "failed.\n", i, sc_addr[i]); + err = -ENOMEM; if (i == 1) goto ERROR_SC_3; goto ERROR_SC_2; @@ -983,12 +878,9 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, /* Undo inits in case of errors */ ERROR_SC_3: - i2c_detach_client(data->lm75[0]); + i2c_unregister_device(data->lm75[0]); ERROR_SC_2: - kfree(data->lm75[1]); ERROR_SC_1: - kfree(data->lm75[0]); -ERROR_SC_0: return err; } @@ -1029,7 +921,7 @@ static struct attribute* w83781d_attributes[] = { &dev_attr_vrm.attr, &dev_attr_alarms.attr, &dev_attr_beep_mask.attr, - &dev_attr_beep_enable.attr, + &sensor_dev_attr_beep_enable.dev_attr.attr, NULL }; static const struct attribute_group w83781d_group = { @@ -1151,96 +1043,74 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) } } - if (is_isa) { - err = device_create_file(&pdev->dev, &dev_attr_name); - if (err) - return err; - } - return 0; } +/* Return 0 if detection is successful, -ENODEV otherwise */ static int -w83781d_detect(struct i2c_adapter *adapter, int address, int kind) +w83781d_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { int val1 = 0, val2; - struct i2c_client *client; - struct device *dev; - struct w83781d_data *data; - int err; + struct w83781d_data *isa = w83781d_data_if_isa(); + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *client_name = ""; enum vendor { winbond, asus } vendid; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -EINVAL; - goto ERROR1; - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access w83781d_{read,write}_value. */ - - if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - mutex_init(&data->lock); - client->adapter = adapter; - client->driver = &w83781d_driver; - dev = &client->dev; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - /* Now, we do the remaining detection. */ + /* We block updates of the ISA device to minimize the risk of + concurrent access to the same W83781D chip through different + interfaces. */ + if (isa) + mutex_lock(&isa->update_lock); /* The w8378?d may be stuck in some other bank than bank 0. This may make reading other information impossible. Specify a force=... or force_*=... parameter, and the Winbond will be reset to the right bank. */ if (kind < 0) { - if (w83781d_read_value(data, W83781D_REG_CONFIG) & 0x80) { + if (i2c_smbus_read_byte_data + (client, W83781D_REG_CONFIG) & 0x80) { dev_dbg(&adapter->dev, "Detection of w83781d chip " "failed at step 3\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - val1 = w83781d_read_value(data, W83781D_REG_BANK); - val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); /* Check for Winbond or Asus ID if in bank 0 */ if ((!(val1 & 0x07)) && (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { dev_dbg(&adapter->dev, "Detection of w83781d chip " "failed at step 4\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } /* If Winbond SMBus, check address at 0x48. Asus doesn't support, except for as99127f rev.2 */ if ((!(val1 & 0x80) && (val2 == 0xa3)) || ((val1 & 0x80) && (val2 == 0x5c))) { - if (w83781d_read_value - (data, W83781D_REG_I2C_ADDR) != address) { + if (i2c_smbus_read_byte_data + (client, W83781D_REG_I2C_ADDR) != address) { dev_dbg(&adapter->dev, "Detection of w83781d " "chip failed at step 5\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } } /* We have either had a force parameter, or we have already detected the Winbond. Put it now into bank 0 and Vendor ID High Byte */ - w83781d_write_value(data, W83781D_REG_BANK, - (w83781d_read_value(data, W83781D_REG_BANK) - & 0x78) | 0x80); + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + (i2c_smbus_read_byte_data(client, W83781D_REG_BANK) + & 0x78) | 0x80); /* Determine the chip type. */ if (kind <= 0) { /* get vendor ID */ - val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); if (val2 == 0x5c) vendid = winbond; else if (val2 == 0x12) @@ -1248,11 +1118,10 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) else { dev_dbg(&adapter->dev, "w83781d chip vendor is " "neither Winbond nor Asus\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - val1 = w83781d_read_value(data, W83781D_REG_WCHIPID); + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) kind = w83781d; else if (val1 == 0x30 && vendid == winbond) @@ -1266,11 +1135,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) dev_warn(&adapter->dev, "Ignoring 'force' " "parameter for unknown chip at " "address 0x%02x\n", address); - err = -EINVAL; - goto ERROR2; + goto err_nodev; + } + + if ((kind == w83781d || kind == w83782d) + && w83781d_alias_detect(client, val1)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + goto err_nodev; } } + if (isa) + mutex_unlock(&isa->update_lock); + if (kind == w83781d) { client_name = "w83781d"; } else if (kind == w83782d) { @@ -1281,24 +1159,46 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) client_name = "as99127f"; } - /* Fill in the remaining client fields and put into the global list */ - strlcpy(client->name, client_name, I2C_NAME_SIZE); - data->type = kind; + strlcpy(info->type, client_name, I2C_NAME_SIZE); + + return 0; + + err_nodev: + if (isa) + mutex_unlock(&isa->update_lock); + return -ENODEV; +} + +static int +w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct w83781d_data *data; + int err; + + data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto ERROR1; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + mutex_init(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(client))) - goto ERROR2; + data->type = id->driver_data; + data->client = client; /* attach secondary i2c lm75-like clients */ - if ((err = w83781d_detect_subclients(adapter, address, - kind, client))) + err = w83781d_detect_subclients(client); + if (err) goto ERROR3; /* Initialize the chip */ w83781d_init_device(dev); /* Register sysfs hooks */ - err = w83781d_create_files(dev, kind, 0); + err = w83781d_create_files(dev, data->type, 0); if (err) goto ERROR4; @@ -1314,264 +1214,113 @@ ERROR4: sysfs_remove_group(&dev->kobj, &w83781d_group); sysfs_remove_group(&dev->kobj, &w83781d_group_opt); - if (data->lm75[1]) { - i2c_detach_client(data->lm75[1]); - kfree(data->lm75[1]); - } - if (data->lm75[0]) { - i2c_detach_client(data->lm75[0]); - kfree(data->lm75[0]); - } + if (data->lm75[0]) + i2c_unregister_device(data->lm75[0]); + if (data->lm75[1]) + i2c_unregister_device(data->lm75[1]); ERROR3: - i2c_detach_client(client); -ERROR2: + i2c_set_clientdata(client, NULL); kfree(data); ERROR1: return err; } static int -w83781d_detach_client(struct i2c_client *client) +w83781d_remove(struct i2c_client *client) { struct w83781d_data *data = i2c_get_clientdata(client); - int err; - - /* main client */ - if (data) { - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &w83781d_group); - sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt); - } - - if ((err = i2c_detach_client(client))) - return err; + struct device *dev = &client->dev; - /* main client */ - if (data) - kfree(data); - - /* subclient */ - else - kfree(client); - - return 0; -} - -static int __devinit -w83781d_isa_probe(struct platform_device *pdev) -{ - int err, reg; - struct w83781d_data *data; - struct resource *res; - const char *name; - - /* Reserve the ISA region */ - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, - "w83781d")) { - err = -EBUSY; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release_region; - } - mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); - platform_set_drvdata(pdev, data); - - reg = w83781d_read_value(data, W83781D_REG_WCHIPID); - switch (reg) { - case 0x30: - data->type = w83782d; - name = "w83782d"; - break; - default: - data->type = w83781d; - name = "w83781d"; - } - strlcpy(data->client.name, name, I2C_NAME_SIZE); - - /* Initialize the W83781D chip */ - w83781d_init_device(&pdev->dev); - - /* Register sysfs hooks */ - err = w83781d_create_files(&pdev->dev, data->type, 1); - if (err) - goto exit_remove_files; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; + hwmon_device_unregister(data->hwmon_dev); - exit_remove_files: - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - exit_release_region: - release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); - exit: - return err; -} + sysfs_remove_group(&dev->kobj, &w83781d_group); + sysfs_remove_group(&dev->kobj, &w83781d_group_opt); -static int __devexit -w83781d_isa_remove(struct platform_device *pdev) -{ - struct w83781d_data *data = platform_get_drvdata(pdev); + if (data->lm75[0]) + i2c_unregister_device(data->lm75[0]); + if (data->lm75[1]) + i2c_unregister_device(data->lm75[1]); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); - device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); + i2c_set_clientdata(client, NULL); kfree(data); return 0; } -/* The SMBus locks itself, usually, but nothing may access the Winbond between - bank switches. ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ static int -w83781d_read_value(struct w83781d_data *data, u16 reg) +w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; - int res, word_sized, bank; + struct i2c_client *client = data->client; + int res, bank; struct i2c_client *cl; - mutex_lock(&data->lock); - if (!client->driver) { /* ISA device */ - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(client->addr + - W83781D_DATA_REG_OFFSET); - } - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - res = i2c_smbus_read_byte_data(client, reg & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(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)); - break; - case 0x55: /* OVER */ - default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); - break; - } + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = swab16(i2c_smbus_read_word_data(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)); + break; + case 0x55: /* OVER */ + default: + res = swab16(i2c_smbus_read_word_data(cl, 3)); + break; } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); } - mutex_unlock(&data->lock); + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + return res; } static int -w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; - int word_sized, bank; + struct i2c_client *client = data->client; + int bank; struct i2c_client *cl; - mutex_lock(&data->lock); - if (!client->driver) { /* ISA device */ - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - i2c_smbus_write_byte_data(client, reg & 0xff, - value & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x52: /* CONFIG */ - i2c_smbus_write_byte_data(cl, 1, value & 0xff); - break; - case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); - break; - case 0x55: /* OVER */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); - break; - } + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, swab16(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, swab16(value)); + break; } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); } - mutex_unlock(&data->lock); + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + return 0; } @@ -1678,7 +1427,7 @@ w83781d_init_device(struct device *dev) static struct w83781d_data *w83781d_update_device(struct device *dev) { struct w83781d_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int i; mutex_lock(&data->update_lock); @@ -1775,8 +1524,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) W83781D_REG_ALARM2) << 8); } i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); - data->beep_enable = i >> 7; - data->beep_mask = ((i & 0x7f) << 8) + + data->beep_mask = (i << 8) + w83781d_read_value(data, W83781D_REG_BEEP_INTS1); if ((data->type != w83781d) && (data->type != as99127f)) { data->beep_mask |= @@ -1792,6 +1540,275 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) return data; } +static const struct i2c_device_id w83781d_ids[] = { + { "w83781d", w83781d, }, + { "w83782d", w83782d, }, + { "w83783s", w83783s, }, + { "as99127f", as99127f }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, w83781d_ids); + +static struct i2c_driver w83781d_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "w83781d", + }, + .probe = w83781d_probe, + .remove = w83781d_remove, + .id_table = w83781d_ids, + .detect = w83781d_detect, + .address_data = &addr_data, +}; + +/* + * ISA related code + */ +#ifdef CONFIG_ISA + +/* ISA device, if found */ +static struct platform_device *pdev; + +static unsigned short isa_address = 0x290; + +/* I2C devices get this name attribute automatically, but for ISA devices + we must create it by ourselves. */ +static ssize_t +show_name(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct w83781d_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct w83781d_data *w83781d_data_if_isa(void) +{ + return pdev ? platform_get_drvdata(pdev) : NULL; +} + +/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + struct w83781d_data *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + isa = platform_get_drvdata(pdev); + + if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (w83781d_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) + return 0; + } + if (w83781d_read_value(isa, W83781D_REG_CONFIG) != + i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (w83781d_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) + return 0; + } + + return 1; +} + +static int +w83781d_read_value_isa(struct w83781d_data *data, u16 reg) +{ + int word_sized, res; + + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + data->isa_addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + data->isa_addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(data->isa_addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); + } + return res; +} + +static void +w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) +{ + int word_sized; + + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + data->isa_addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + data->isa_addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + data->isa_addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); + } +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +static int +w83781d_read_value(struct w83781d_data *data, u16 reg) +{ + struct i2c_client *client = data->client; + int res; + + mutex_lock(&data->lock); + if (client) + res = w83781d_read_value_i2c(data, reg); + else + res = w83781d_read_value_isa(data, reg); + mutex_unlock(&data->lock); + return res; +} + +static int +w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +{ + struct i2c_client *client = data->client; + + mutex_lock(&data->lock); + if (client) + w83781d_write_value_i2c(data, reg, value); + else + w83781d_write_value_isa(data, reg, value); + mutex_unlock(&data->lock); + return 0; +} + +static int __devinit +w83781d_isa_probe(struct platform_device *pdev) +{ + int err, reg; + struct w83781d_data *data; + struct resource *res; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, + "w83781d")) { + err = -EBUSY; + goto exit; + } + + data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit_release_region; + } + mutex_init(&data->lock); + data->isa_addr = res->start; + platform_set_drvdata(pdev, data); + + reg = w83781d_read_value(data, W83781D_REG_WCHIPID); + switch (reg) { + case 0x30: + data->type = w83782d; + data->name = "w83782d"; + break; + default: + data->type = w83781d; + data->name = "w83781d"; + } + + /* Initialize the W83781D chip */ + w83781d_init_device(&pdev->dev); + + /* Register sysfs hooks */ + err = w83781d_create_files(&pdev->dev, data->type, 1); + if (err) + goto exit_remove_files; + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto exit_remove_files; + + data->hwmon_dev = hwmon_device_register(&pdev->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(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + exit_release_region: + release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); + exit: + return err; +} + +static int __devexit +w83781d_isa_remove(struct platform_device *pdev) +{ + struct w83781d_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + release_region(data->isa_addr + W83781D_ADDR_REG_OFFSET, 2); + kfree(data); + + return 0; +} + +static struct platform_driver w83781d_isa_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "w83781d", + }, + .probe = w83781d_isa_probe, + .remove = __devexit_p(w83781d_isa_remove), +}; + /* return 1 if a supported chip is found, 0 otherwise */ static int __init w83781d_isa_found(unsigned short address) @@ -1928,18 +1945,14 @@ w83781d_isa_device_add(unsigned short address) } static int __init -sensors_w83781d_init(void) +w83781d_isa_register(void) { int res; - res = i2c_add_driver(&w83781d_driver); - if (res) - goto exit; - if (w83781d_isa_found(isa_address)) { res = platform_driver_register(&w83781d_isa_driver); if (res) - goto exit_unreg_i2c_driver; + goto exit; /* Sets global pdev as a side effect */ res = w83781d_isa_device_add(isa_address); @@ -1949,21 +1962,94 @@ sensors_w83781d_init(void) return 0; - exit_unreg_isa_driver: +exit_unreg_isa_driver: platform_driver_unregister(&w83781d_isa_driver); - exit_unreg_i2c_driver: - i2c_del_driver(&w83781d_driver); - exit: +exit: return res; } static void __exit -sensors_w83781d_exit(void) +w83781d_isa_unregister(void) { if (pdev) { platform_device_unregister(pdev); platform_driver_unregister(&w83781d_isa_driver); } +} +#else /* !CONFIG_ISA */ + +static struct w83781d_data *w83781d_data_if_isa(void) +{ + return NULL; +} + +static int +w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + return 0; +} + +static int +w83781d_read_value(struct w83781d_data *data, u16 reg) +{ + int res; + + mutex_lock(&data->lock); + res = w83781d_read_value_i2c(data, reg); + mutex_unlock(&data->lock); + + return res; +} + +static int +w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +{ + mutex_lock(&data->lock); + w83781d_write_value_i2c(data, reg, value); + mutex_unlock(&data->lock); + + return 0; +} + +static int __init +w83781d_isa_register(void) +{ + return 0; +} + +static void __exit +w83781d_isa_unregister(void) +{ +} +#endif /* CONFIG_ISA */ + +static int __init +sensors_w83781d_init(void) +{ + int res; + + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ + res = w83781d_isa_register(); + if (res) + goto exit; + + res = i2c_add_driver(&w83781d_driver); + if (res) + goto exit_unreg_isa; + + return 0; + + exit_unreg_isa: + w83781d_isa_unregister(); + exit: + return res; +} + +static void __exit +sensors_w83781d_exit(void) +{ + w83781d_isa_unregister(); i2c_del_driver(&w83781d_driver); } diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index de21142d106..5768def8a4f 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -23,7 +23,7 @@ Supports following chips: Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83791d 10 5 3 3 0x71 0x5ca3 yes no + w83791d 10 5 5 3 0x71 0x5ca3 yes no The w83791d chip appears to be part way between the 83781d and the 83792d. Thus, this file is derived from both the w83792d.c and @@ -45,6 +45,7 @@ #define NUMBER_OF_VIN 10 #define NUMBER_OF_FANIN 5 #define NUMBER_OF_TEMPIN 3 +#define NUMBER_OF_PWM 5 /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -116,6 +117,25 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = { 0xBD, /* FAN 5 Count Low Limit in DataSheet */ }; +static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { + 0x81, /* PWM 1 duty cycle register in DataSheet */ + 0x83, /* PWM 2 duty cycle register in DataSheet */ + 0x94, /* PWM 3 duty cycle register in DataSheet */ + 0xA0, /* PWM 4 duty cycle register in DataSheet */ + 0xA1, /* PWM 5 duty cycle register in DataSheet */ +}; + +static const u8 W83791D_REG_TEMP_TARGET[3] = { + 0x85, /* PWM 1 target temperature for temp 1 */ + 0x86, /* PWM 2 target temperature for temp 2 */ + 0x96, /* PWM 3 target temperature for temp 3 */ +}; + +static const u8 W83791D_REG_TEMP_TOL[2] = { + 0x87, /* PWM 1/2 temperature tolerance */ + 0x97, /* PWM 3 temperature tolerance */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -160,6 +180,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = { 0xA3, /* BEEP Control Register 3 */ }; +#define W83791D_REG_GPIO 0x15 #define W83791D_REG_CONFIG 0x40 #define W83791D_REG_VID_FANDIV 0x47 #define W83791D_REG_DID_VID4 0x49 @@ -224,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div) (val) < 0 ? ((val) - 250) / 500 * 128 : \ ((val) + 250) / 500 * 128) +/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */ +#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) + +/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */ +#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 15000 ? 15 : \ + ((val) + 500) / 1000) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) #define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) @@ -275,6 +305,14 @@ struct w83791d_data { two sensors with three values (cur, over, hyst) */ + /* PWMs */ + u8 pwm[5]; /* pwm duty cycle */ + u8 pwm_enable[3]; /* pwm enable status for fan 1-3 + (fan 4-5 only support manual mode) */ + + u8 temp_target[3]; /* pwm 1-3 target temperature */ + u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -652,6 +690,217 @@ static struct sensor_device_attribute sda_fan_alarm[] = { SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22), }; +/* read/write PWMs */ +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm[nr]); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm[] = { + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 0), + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 1), + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 2), + SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 3), + SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 4), +}; + +static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1); +} + +static ssize_t store_pwmenable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 reg_cfg_tmp; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + int ret = strict_strtoul(buf, 10, &val); + + if (ret || val < 1 || val > 3) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val - 1; + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 2; + keep_mask = 0xf3; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0xcf; + break; + case 2: + reg_idx = 1; + val_shift = 2; + keep_mask = 0xf3; + break; + } + + reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]); + reg_cfg_tmp = (reg_cfg_tmp & keep_mask) | + data->pwm_enable[nr] << val_shift; + + w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp); + mutex_unlock(&data->update_lock); + + return count; +} +static struct sensor_device_attribute sda_pwmenable[] = { + SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 0), + SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 1), + SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 2), +}; + +/* For Smart Fan I / Thermal Cruise */ +static ssize_t show_temp_target(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr])); +} + +static ssize_t store_temp_target(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->temp_target[nr] = TARGET_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TARGET[nr]) & 0x80; + w83791d_write(client, W83791D_REG_TEMP_TARGET[nr], + data->temp_target[nr] | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_target[] = { + SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 0), + SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 1), + SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 2), +}; + +static ssize_t show_temp_tolerance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr])); +} + +static ssize_t store_temp_tolerance(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 0; + keep_mask = 0xf0; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0x0f; + break; + case 2: + reg_idx = 1; + val_shift = 0; + keep_mask = 0xf0; + break; + } + + mutex_lock(&data->update_lock); + data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask; + w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx], + (data->temp_tolerance[nr] << val_shift) | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_tolerance[] = { + SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0), + SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 1), + SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -908,8 +1157,6 @@ static struct attribute *w83791d_attributes[] = { FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(1), FAN_UNIT_ATTRS(2), - FAN_UNIT_ATTRS(3), - FAN_UNIT_ATTRS(4), TEMP_UNIT_ATTRS(0), TEMP_UNIT_ATTRS(1), TEMP_UNIT_ATTRS(2), @@ -918,6 +1165,18 @@ static struct attribute *w83791d_attributes[] = { &sda_beep_ctrl[1].dev_attr.attr, &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, + &sda_pwm[0].dev_attr.attr, + &sda_pwm[1].dev_attr.attr, + &sda_pwm[2].dev_attr.attr, + &sda_pwmenable[0].dev_attr.attr, + &sda_pwmenable[1].dev_attr.attr, + &sda_pwmenable[2].dev_attr.attr, + &sda_temp_target[0].dev_attr.attr, + &sda_temp_target[1].dev_attr.attr, + &sda_temp_target[2].dev_attr.attr, + &sda_temp_tolerance[0].dev_attr.attr, + &sda_temp_tolerance[1].dev_attr.attr, + &sda_temp_tolerance[2].dev_attr.attr, NULL }; @@ -925,6 +1184,20 @@ static const struct attribute_group w83791d_group = { .attrs = w83791d_attributes, }; +/* Separate group of attributes for fan/pwm 4-5. Their pins can also be + in use for GPIO in which case their sysfs-interface should not be made + available */ +static struct attribute *w83791d_attributes_fanpwm45[] = { + FAN_UNIT_ATTRS(3), + FAN_UNIT_ATTRS(4), + &sda_pwm[3].dev_attr.attr, + &sda_pwm[4].dev_attr.attr, + NULL +}; + +static const struct attribute_group w83791d_group_fanpwm45 = { + .attrs = w83791d_attributes_fanpwm45, +}; static int w83791d_detect_subclients(struct i2c_client *client) { @@ -1056,6 +1329,7 @@ static int w83791d_probe(struct i2c_client *client, struct w83791d_data *data; struct device *dev = &client->dev; int i, err; + u8 has_fanpwm45; #ifdef DEBUG int val1; @@ -1090,15 +1364,27 @@ static int w83791d_probe(struct i2c_client *client, if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group))) goto error3; + /* Check if pins of fan/pwm 4-5 are in use as GPIO */ + has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10; + if (has_fanpwm45) { + err = sysfs_create_group(&client->dev.kobj, + &w83791d_group_fanpwm45); + if (err) + goto error4; + } + /* Everything is ready, now register the working device */ data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto error4; + goto error5; } return 0; +error5: + if (has_fanpwm45) + sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45); error4: sysfs_remove_group(&client->dev.kobj, &w83791d_group); error3: @@ -1236,6 +1522,36 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) for (i = 0; i < 3; i++) data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04; + /* Update PWM duty cycle */ + for (i = 0; i < NUMBER_OF_PWM; i++) { + data->pwm[i] = w83791d_read(client, + W83791D_REG_PWM[i]); + } + + /* Update PWM enable status */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_FAN_CFG[i]); + } + data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03; + data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; + data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + + /* Update PWM target temperature */ + for (i = 0; i < 3; i++) { + data->temp_target[i] = w83791d_read(client, + W83791D_REG_TEMP_TARGET[i]) & 0x7f; + } + + /* Update PWM temperature tolerance */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_TEMP_TOL[i]); + } + data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f; + data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f; + data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, |