/* * fschmd.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring * Copyright (C) 2007 Hans de Goede * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * New merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes, * Scylla, Hercules and Heimdall chips * * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6 * (candidate) fschmd drivers: * Copyright (C) 2006 Thilo Cestonaro * Copyright (C) 2003, 2004 Reinhard Nissl * Copyright (C) 2000 Hermann Jung * Copyright (C) 1998, 1999 Frodo Looijaard * and Philip Edelbrock */ #include #include #include #include #include #include #include #include #include #include /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); /* * The FSCHMD registers and other defines */ /* chip identification */ #define FSCHMD_REG_IDENT_0 0x00 #define FSCHMD_REG_IDENT_1 0x01 #define FSCHMD_REG_IDENT_2 0x02 #define FSCHMD_REG_REVISION 0x03 /* global control and status */ #define FSCHMD_REG_EVENT_STATE 0x04 #define FSCHMD_REG_CONTROL 0x05 /* watchdog (support to be implemented) */ #define FSCHMD_REG_WDOG_PRESET 0x28 #define FSCHMD_REG_WDOG_STATE 0x23 #define FSCHMD_REG_WDOG_CONTROL 0x21 /* voltage supervision */ #define FSCHMD_REG_VOLT_12 0x45 #define FSCHMD_REG_VOLT_5 0x42 #define FSCHMD_REG_VOLT_BATT 0x48 /* minimum pwm at which the fan is driven (pwm can by increased depending on the temp. Notice that for the scy some fans share there minimum speed. Also notice that with the scy the sensor order is different then with the other chips, this order was in the 2.4 driver and kept for consistency. */ static const u8 FSCHMD_REG_FAN_MIN[5][6] = { { 0x55, 0x65 }, /* pos */ { 0x55, 0x65, 0xb5 }, /* her */ { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */ { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */ { 0x55, 0x65, 0xa5, 0xb5, 0xc5 } }; /* hmd */ /* actual fan speed */ static const u8 FSCHMD_REG_FAN_ACT[5][6] = { { 0x0e, 0x6b, 0xab }, /* pos */ { 0x0e, 0x6b, 0xbb }, /* her */ { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */ { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */ { 0x5b, 0x6b, 0xab, 0xbb, 0xcb } }; /* hmd */ /* fan status registers */ static const u8 FSCHMD_REG_FAN_STATE[5][6] = { { 0x0d, 0x62, 0xa2 }, /* pos */ { 0x0d, 0x62, 0xb2 }, /* her */ { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */ { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */ { 0x52, 0x62, 0xa2, 0xb2, 0xc2 } }; /* hmd */ /* fan ripple / divider registers */ static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = { { 0x0f, 0x6f, 0xaf }, /* pos */ { 0x0f, 0x6f, 0xbf }, /* her */ { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */ { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */ { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf } }; /* hmd */ static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 }; /* actual temperature registers */ static const u8 FSCHMD_REG_TEMP_ACT[5][5] = { { 0x64, 0x32, 0x35 }, /* pos */ { 0x64, 0x32, 0x35 }, /* her */ { 0x64, 0xD0, 0x32, 0x35 }, /* scy */ { 0x64, 0x32, 0x35 }, /* hrc */ { 0x70, 0x80, 0x90, 0xd0, 0xe0 } }; /* hmd */ /* temperature state registers */ static const u8 FSCHMD_REG_TEMP_STATE[5][5] = { { 0x71, 0x81, 0x91 }, /* pos */ { 0x71, 0x81, 0x91 }, /* her */ { 0x71, 0xd1, 0x81, 0x91 }, /* scy */ { 0x71, 0x81, 0x91 }, /* hrc */ { 0x71, 0x81, 0x91, 0xd1, 0xe1 } }; /*������hmd */ /* temperature high limit registers, FSC does not document these. Proven to be there with field testing on the fscher and fschrc, already supported / used in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers at these addresses, but doesn't want to confirm they are the same as with the fscher?? */ static const u8 FSCHMD_REG_TEMP_LIMIT[5][5] = { { 0, 0, 0 }, /* pos */ { 0x76, 0x86, 0x96 }, /* her */ { 0x76, 0xd6, 0x86, 0x96 }, /* scy */ { 0x76, 0x86, 0x96 }, /* hrc */ { 0x76, 0x86, 0x96, 0xd6, 0xe6 } }; /*������hmd */ /* These were found through experimenting with an fscher, currently they are not used, but we keep them around for future reference. static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 }; #define FSCHMD_NAME "fschmd" /* * Functions declarations */ static int fschmd_attach_adapter(struct i2c_adapter *adapter); static int fschmd_detach_client(struct i2c_client *client); static struct fschmd_data *fschmd_update_device(struct device *dev); /* * Driver data (common to all clients) */ static struct i2c_driver fschmd_driver = { .driver = { .name = FSCHMD_NAME, }, .attach_adapter = fschmd_attach_adapter, .detach_client = fschmd_detach_client, }; /* * Client data (each client gets its own) */ struct fschmd_data { enum chips kind; struct i2c_client client; struct class_device *class_dev; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* register values */ u8 global_control; /* global control register */ u8 volt[3]; /* 12, 5, battery voltage */ u8 temp_act[5]; /* temperature */ u8 temp_status[5]; /* status of sensor */ u8 temp_max[6]; /* high temp limit, notice: undocumented! */ u8 fan_act[6]; /* fans revolutions per second */ u8 fan_status[6]; /* fan status */ u8 fan_min[6]; /* fan min value for rps */ u8 fan_ripple[6]; /* divider for rps */ }; /* * Sysfs attr show / store functions */ static ssize_t show_in_value(struct device *dev, struct device_attribute *devattr, char *buf) { const int max_reading[3] = { 14200, 6600, 3300 }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); return sprintf(buf, "%d\n", (data->volt[attr->index] * max_reading[attr->index]) / 255); } #define TEMP_FROM_REG(val) (((val) - 128) * 1000) static ssize_t show_temp_value(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[attr->index])); } static ssize_t show_temp_max(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG( data->temp_max[attr->index])); } static ssize_t store_temp_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = i2c_get_clientdata(to_i2c_client(dev)); long v = simple_strtol(buf, NULL, 10) / 1000; SENSORS_LIMIT(v, -128, 127); v += 128; mutex_lock(&data->update_lock); i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_TEMP_LIMIT[data->kind][attr->index], v); data->temp_max[attr->index] = v; mutex_unlock(&data->update_lock); return count; } static ssize_t show_temp_fault(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); /* bit 0 set means sensor working ok, so no fault! */ if (data->temp_status[attr->index] & 0x01) return sprintf(buf, "0\n"); else return sprintf(buf, "1\n"); } static ssize_t show_temp_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); /* only signal an alarm if the sensor is working and alert == 1 */ if ((data->temp_status[attr->index] & 0x03) == 0x03) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); } #define RPM_FROM_REG(val) (val*60) static ssize_t show_fan_value(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[attr->index])); } static ssize_t show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); /* bits 2..7 reserved => mask with 3 */ return sprintf(buf, "%d\n", 1 << (data->fan_ripple[attr->index] & 3)); } static ssize_t store_fan_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); /* supported values: 2, 4, 8 */ unsigned long v = simple_strtoul(buf, NULL, 10); switch (v) { case 2: v = 1; break; case 4: v = 2; break; case 8: v = 3; break; default: dev_err(&data->client.dev, "fan_div value %lu not " "supported. Choose one of 2, 4 or 8!\n", v); return -EINVAL; } mutex_lock(&data->update_lock); /* bits 2..7 reserved => mask with 0x03 */ data->fan_ripple[attr->index] &= ~0x03; data->fan_ripple[attr->index] |= v; i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_FAN_RIPPLE[data->kind][attr->index], data->fan_ripple[attr->index]); mutex_unlock(&data->update_lock); return count; } static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = fschmd_update_device(dev); if (data->fan_status[attr->index] & 0x04) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); } static ssize_t show_pwm_auto_point1_pwm(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int val = fschmd_update_device(dev)->fan_min[attr->index]; /* 0 = allow turning off, 1-255 = 50-100% */ if (val) val = val / 2 + 128; return sprintf(buf, "%d\n", val); } static ssize_t store_pwm_auto_point1_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct fschmd_data *data = i2c_get_clientdata(to_i2c_client(dev)); unsigned long v = simple_strtoul(buf, NULL, 10); /* register: 0 = allow turning off, 1-255 = 50-100% */ if (v) { SENSORS_LIMIT(v, 128, 255); v = (v - 128) * 2 + 1; } mutex_lock(&data->update_lock); i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_FAN_MIN[data->kind][attr->index], v); data->fan_min[attr->index] = v; mutex_unlock(&data->update_lock); return count; } /* The FSC hwmon family has the ability to force an attached alert led to flash from software, we export this as an alert_led sysfs attr */ static ssize_t show_alert_led(struct device *dev, struct device_attribute *devattr, char *buf) { struct fschmd_data *data = fschmd_update_device(dev); if (data->global_control & 0x01) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); } static ssize_t store_alert_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct fschmd_data *data = fschmd_update_device(dev); unsigned long v = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); if (v) data->global_control |= 0x01; else data->global_control &= ~0x01; i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_CONTROL, v); mutex_unlock(&data->update_lock); return count; } static struct sensor_device_attribute fschmd_attr[] = { SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), SENSOR_ATTR(alert_led, 0644, show_alert_led, store_alert_led, 0), }; static struct sensor_device_attribute fschmd_temp_attr[] = { SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0), SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0), SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0), SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1), SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1), SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1), SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2), SENSOR_ATTR(temp3_max, 0644, show_temp_max, store_temp_max, 2), SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2), SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2), SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3), SENSOR_ATTR(temp4_max, 0644, show_temp_max, store_temp_max, 3), SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3), SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3), SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4), SENSOR_ATTR(temp5_max, 0644, show_temp_max, store_temp_max, 4), SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), }; static struct sensor_device_attribute fschmd_fan_attr[] = { SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0), SENSOR_ATTR(fan1_div, 0644, show_fan_div, store_fan_div, 0), SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0), SENSOR_ATTR(pwm1_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 0), SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1), SENSOR_ATTR(fan2_div, 0644, show_fan_div, store_fan_div, 1), SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1), SENSOR_ATTR(pwm2_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 1), SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2), SENSOR_ATTR(fan3_div, 0644, show_fan_div, store_fan_div, 2), SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2), SENSOR_ATTR(pwm3_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 2), SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3), SENSOR_ATTR(fan4_div, 0644, show_fan_div, store_fan_div, 3), SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3), SENSOR_ATTR(pwm4_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 3), SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4), SENSOR_ATTR(fan5_div, 0644, show_fan_div, store_fan_div, 4), SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4), SENSOR_ATTR(pwm5_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 4), SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5), SENSOR_ATTR(fan6_div, 0644, show_fan_div, store_fan_div, 5), SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5), SENSOR_ATTR(pwm6_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, store_pwm_auto_point1_pwm, 5), }; /* * Real code */ static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind) { struct i2c_client *new_client; struct fschmd_data *data; u8 revision; const char *names[4] = { "Scylla", "Poseidon", "Hermes", "Heimdall" }; int i, err = 0; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; /* 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 i2c_smbus_read_byte_data. */ if (!(data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL))) return -ENOMEM; /* The common I2C client data is placed right before the * Poseidon-specific data. */ new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &fschmd_driver; new_client->flags = 0; strlcpy(new_client->name, FSCHMD_NAME, I2C_NAME_SIZE); data->valid = 0; mutex_init(&data->update_lock); /* Detect & Identify the chip */ if (kind <= 0) { char id[4]; id[0] = i2c_smbus_read_byte_data(new_client, FSCHMD_REG_IDENT_0); id[1] = i2c_smbus_read_byte_data(new_client, FSCHMD_REG_IDENT_1); id[2] = i2c_smbus_read_byte_data(new_client, FSCHMD_REG_IDENT_2); id[3] = 0; if (!strcmp(id, "PEG")) { kind = fscpos; /* The Poseidon has hardwired temp limits, fill these in for the alarm resetting code */ data->temp_max[0] = 70 + 128; data->temp_max[1] = 50 + 128; data->temp_max[2] = 50 + 128; } else if (!strcmp(id, "HER")) kind = fscher; else if (!strcmp(id, "SCY")) kind = fscscy; else if (!strcmp(id, "HRC")) kind = fschrc; else if (!strcmp(id, "HMD")) kind = fschmd; else goto exit_free; } /* i2c kind goes from 1-4, we want from 0-3 to address arrays */ data->kind = kind - 1; /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) goto exit_free; for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) { err = device_create_file(&new_client->dev, &fschmd_attr[i].dev_attr); if (err) goto exit_remove_files; } for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) { /* Poseidon doesn't have TEMP_LIMIT registers */ if (kind == fscpos && (i % 4) == 1) continue; err = device_create_file(&new_client->dev, &fschmd_temp_attr[i].dev_attr); if (err) goto exit_remove_files; } for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 4); i++) { /* Poseidon doesn't have a FAN_MIN register for its 3th fan */ if (kind == fscpos && i == (2 * 4 + 3)) continue; err = device_create_file(&new_client->dev, &fschmd_fan_attr[i].dev_attr); if (err) goto exit_remove_files; } data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); goto exit_remove_files; } revision = i2c_smbus_read_byte_data(new_client, FSCHMD_REG_REVISION); printk(KERN_INFO "fschmd: Detected FSC %s chip, revision: %d\n", names[data->kind], (int) revision); return 0; exit_remove_files: for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) device_remove_file(&new_client->dev, &fschmd_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) device_remove_file(&new_client->dev, &fschmd_temp_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 4); i++) device_remove_file(&new_client->dev, &fschmd_fan_attr[i].dev_attr); i2c_detach_client(new_client); exit_free: kfree(data); return err; } static int fschmd_attach_adapter(struct i2c_adapter *adapter) { if (!(adapter->class & I2C_CLASS_HWMON)) return 0; return i2c_probe(adapter, &addr_data, fschmd_detect); } static int fschmd_detach_client(struct i2c_client *client) { struct fschmd_data *data = i2c_get_clientdata(client); int i, err; hwmon_device_unregister(data->class_dev); for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) device_remove_file(&client->dev, &fschmd_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) device_remove_file(&client->dev, &fschmd_temp_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 4); i++) device_remove_file(&client->dev, &fschmd_fan_attr[i].dev_attr); if ((err = i2c_detach_client(client))) return err; kfree(data); return 0; } static struct fschmd_data *fschmd_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct fschmd_data *data = i2c_get_clientdata(client); int i; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { for (i = 0; i < FSCHMD_NO_TEMP_SENSORS[data->kind]; i++) { data->temp_act[i] = i2c_smbus_read_byte_data(client, FSCHMD_REG_TEMP_ACT[data->kind][i]); data->temp_status[i] = i2c_smbus_read_byte_data(client, FSCHMD_REG_TEMP_STATE[data->kind][i]); /* The fscpos doesn't have TEMP_LIMIT registers */ if (FSCHMD_REG_TEMP_LIMIT[data->kind][i]) data->temp_max[i] = i2c_smbus_read_byte_data( client, FSCHMD_REG_TEMP_LIMIT[data->kind][i]); /* reset alarm if the alarm condition is gone, the chip doesn't do this itself */ if ((data->temp_status[i] & 0x02) && data->temp_act[i] < data->temp_max[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_TEMP_STATE[data->kind][i], 0x02); } for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) { data->fan_act[i] = i2c_smbus_read_byte_data(client, FSCHMD_REG_FAN_ACT[data->kind][i]); data->fan_status[i] = i2c_smbus_read_byte_data(client, FSCHMD_REG_FAN_STATE[data->kind][i]); data->fan_ripple[i] = i2c_smbus_read_byte_data(client, FSCHMD_REG_FAN_RIPPLE[data->kind][i]); /* The fscpos third fan doesn't have a fan_min */ if (FSCHMD_REG_FAN_MIN[data->kind][i]) data->fan_min[i] = i2c_smbus_read_byte_data( client, FSCHMD_REG_FAN_MIN[data->kind][i]); /* reset fan status if speed is back to > 0 */ if ((data->fan_status[i] & 0x04) && data->fan_act[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_FAN_STATE[data->kind][i], 0x04); } data->global_control = i2c_smbus_read_byte_data(client, FSCHMD_REG_CONTROL); data->volt[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_VOLT_12); data->volt[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_VOLT_5); data->volt[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_VOLT_BATT); /* To be implemented in the future data->watchdog[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_WDOG_PRESET); data->watchdog[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_WDOG_STATE); data->watchdog[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_WDOG_CONTROL); */ data->last_updated = jiffies; data->valid = 1; } mutex_unlock(&data->update_lock); return data; } static int __init fschmd_init(void) { return i2c_add_driver(&fschmd_driver); } static void __exit fschmd_exit(void) { i2c_del_driver(&fschmd_driver); } MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Hercules and Heimdall driver"); MODULE_LICENSE("GPL"); module_init(fschmd_init); module_exit(fschmd_exit);