diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-06 16:25:32 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-06 16:25:32 -0700 |
commit | b2267928115c563206ff006eb83e7935b7565eec (patch) | |
tree | 9bde67c65534c1711480385e9acaf67630be1ed9 /i2c | |
parent | 24422233277d7b2b940fa93e7374d881063a6cff (diff) | |
download | patches-b2267928115c563206ff006eb83e7935b7565eec.tar.gz |
i2c patches and remove kevent patch
Diffstat (limited to 'i2c')
-rw-r--r-- | i2c/hwmon-abituguru-fixes.patch | 416 | ||||
-rw-r--r-- | i2c/hwmon-abituguru-new-driver.patch | 1843 | ||||
-rw-r--r-- | i2c/hwmon-abituguru-nofans-detect-fix.patch | 39 | ||||
-rw-r--r-- | i2c/hwmon-hdaps-typo.patch | 30 | ||||
-rw-r--r-- | i2c/hwmon-maintenance-update.patch | 47 | ||||
-rw-r--r-- | i2c/hwmon-sysfs-interface-update-1.patch | 379 | ||||
-rw-r--r-- | i2c/hwmon-sysfs-interface-update-2.patch | 138 | ||||
-rw-r--r-- | i2c/hwmon-w83792d-add-data-lock.patch | 194 | ||||
-rw-r--r-- | i2c/hwmon-w83792d-pwm-set-fix.patch | 173 | ||||
-rw-r--r-- | i2c/i2c-Kconfig-suggest-N-for-rare-devices.patch | 88 | ||||
-rw-r--r-- | i2c/i2c-opencores-new-driver.patch | 479 |
11 files changed, 3826 insertions, 0 deletions
diff --git a/i2c/hwmon-abituguru-fixes.patch b/i2c/hwmon-abituguru-fixes.patch new file mode 100644 index 0000000000000..06680ba23b7f2 --- /dev/null +++ b/i2c/hwmon-abituguru-fixes.patch @@ -0,0 +1,416 @@ +From khali@linux-fr.org Sun Jun 4 11:23:04 2006 +Date: Sun, 4 Jun 2006 20:23:01 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Hans de Goede <j.w.r.degoede@hhs.nl> +Subject: abituguru: Review fixes +Message-Id: <20060604202301.df24f89f.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-abituguru-fixes.patch + +From: Hans de Goede <j.w.r.degoede@hhs.nl> + +Fixes to the Abit uGuru driver as requested in review by Jean Delvare: + - exactly calculate the sysfs_names array length using macro + - use snprintf when generating names to double check that the sysfs_names + array does not overflow. + - use ARRAY_SIZE and / or defines to determine number of loops in for loops + instead of using hardcoded values. + - In abituguru_probe(), refactor the error path leaving a single call to kfree + +Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/abituguru.c | 195 +++++++++++++++++++++++++--------------------- + 1 file changed, 110 insertions(+), 85 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/abituguru.c ++++ gregkh-2.6/drivers/hwmon/abituguru.c +@@ -36,6 +36,10 @@ + #define ABIT_UGURU_SENSOR_BANK1 0x21 /* 16x volt and temp */ + #define ABIT_UGURU_FAN_PWM 0x24 /* 3x 5 bytes */ + #define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */ ++/* max nr of sensors in bank1, a bank1 sensor can be in, temp or nc */ ++#define ABIT_UGURU_MAX_BANK1_SENSORS 16 ++/* Warning if you increase one of the 2 MAX defines below to 10 or higher you ++ should adjust the belonging _NAMES_LENGTH macro for the 2 digit number! */ + /* max nr of sensors in bank2, currently mb's with max 6 fans are known */ + #define ABIT_UGURU_MAX_BANK2_SENSORS 6 + /* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */ +@@ -74,10 +78,33 @@ + /* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */ + #define ABIT_UGURU_MAX_RETRIES 3 + #define ABIT_UGURU_RETRY_DELAY (HZ/5) +-/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is a error */ ++/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is an error */ + #define ABIT_UGURU_MAX_TIMEOUTS 2 ++/* utility macros */ ++#define ABIT_UGURU_NAME "abituguru" ++#define ABIT_UGURU_DEBUG(level, format, arg...) \ ++ if (level <= verbose) \ ++ printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg) ++/* Macros to help calculate the sysfs_names array length */ ++/* sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, ++ in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0 */ ++#define ABITUGURU_IN_NAMES_LENGTH (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14) ++/* sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0, ++ temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0 */ ++#define ABITUGURU_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16) ++/* sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0, ++ fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0 */ ++#define ABITUGURU_FAN_NAMES_LENGTH (11 + 9 + 11 + 18 + 10 + 14) ++/* sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0, ++ pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0 */ ++#define ABITUGURU_PWM_NAMES_LENGTH (12 + 24 + 2 * 21 + 2 * 22) ++/* IN_NAMES_LENGTH > TEMP_NAMES_LENGTH so assume all bank1 sensors are in */ ++#define ABITUGURU_SYSFS_NAMES_LENGTH ( \ ++ ABIT_UGURU_MAX_BANK1_SENSORS * ABITUGURU_IN_NAMES_LENGTH + \ ++ ABIT_UGURU_MAX_BANK2_SENSORS * ABITUGURU_FAN_NAMES_LENGTH + \ ++ ABIT_UGURU_MAX_PWMS * ABITUGURU_PWM_NAMES_LENGTH) + +-/* All the variables below are named identical to the oguru and oguru2 programs ++/* All the macros below are named identical to the oguru and oguru2 programs + reverse engineered by Olle Sandberg, hence the names might not be 100% + logical. I could come up with better names, but I prefer keeping the names + identical so that this driver can be compared with his work more easily. */ +@@ -93,11 +120,6 @@ + #define ABIT_UGURU_STATUS_READ 0x01 /* Ready to be read */ + #define ABIT_UGURU_STATUS_INPUT 0x08 /* More input */ + #define ABIT_UGURU_STATUS_READY 0x09 /* Ready to be written */ +-/* utility macros */ +-#define ABIT_UGURU_NAME "abituguru" +-#define ABIT_UGURU_DEBUG(level, format, arg...) \ +- if (level <= verbose) \ +- printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg) + + /* Constants */ + /* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */ +@@ -156,24 +178,23 @@ struct abituguru_data { + of a sensor is a volt or a temp sensor, for bank2 and the pwms its + easier todo things the same way. For in sensors we have 9 (temp 7) + sysfs entries per sensor, for bank2 and pwms 6. */ +- struct sensor_device_attribute_2 sysfs_attr[16 * 9 + ++ struct sensor_device_attribute_2 sysfs_attr[ ++ ABIT_UGURU_MAX_BANK1_SENSORS * 9 + + ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6]; +- /* Buffer to store the dynamically generated sysfs names, we need 2120 +- bytes for bank1 (worst case scenario of 16 in sensors), 444 bytes +- for fan1-6 and 738 bytes for pwm1-6 + some room to spare in case I +- miscounted :) */ +- char bank1_names[3400]; ++ /* Buffer to store the dynamically generated sysfs names */ ++ char sysfs_names[ABITUGURU_SYSFS_NAMES_LENGTH]; + + /* Bank 1 data */ +- u8 bank1_sensors[2]; /* number of [0] in, [1] temp sensors */ +- u8 bank1_address[2][16];/* addresses of [0] in, [1] temp sensors */ +- u8 bank1_value[16]; +- /* This array holds 16 x 3 entries for all the bank 1 sensor settings ++ /* number of and addresses of [0] in, [1] temp sensors */ ++ u8 bank1_sensors[2]; ++ u8 bank1_address[2][ABIT_UGURU_MAX_BANK1_SENSORS]; ++ u8 bank1_value[ABIT_UGURU_MAX_BANK1_SENSORS]; ++ /* This array holds 3 entries per sensor for the bank 1 sensor settings + (flags, min, max for voltage / flags, warn, shutdown for temp). */ +- u8 bank1_settings[16][3]; ++ u8 bank1_settings[ABIT_UGURU_MAX_BANK1_SENSORS][3]; + /* Maximum value for each sensor used for scaling in mV/millidegrees + Celsius. */ +- int bank1_max_value[16]; ++ int bank1_max_value[ABIT_UGURU_MAX_BANK1_SENSORS]; + + /* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */ + u8 bank2_sensors; /* actual number of bank2 sensors found */ +@@ -379,7 +400,7 @@ abituguru_detect_bank1_sensor_type(struc + /* First read the sensor and the current settings */ + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val, + 1, ABIT_UGURU_MAX_RETRIES) != 1) +- return -EIO; ++ return -ENODEV; + + /* Test val is sane / usable for sensor type detection. */ + if ((val < 10u) || (val > 240u)) { +@@ -401,7 +422,7 @@ abituguru_detect_bank1_sensor_type(struc + buf[2] = 250; + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, + buf, 3) != 3) +- return -EIO; ++ return -ENODEV; + /* Now we need 20 ms to give the uguru time to read the sensors + and raise a voltage alarm */ + set_current_state(TASK_UNINTERRUPTIBLE); +@@ -409,19 +430,19 @@ abituguru_detect_bank1_sensor_type(struc + /* Check for alarm and check the alarm is a volt low alarm. */ + if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) +- return -EIO; ++ return -ENODEV; + if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, + sensor_addr, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) +- return -EIO; ++ return -ENODEV; + if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) { + /* Restore original settings */ + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, + sensor_addr, + data->bank1_settings[sensor_addr], + 3) != 3) +- return -EIO; ++ return -ENODEV; + ABIT_UGURU_DEBUG(2, " found volt sensor\n"); + return ABIT_UGURU_IN_SENSOR; + } else +@@ -439,7 +460,7 @@ abituguru_detect_bank1_sensor_type(struc + buf[2] = 10; + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, + buf, 3) != 3) +- return -EIO; ++ return -ENODEV; + /* Now we need 50 ms to give the uguru time to read the sensors + and raise a temp alarm */ + set_current_state(TASK_UNINTERRUPTIBLE); +@@ -447,12 +468,12 @@ abituguru_detect_bank1_sensor_type(struc + /* Check for alarm and check the alarm is a temp high alarm. */ + if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) +- return -EIO; ++ return -ENODEV; + if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, + sensor_addr, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) +- return -EIO; ++ return -ENODEV; + if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) { + ret = ABIT_UGURU_TEMP_SENSOR; + ABIT_UGURU_DEBUG(2, " found temp sensor\n"); +@@ -466,7 +487,7 @@ abituguru_detect_bank1_sensor_type(struc + /* Restore original settings */ + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, + data->bank1_settings[sensor_addr], 3) != 3) +- return -EIO; ++ return -ENODEV; + + return ret; + } +@@ -1061,21 +1082,21 @@ static const struct sensor_device_attrib + store_pwm_setting, 4, 0), + }; + +-static const struct sensor_device_attribute_2 abituguru_sysfs_attr[] = { ++static struct sensor_device_attribute_2 abituguru_sysfs_attr[] = { + SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0), + }; + + static int __devinit abituguru_probe(struct platform_device *pdev) + { + struct abituguru_data *data; +- int i, j, res; ++ int i, j, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV; + char *sysfs_filename; +- int sysfs_attr_i = 0; + + /* El weirdo probe order, to keep the sysfs order identical to the + BIOS and window-appliction listing order. */ +- const u8 probe_order[16] = { 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, +- 0x02, 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C }; ++ const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = { ++ 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02, ++ 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C }; + + if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL))) + return -ENOMEM; +@@ -1092,24 +1113,18 @@ static int __devinit abituguru_probe(str + - testread / see if one really is there. + - make an in memory copy of all the uguru settings for future use. */ + if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, +- data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) { +- kfree(data); +- return -ENODEV; +- } ++ data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) ++ goto abituguru_probe_error; + +- for (i = 0; i < 16; i++) { ++ for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i, + &data->bank1_value[i], 1, +- ABIT_UGURU_MAX_RETRIES) != 1) { +- kfree(data); +- return -ENODEV; +- } ++ ABIT_UGURU_MAX_RETRIES) != 1) ++ goto abituguru_probe_error; + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i, + data->bank1_settings[i], 3, +- ABIT_UGURU_MAX_RETRIES) != 3) { +- kfree(data); +- return -ENODEV; +- } ++ ABIT_UGURU_MAX_RETRIES) != 3) ++ goto abituguru_probe_error; + } + /* Note: We don't know how many bank2 sensors / pwms there really are, + but in order to "detect" this we need to read the maximum amount +@@ -1119,48 +1134,45 @@ static int __devinit abituguru_probe(str + for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i, + &data->bank2_value[i], 1, +- ABIT_UGURU_MAX_RETRIES) != 1) { +- kfree(data); +- return -ENODEV; +- } ++ ABIT_UGURU_MAX_RETRIES) != 1) ++ goto abituguru_probe_error; + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i, + data->bank2_settings[i], 2, +- ABIT_UGURU_MAX_RETRIES) != 2) { +- kfree(data); +- return -ENODEV; +- } ++ ABIT_UGURU_MAX_RETRIES) != 2) ++ goto abituguru_probe_error; + } + for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) { + if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i, + data->pwm_settings[i], 5, +- ABIT_UGURU_MAX_RETRIES) != 5) { +- kfree(data); +- return -ENODEV; +- } ++ ABIT_UGURU_MAX_RETRIES) != 5) ++ goto abituguru_probe_error; + } + data->last_updated = jiffies; + + /* Detect sensor types and fill the sysfs attr for bank1 */ +- sysfs_filename = data->bank1_names; +- for (i = 0; i < 16; i++) { ++ sysfs_attr_i = 0; ++ sysfs_filename = data->sysfs_names; ++ sysfs_names_free = ABITUGURU_SYSFS_NAMES_LENGTH; ++ for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { + res = abituguru_detect_bank1_sensor_type(data, probe_order[i]); +- if (res < 0) { +- kfree(data); +- return -ENODEV; +- } ++ if (res < 0) ++ goto abituguru_probe_error; + if (res == ABIT_UGURU_NC) + continue; + ++ /* res 1 (temp) sensors have 7 sysfs entries, 0 (in) 9 */ + for (j = 0; j < (res ? 7 : 9); j++) { +- const char *name_templ = abituguru_sysfs_bank1_templ[ +- res][j].dev_attr.attr.name; ++ used = snprintf(sysfs_filename, sysfs_names_free, ++ abituguru_sysfs_bank1_templ[res][j].dev_attr. ++ attr.name, data->bank1_sensors[res] + res) ++ + 1; + data->sysfs_attr[sysfs_attr_i] = + abituguru_sysfs_bank1_templ[res][j]; + data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = + sysfs_filename; +- sysfs_filename += sprintf(sysfs_filename, name_templ, +- data->bank1_sensors[res] + res) + 1; + data->sysfs_attr[sysfs_attr_i].index = probe_order[i]; ++ sysfs_filename += used; ++ sysfs_names_free -= used; + sysfs_attr_i++; + } + data->bank1_max_value[probe_order[i]] = +@@ -1172,52 +1184,65 @@ static int __devinit abituguru_probe(str + /* Detect number of sensors and fill the sysfs attr for bank2 (fans) */ + abituguru_detect_no_bank2_sensors(data); + for (i = 0; i < data->bank2_sensors; i++) { +- for (j = 0; j < 6; j++) { +- const char *name_templ = abituguru_sysfs_fan_templ[j]. +- dev_attr.attr.name; ++ for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_fan_templ); j++) { ++ used = snprintf(sysfs_filename, sysfs_names_free, ++ abituguru_sysfs_fan_templ[j].dev_attr.attr.name, ++ i + 1) + 1; + data->sysfs_attr[sysfs_attr_i] = + abituguru_sysfs_fan_templ[j]; + data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = + sysfs_filename; +- sysfs_filename += sprintf(sysfs_filename, name_templ, +- i + 1) + 1; + data->sysfs_attr[sysfs_attr_i].index = i; ++ sysfs_filename += used; ++ sysfs_names_free -= used; + sysfs_attr_i++; + } + } + /* Detect number of sensors and fill the sysfs attr for pwms */ + abituguru_detect_no_pwms(data); + for (i = 0; i < data->pwms; i++) { +- for (j = 0; j < 6; j++) { +- const char *name_templ = abituguru_sysfs_pwm_templ[j]. +- dev_attr.attr.name; ++ for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_pwm_templ); j++) { ++ used = snprintf(sysfs_filename, sysfs_names_free, ++ abituguru_sysfs_pwm_templ[j].dev_attr.attr.name, ++ i + 1) + 1; + data->sysfs_attr[sysfs_attr_i] = + abituguru_sysfs_pwm_templ[j]; + data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = + sysfs_filename; +- sysfs_filename += sprintf(sysfs_filename, name_templ, +- i + 1) + 1; + data->sysfs_attr[sysfs_attr_i].index = i; ++ sysfs_filename += used; ++ sysfs_names_free -= used; + sysfs_attr_i++; + } + } +- /* Last add any "generic" entries to sysfs */ +- for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) { +- data->sysfs_attr[sysfs_attr_i] = abituguru_sysfs_attr[i]; +- sysfs_attr_i++; ++ /* Fail safe check, this should never happen! */ ++ if (sysfs_names_free < 0) { ++ printk(KERN_ERR ABIT_UGURU_NAME ": Fatal error ran out of " ++ "space for sysfs attr names. This should never " ++ "happen please report to the abituguru maintainer " ++ "(see MAINTAINERS)\n"); ++ res = -ENAMETOOLONG; ++ goto abituguru_probe_error; + } + printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n"); + + /* Register sysfs hooks */ + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { +- kfree(data); +- return PTR_ERR(data->class_dev); ++ res = PTR_ERR(data->class_dev); ++ goto abituguru_probe_error; + } + for (i = 0; i < sysfs_attr_i; i++) + device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); ++ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) ++ device_create_file(&pdev->dev, ++ &abituguru_sysfs_attr[i].dev_attr); + + return 0; ++ ++abituguru_probe_error: ++ kfree(data); ++ return res; + } + + static int __devexit abituguru_remove(struct platform_device *pdev) +@@ -1244,7 +1269,7 @@ static struct abituguru_data *abituguru_ + if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, + data->alarms, 3, 0)) != 3) + goto LEAVE_UPDATE; +- for (i = 0; i < 16; i++) { ++ for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { + if ((err = abituguru_read(data, + ABIT_UGURU_SENSOR_BANK1, i, + &data->bank1_value[i], 1, 0)) != 1) diff --git a/i2c/hwmon-abituguru-new-driver.patch b/i2c/hwmon-abituguru-new-driver.patch new file mode 100644 index 0000000000000..8abb085abb2f3 --- /dev/null +++ b/i2c/hwmon-abituguru-new-driver.patch @@ -0,0 +1,1843 @@ +From khali@linux-fr.org Sun Jun 4 11:22:28 2006 +Date: Sun, 4 Jun 2006 20:22:24 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Hans de Goede <j.w.r.degoede@hhs.nl> +Subject: abituguru: New hardware monitoring driver +Message-Id: <20060604202224.b38707bd.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-abituguru-new-driver.patch + +From: Hans de Goede <j.w.r.degoede@hhs.nl> + +New hardware monitoring driver for the Abit uGuru + +Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/hwmon/abituguru | 59 + + Documentation/hwmon/abituguru-datasheet | 312 +++++++ + MAINTAINERS | 6 + drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/abituguru.c | 1391 ++++++++++++++++++++++++++++++++ + 6 files changed, 1781 insertions(+) + +--- /dev/null ++++ gregkh-2.6/Documentation/hwmon/abituguru +@@ -0,0 +1,59 @@ ++Kernel driver abituguru ++======================= ++ ++Supported chips: ++ * Abit uGuru (Hardware Monitor part only) ++ Prefix: 'abituguru' ++ Addresses scanned: ISA 0x0E0 ++ Datasheet: Not available, this driver is based on reverse engineering. ++ A "Datasheet" has been written based on the reverse engineering it ++ should be available in the same dir as this file under the name ++ abituguru-datasheet. ++ ++Authors: ++ Hans de Goede <j.w.r.degoede@hhs.nl>, ++ (Initial reverse engineering done by Olle Sandberg ++ <ollebull@gmail.com>) ++ ++ ++Module Parameters ++----------------- ++ ++* force: bool Force detection. Note this parameter only causes the ++ detection to be skipped, if the uGuru can't be read ++ the module initialization (insmod) will still fail. ++* fan_sensors: int Tell the driver how many fan speed sensors there are ++ on your motherboard. Default: 0 (autodetect). ++* pwms: int Tell the driver how many fan speed controls (fan ++ pwms) your motherboard has. Default: 0 (autodetect). ++* verbose: int How verbose should the driver be? (0-3): ++ 0 normal output ++ 1 + verbose error reporting ++ 2 + sensors type probing info\n" ++ 3 + retryable error reporting ++ Default: 2 (the driver is still in the testing phase) ++ ++Notice if you need any of the first three options above please insmod the ++driver with verbose set to 3 and mail me <j.w.r.degoede@hhs.nl> the output of: ++dmesg | grep abituguru ++ ++ ++Description ++----------- ++ ++This driver supports the hardware monitoring features of the Abit uGuru chip ++found on Abit uGuru featuring motherboards (most modern Abit motherboards). ++ ++The uGuru chip in reality is a Winbond W83L950D in disguise (despite Abit ++claiming it is "a new microprocessor designed by the ABIT Engineers"). ++Unfortunatly this doesn't help since the W83L950D is a generic ++microcontroller with a custom Abit application running on it. ++ ++Despite Abit not releasing any information regarding the uGuru, Olle ++Sandberg <ollebull@gmail.com> has managed to reverse engineer the sensor part ++of the uGuru. Without his work this driver would not have been possible. ++ ++Known Issues ++------------ ++ ++The voltage and frequency control parts of the Abit uGuru are not supported. +--- /dev/null ++++ gregkh-2.6/Documentation/hwmon/abituguru-datasheet +@@ -0,0 +1,312 @@ ++uGuru datasheet ++=============== ++ ++First of all, what I know about uGuru is no fact based on any help, hints or ++datasheet from Abit. The data I have got on uGuru have I assembled through ++my weak knowledge in "backwards engineering". ++And just for the record, you may have noticed uGuru isn't a chip developed by ++Abit, as they claim it to be. It's realy just an microprocessor (uC) created by ++Winbond (W83L950D). And no, reading the manual for this specific uC or ++mailing Windbond for help won't give any usefull data about uGuru, as it is ++the program inside the uC that is responding to calls. ++ ++Olle Sandberg <ollebull@gmail.com>, 2005-05-25 ++ ++ ++Original version by Olle Sandberg who did the heavy lifting of the initial ++reverse engineering. This version has been almost fully rewritten for clarity ++and extended with write support and info on more databanks, the write support ++is once again reverse engineered by Olle the additional databanks have been ++reverse engineered by me. I would like to express my thanks to Olle, this ++document and the Linux driver could not have been written without his efforts. ++ ++Note: because of the lack of specs only the sensors part of the uGuru is ++described here and not the CPU / RAM / etc voltage & frequency control. ++ ++Hans de Goede <j.w.r.degoede@hhs.nl>, 28-01-2006 ++ ++ ++Detection ++========= ++ ++As far as known the uGuru is always placed at and using the (ISA) I/O-ports ++0xE0 and 0xE4, so we don't have to scan any port-range, just check what the two ++ports are holding for detection. We will refer to 0xE0 as CMD (command-port) ++and 0xE4 as DATA because Abit refers to them with these names. ++ ++If DATA holds 0x00 or 0x08 and CMD holds 0x00 or 0xAC an uGuru could be ++present. We have to check for two different values at data-port, because ++after a reboot uGuru will hold 0x00 here, but if the driver is removed and ++later on attached again data-port will hold 0x08, more about this later. ++ ++After wider testing of the Linux kernel driver some variants of the uGuru have ++turned up which will hold 0x00 instead of 0xAC at the CMD port, thus we also ++have to test CMD for two different values. On these uGuru's DATA will initally ++hold 0x09 and will only hold 0x08 after reading CMD first, so CMD must be read ++first! ++ ++To be really sure an uGuru is present a test read of one or more register ++sets should be done. ++ ++ ++Reading / Writing ++================= ++ ++Addressing ++---------- ++ ++The uGuru has a number of different addressing levels. The first addressing ++level we will call banks. A bank holds data for one or more sensors. The data ++in a bank for a sensor is one or more bytes large. ++ ++The number of bytes is fixed for a given bank, you should always read or write ++that many bytes, reading / writing more will fail, the results when writing ++less then the number of bytes for a given bank are undetermined. ++ ++See below for all known bank addresses, numbers of sensors in that bank, ++number of bytes data per sensor and contents/meaning of those bytes. ++ ++Although both this document and the kernel driver have kept the sensor ++terminoligy for the addressing within a bank this is not 100% correct, in ++bank 0x24 for example the addressing within the bank selects a PWM output not ++a sensor. ++ ++Notice that some banks have both a read and a write address this is how the ++uGuru determines if a read from or a write to the bank is taking place, thus ++when reading you should always use the read address and when writing the ++write address. The write address is always one (1) more then the read address. ++ ++ ++uGuru ready ++----------- ++ ++Before you can read from or write to the uGuru you must first put the uGuru ++in "ready" mode. ++ ++To put the uGuru in ready mode first write 0x00 to DATA and then wait for DATA ++to hold 0x09, DATA should read 0x09 within 250 read cycles. ++ ++Next CMD _must_ be read and should hold 0xAC, usually CMD will hold 0xAC the ++first read but sometimes it takes a while before CMD holds 0xAC and thus it ++has to be read a number of times (max 50). ++ ++After reading CMD, DATA should hold 0x08 which means that the uGuru is ready ++for input. As above DATA will usually hold 0x08 the first read but not always. ++This step can be skipped, but it is undetermined what happens if the uGuru has ++not yet reported 0x08 at DATA and you proceed with writing a bank address. ++ ++ ++Sending bank and sensor addresses to the uGuru ++---------------------------------------------- ++ ++First the uGuru must be in "ready" mode as described above, DATA should hold ++0x08 indicating that the uGuru wants input, in this case the bank address. ++ ++Next write the bank address to DATA. After the bank address has been written ++wait for to DATA to hold 0x08 again indicating that it wants / is ready for ++more input (max 250 reads). ++ ++Once DATA holds 0x08 again write the sensor address to CMD. ++ ++ ++Reading ++------- ++ ++First send the bank and sensor addresses as described above. ++Then for each byte of data you want to read wait for DATA to hold 0x01 ++which indicates that the uGuru is ready to be read (max 250 reads) and once ++DATA holds 0x01 read the byte from CMD. ++ ++Once all bytes have been read data will hold 0x09, but there is no reason to ++test for this. Notice that the number of bytes is bank address dependent see ++above and below. ++ ++After completing a successfull read it is advised to put the uGuru back in ++ready mode, so that it is ready for the next read / write cycle. This way ++if your program / driver is unloaded and later loaded again the detection ++algorithm described above will still work. ++ ++ ++ ++Writing ++------- ++ ++First send the bank and sensor addresses as described above. ++Then for each byte of data you want to write wait for DATA to hold 0x00 ++which indicates that the uGuru is ready to be written (max 250 reads) and ++once DATA holds 0x00 write the byte to CMD. ++ ++Once all bytes have been written wait for DATA to hold 0x01 (max 250 reads) ++don't ask why this is the way it is. ++ ++Once DATA holds 0x01 read CMD it should hold 0xAC now. ++ ++After completing a successfull write it is advised to put the uGuru back in ++ready mode, so that it is ready for the next read / write cycle. This way ++if your program / driver is unloaded and later loaded again the detection ++algorithm described above will still work. ++ ++ ++Gotchas ++------- ++ ++After wider testing of the Linux kernel driver some variants of the uGuru have ++turned up which do not hold 0x08 at DATA within 250 reads after writing the ++bank address. With these versions this happens quite frequent, using larger ++timeouts doesn't help, they just go offline for a second or 2, doing some ++internal callibration or whatever. Your code should be prepared to handle ++this and in case of no response in this specific case just goto sleep for a ++while and then retry. ++ ++ ++Address Map ++=========== ++ ++Bank 0x20 Alarms (R) ++-------------------- ++This bank contains 0 sensors, iow the sensor address is ignored (but must be ++written) just use 0. Bank 0x20 contains 3 bytes: ++ ++Byte 0: ++This byte holds the alarm flags for sensor 0-7 of Sensor Bank1, with bit 0 ++corresponding to sensor 0, 1 to 1, etc. ++ ++Byte 1: ++This byte holds the alarm flags for sensor 8-15 of Sensor Bank1, with bit 0 ++corresponding to sensor 8, 1 to 9, etc. ++ ++Byte 2: ++This byte holds the alarm flags for sensor 0-5 of Sensor Bank2, with bit 0 ++corresponding to sensor 0, 1 to 1, etc. ++ ++ ++Bank 0x21 Sensor Bank1 Values / Readings (R) ++-------------------------------------------- ++This bank contains 16 sensors, for each sensor it contains 1 byte. ++So far the following sensors are known to be available on all motherboards: ++Sensor 0 CPU temp ++Sensor 1 SYS temp ++Sensor 3 CPU core volt ++Sensor 4 DDR volt ++Sensor 10 DDR Vtt volt ++Sensor 15 PWM temp ++ ++Byte 0: ++This byte holds the reading from the sensor. Sensors in Bank1 can be both ++volt and temp sensors, this is motherboard specific. The uGuru however does ++seem to know (be programmed with) what kindoff sensor is attached see Sensor ++Bank1 Settings description. ++ ++Volt sensors use a linear scale, a reading 0 corresponds with 0 volt and a ++reading of 255 with 3494 mV. The sensors for higher voltages however are ++connected through a division circuit. The currently known division circuits ++in use result in ranges of: 0-4361mV, 0-6248mV or 0-14510mV. 3.3 volt sources ++use the 0-4361mV range, 5 volt the 0-6248mV and 12 volt the 0-14510mV . ++ ++Temp sensors also use a linear scale, a reading of 0 corresponds with 0 degree ++Celsius and a reading of 255 with a reading of 255 degrees Celsius. ++ ++ ++Bank 0x22 Sensor Bank1 Settings (R) ++Bank 0x23 Sensor Bank1 Settings (W) ++----------------------------------- ++ ++This bank contains 16 sensors, for each sensor it contains 3 bytes. Each ++set of 3 bytes contains the settings for the sensor with the same sensor ++address in Bank 0x21 . ++ ++Byte 0: ++Alarm behaviour for the selected sensor. A 1 enables the described behaviour. ++Bit 0: Give an alarm if measured temp is over the warning threshold (RW) * ++Bit 1: Give an alarm if measured volt is over the max threshold (RW) ** ++Bit 2: Give an alarm if measured volt is under the min threshold (RW) ** ++Bit 3: Beep if alarm (RW) ++Bit 4: 1 if alarm cause measured temp is over the warning threshold (R) ++Bit 5: 1 if alarm cause measured volt is over the max threshold (R) ++Bit 6: 1 if alarm cause measured volt is under the min threshold (R) ++Bit 7: Volt sensor: Shutdown if alarm persist for more then 4 seconds (RW) ++ Temp sensor: Shutdown if temp is over the shutdown threshold (RW) ++ ++* This bit is only honored/used by the uGuru if a temp sensor is connected ++** This bit is only honored/used by the uGuru if a volt sensor is connected ++Note with some trickery this can be used to find out what kinda sensor is ++detected see the Linux kernel driver for an example with many comments on ++how todo this. ++ ++Byte 1: ++Temp sensor: warning threshold (scale as bank 0x21) ++Volt sensor: min threshold (scale as bank 0x21) ++ ++Byte 2: ++Temp sensor: shutdown threshold (scale as bank 0x21) ++Volt sensor: max threshold (scale as bank 0x21) ++ ++ ++Bank 0x24 PWM outputs for FAN's (R) ++Bank 0x25 PWM outputs for FAN's (W) ++----------------------------------- ++ ++This bank contains 3 "sensors", for each sensor it contains 5 bytes. ++Sensor 0 usually controls the CPU fan ++Sensor 1 usually controls the NB (or chipset for single chip) fan ++Sensor 2 usually controls the System fan ++ ++Byte 0: ++Flag 0x80 to enable control, Fan runs at 100% when disabled. ++low nibble (temp)sensor address at bank 0x21 used for control. ++ ++Byte 1: ++0-255 = 0-12v (linear), specify voltage at which fan will rotate when under ++low threshold temp (specified in byte 3) ++ ++Byte 2: ++0-255 = 0-12v (linear), specify voltage at which fan will rotate when above ++high threshold temp (specified in byte 4) ++ ++Byte 3: ++Low threshold temp (scale as bank 0x21) ++ ++byte 4: ++High threshold temp (scale as bank 0x21) ++ ++ ++Bank 0x26 Sensors Bank2 Values / Readings (R) ++--------------------------------------------- ++ ++This bank contains 6 sensors (AFAIK), for each sensor it contains 1 byte. ++So far the following sensors are known to be available on all motherboards: ++Sensor 0: CPU fan speed ++Sensor 1: NB (or chipset for single chip) fan speed ++Sensor 2: SYS fan speed ++ ++Byte 0: ++This byte holds the reading from the sensor. 0-255 = 0-15300 (linear) ++ ++ ++Bank 0x27 Sensors Bank2 Settings (R) ++Bank 0x28 Sensors Bank2 Settings (W) ++------------------------------------ ++ ++This bank contains 6 sensors (AFAIK), for each sensor it contains 2 bytes. ++ ++Byte 0: ++Alarm behaviour for the selected sensor. A 1 enables the described behaviour. ++Bit 0: Give an alarm if measured rpm is under the min threshold (RW) ++Bit 3: Beep if alarm (RW) ++Bit 7: Shutdown if alarm persist for more then 4 seconds (RW) ++ ++Byte 1: ++min threshold (scale as bank 0x26) ++ ++ ++Warning for the adventerous ++=========================== ++ ++A word of caution to those who want to experiment and see if they can figure ++the voltage / clock programming out, I tried reading and only reading banks ++0-0x30 with the reading code used for the sensor banks (0x20-0x28) and this ++resulted in a _permanent_ reprogramming of the voltages, luckily I had the ++sensors part configured so that it would shutdown my system on any out of spec ++voltages which proprably safed my computer (after a reboot I managed to ++immediatly enter the bios and reload the defaults). This probably means that ++the read/write cycle for the non sensor part is different from the sensor part. +--- gregkh-2.6.orig/MAINTAINERS ++++ gregkh-2.6/MAINTAINERS +@@ -181,6 +181,12 @@ M: bcrl@kvack.org + L: linux-aio@kvack.org + S: Supported + ++ABIT UGURU HARDWARE MONITOR DRIVER ++P: Hans de Goede ++M: j.w.r.degoede@hhs.nl ++L: lm-sensors@lm-sensors.org ++S: Maintained ++ + ACENIC DRIVER + P: Jes Sorensen + M: jes@trained-monkey.org +--- gregkh-2.6.orig/drivers/hwmon/Kconfig ++++ gregkh-2.6/drivers/hwmon/Kconfig +@@ -27,6 +27,18 @@ config HWMON_VID + tristate + default n + ++config SENSORS_ABITUGURU ++ tristate "Abit uGuru" ++ depends on HWMON && EXPERIMENTAL ++ help ++ If you say yes here you get support for the Abit uGuru chips ++ sensor part. The voltage and frequency control parts of the Abit ++ uGuru are not supported. The Abit uGuru chip can be found on Abit ++ uGuru featuring motherboards (most modern Abit motherboards). ++ ++ This driver can also be built as a module. If so, the module ++ will be called abituguru. ++ + config SENSORS_ADM1021 + tristate "Analog Devices ADM1021 and compatibles" + depends on HWMON && I2C +--- gregkh-2.6.orig/drivers/hwmon/Makefile ++++ gregkh-2.6/drivers/hwmon/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_SENSORS_W83792D) += w83792d + obj-$(CONFIG_SENSORS_W83781D) += w83781d.o + obj-$(CONFIG_SENSORS_W83791D) += w83791d.o + ++obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o + obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o + obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o + obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o +--- /dev/null ++++ gregkh-2.6/drivers/hwmon/abituguru.c +@@ -0,0 +1,1391 @@ ++/* ++ abituguru.c Copyright (c) 2005-2006 Hans de Goede <j.w.r.degoede@hhs.nl> ++ ++ 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. ++*/ ++/* ++ This driver supports the sensor part of the custom Abit uGuru chip found ++ on Abit uGuru motherboards. Note: because of lack of specs the CPU / RAM / ++ etc voltage & frequency control is not supported! ++*/ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/jiffies.h> ++#include <linux/mutex.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/hwmon.h> ++#include <linux/hwmon-sysfs.h> ++#include <asm/io.h> ++ ++/* Banks */ ++#define ABIT_UGURU_ALARM_BANK 0x20 /* 1x 3 bytes */ ++#define ABIT_UGURU_SENSOR_BANK1 0x21 /* 16x volt and temp */ ++#define ABIT_UGURU_FAN_PWM 0x24 /* 3x 5 bytes */ ++#define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */ ++/* max nr of sensors in bank2, currently mb's with max 6 fans are known */ ++#define ABIT_UGURU_MAX_BANK2_SENSORS 6 ++/* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */ ++#define ABIT_UGURU_MAX_PWMS 5 ++/* uGuru sensor bank 1 flags */ /* Alarm if: */ ++#define ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE 0x01 /* temp over warn */ ++#define ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE 0x02 /* volt over max */ ++#define ABIT_UGURU_VOLT_LOW_ALARM_ENABLE 0x04 /* volt under min */ ++#define ABIT_UGURU_TEMP_HIGH_ALARM_FLAG 0x10 /* temp is over warn */ ++#define ABIT_UGURU_VOLT_HIGH_ALARM_FLAG 0x20 /* volt is over max */ ++#define ABIT_UGURU_VOLT_LOW_ALARM_FLAG 0x40 /* volt is under min */ ++/* uGuru sensor bank 2 flags */ /* Alarm if: */ ++#define ABIT_UGURU_FAN_LOW_ALARM_ENABLE 0x01 /* fan under min */ ++/* uGuru sensor bank common flags */ ++#define ABIT_UGURU_BEEP_ENABLE 0x08 /* beep if alarm */ ++#define ABIT_UGURU_SHUTDOWN_ENABLE 0x80 /* shutdown if alarm */ ++/* uGuru fan PWM (speed control) flags */ ++#define ABIT_UGURU_FAN_PWM_ENABLE 0x80 /* enable speed control */ ++/* Values used for conversion */ ++#define ABIT_UGURU_FAN_MAX 15300 /* RPM */ ++/* Bank1 sensor types */ ++#define ABIT_UGURU_IN_SENSOR 0 ++#define ABIT_UGURU_TEMP_SENSOR 1 ++#define ABIT_UGURU_NC 2 ++/* Timeouts / Retries, if these turn out to need a lot of fiddling we could ++ convert them to params. */ ++/* 250 was determined by trial and error, 200 works most of the time, but not ++ always. I assume this is cpu-speed independent, since the ISA-bus and not ++ the CPU should be the bottleneck. Note that 250 sometimes is still not ++ enough (only reported on AN7 mb) this is handled by a higher layer. */ ++#define ABIT_UGURU_WAIT_TIMEOUT 250 ++/* Normally all expected status in abituguru_ready, are reported after the ++ first read, but sometimes not and we need to poll, 5 polls was not enough ++ 50 sofar is. */ ++#define ABIT_UGURU_READY_TIMEOUT 50 ++/* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */ ++#define ABIT_UGURU_MAX_RETRIES 3 ++#define ABIT_UGURU_RETRY_DELAY (HZ/5) ++/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is a error */ ++#define ABIT_UGURU_MAX_TIMEOUTS 2 ++ ++/* All the variables below are named identical to the oguru and oguru2 programs ++ reverse engineered by Olle Sandberg, hence the names might not be 100% ++ logical. I could come up with better names, but I prefer keeping the names ++ identical so that this driver can be compared with his work more easily. */ ++/* Two i/o-ports are used by uGuru */ ++#define ABIT_UGURU_BASE 0x00E0 ++/* Used to tell uGuru what to read and to read the actual data */ ++#define ABIT_UGURU_CMD 0x00 ++/* Mostly used to check if uGuru is busy */ ++#define ABIT_UGURU_DATA 0x04 ++#define ABIT_UGURU_REGION_LENGTH 5 ++/* uGuru status' */ ++#define ABIT_UGURU_STATUS_WRITE 0x00 /* Ready to be written */ ++#define ABIT_UGURU_STATUS_READ 0x01 /* Ready to be read */ ++#define ABIT_UGURU_STATUS_INPUT 0x08 /* More input */ ++#define ABIT_UGURU_STATUS_READY 0x09 /* Ready to be written */ ++/* utility macros */ ++#define ABIT_UGURU_NAME "abituguru" ++#define ABIT_UGURU_DEBUG(level, format, arg...) \ ++ if (level <= verbose) \ ++ printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg) ++ ++/* Constants */ ++/* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */ ++static const int abituguru_bank1_max_value[2] = { 3494, 255000 }; ++/* Min / Max allowed values for sensor2 (fan) alarm threshold, these values ++ correspond to 300-3000 RPM */ ++static const u8 abituguru_bank2_min_threshold = 5; ++static const u8 abituguru_bank2_max_threshold = 50; ++/* Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4 ++ are temperature trip points. */ ++static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 }; ++/* Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a ++ special case the minium allowed pwm% setting for this is 30% (77) on ++ some MB's this special case is handled in the code! */ ++static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 }; ++static const u8 abituguru_pwm_max[5] = { 0, 255, 255, 75, 75 }; ++ ++ ++/* Insmod parameters */ ++static int force; ++module_param(force, bool, 0); ++MODULE_PARM_DESC(force, "Set to one to force detection."); ++static int fan_sensors; ++module_param(fan_sensors, int, 0); ++MODULE_PARM_DESC(fan_sensors, "Number of fan sensors on the uGuru " ++ "(0 = autodetect)"); ++static int pwms; ++module_param(pwms, int, 0); ++MODULE_PARM_DESC(pwms, "Number of PWMs on the uGuru " ++ "(0 = autodetect)"); ++ ++/* Default verbose is 2, since this driver is still in the testing phase */ ++static int verbose = 2; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n" ++ " 0 normal output\n" ++ " 1 + verbose error reporting\n" ++ " 2 + sensors type probing info\n" ++ " 3 + retryable error reporting"); ++ ++ ++/* For the Abit uGuru, we need to keep some data in memory. ++ The structure is dynamically allocated, at the same time when a new ++ abituguru device is allocated. */ ++struct abituguru_data { ++ struct class_device *class_dev; /* hwmon registered device */ ++ struct mutex update_lock; /* protect access to data and uGuru */ ++ unsigned long last_updated; /* In jiffies */ ++ unsigned short addr; /* uguru base address */ ++ char uguru_ready; /* is the uguru in ready state? */ ++ unsigned char update_timeouts; /* number of update timeouts since last ++ successful update */ ++ ++ /* The sysfs attr and their names are generated automatically, for bank1 ++ we cannot use a predefined array because we don't know beforehand ++ of a sensor is a volt or a temp sensor, for bank2 and the pwms its ++ easier todo things the same way. For in sensors we have 9 (temp 7) ++ sysfs entries per sensor, for bank2 and pwms 6. */ ++ struct sensor_device_attribute_2 sysfs_attr[16 * 9 + ++ ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6]; ++ /* Buffer to store the dynamically generated sysfs names, we need 2120 ++ bytes for bank1 (worst case scenario of 16 in sensors), 444 bytes ++ for fan1-6 and 738 bytes for pwm1-6 + some room to spare in case I ++ miscounted :) */ ++ char bank1_names[3400]; ++ ++ /* Bank 1 data */ ++ u8 bank1_sensors[2]; /* number of [0] in, [1] temp sensors */ ++ u8 bank1_address[2][16];/* addresses of [0] in, [1] temp sensors */ ++ u8 bank1_value[16]; ++ /* This array holds 16 x 3 entries for all the bank 1 sensor settings ++ (flags, min, max for voltage / flags, warn, shutdown for temp). */ ++ u8 bank1_settings[16][3]; ++ /* Maximum value for each sensor used for scaling in mV/millidegrees ++ Celsius. */ ++ int bank1_max_value[16]; ++ ++ /* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */ ++ u8 bank2_sensors; /* actual number of bank2 sensors found */ ++ u8 bank2_value[ABIT_UGURU_MAX_BANK2_SENSORS]; ++ u8 bank2_settings[ABIT_UGURU_MAX_BANK2_SENSORS][2]; /* flags, min */ ++ ++ /* Alarms 2 bytes for bank1, 1 byte for bank2 */ ++ u8 alarms[3]; ++ ++ /* Fan PWM (speed control) 5 bytes per PWM */ ++ u8 pwms; /* actual number of pwms found */ ++ u8 pwm_settings[ABIT_UGURU_MAX_PWMS][5]; ++}; ++ ++/* wait till the uguru is in the specified state */ ++static int abituguru_wait(struct abituguru_data *data, u8 state) ++{ ++ int timeout = ABIT_UGURU_WAIT_TIMEOUT; ++ ++ while (inb_p(data->addr + ABIT_UGURU_DATA) != state) { ++ timeout--; ++ if (timeout == 0) ++ return -EBUSY; ++ } ++ return 0; ++} ++ ++/* Put the uguru in ready for input state */ ++static int abituguru_ready(struct abituguru_data *data) ++{ ++ int timeout = ABIT_UGURU_READY_TIMEOUT; ++ ++ if (data->uguru_ready) ++ return 0; ++ ++ /* Reset? / Prepare for next read/write cycle */ ++ outb(0x00, data->addr + ABIT_UGURU_DATA); ++ ++ /* Wait till the uguru is ready */ ++ if (abituguru_wait(data, ABIT_UGURU_STATUS_READY)) { ++ ABIT_UGURU_DEBUG(1, ++ "timeout exceeded waiting for ready state\n"); ++ return -EIO; ++ } ++ ++ /* Cmd port MUST be read now and should contain 0xAC */ ++ while (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) { ++ timeout--; ++ if (timeout == 0) { ++ ABIT_UGURU_DEBUG(1, ++ "CMD reg does not hold 0xAC after ready command\n"); ++ return -EIO; ++ } ++ } ++ ++ /* After this the ABIT_UGURU_DATA port should contain ++ ABIT_UGURU_STATUS_INPUT */ ++ timeout = ABIT_UGURU_READY_TIMEOUT; ++ while (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) { ++ timeout--; ++ if (timeout == 0) { ++ ABIT_UGURU_DEBUG(1, ++ "state != more input after ready command\n"); ++ return -EIO; ++ } ++ } ++ ++ data->uguru_ready = 1; ++ return 0; ++} ++ ++/* Send the bank and then sensor address to the uGuru for the next read/write ++ cycle. This function gets called as the first part of a read/write by ++ abituguru_read and abituguru_write. This function should never be ++ called by any other function. */ ++static int abituguru_send_address(struct abituguru_data *data, ++ u8 bank_addr, u8 sensor_addr, int retries) ++{ ++ /* assume the caller does error handling itself if it has not requested ++ any retries, and thus be quiet. */ ++ int report_errors = retries; ++ ++ for (;;) { ++ /* Make sure the uguru is ready and then send the bank address, ++ after this the uguru is no longer "ready". */ ++ if (abituguru_ready(data) != 0) ++ return -EIO; ++ outb(bank_addr, data->addr + ABIT_UGURU_DATA); ++ data->uguru_ready = 0; ++ ++ /* Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again ++ and send the sensor addr */ ++ if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) { ++ if (retries) { ++ ABIT_UGURU_DEBUG(3, "timeout exceeded " ++ "waiting for more input state, %d " ++ "tries remaining\n", retries); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(ABIT_UGURU_RETRY_DELAY); ++ retries--; ++ continue; ++ } ++ if (report_errors) ++ ABIT_UGURU_DEBUG(1, "timeout exceeded " ++ "waiting for more input state " ++ "(bank: %d)\n", (int)bank_addr); ++ return -EBUSY; ++ } ++ outb(sensor_addr, data->addr + ABIT_UGURU_CMD); ++ return 0; ++ } ++} ++ ++/* Read count bytes from sensor sensor_addr in bank bank_addr and store the ++ result in buf, retry the send address part of the read retries times. */ ++static int abituguru_read(struct abituguru_data *data, ++ u8 bank_addr, u8 sensor_addr, u8 *buf, int count, int retries) ++{ ++ int i; ++ ++ /* Send the address */ ++ i = abituguru_send_address(data, bank_addr, sensor_addr, retries); ++ if (i) ++ return i; ++ ++ /* And read the data */ ++ for (i = 0; i < count; i++) { ++ if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) { ++ ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for " ++ "read state (bank: %d, sensor: %d)\n", ++ (int)bank_addr, (int)sensor_addr); ++ break; ++ } ++ buf[i] = inb(data->addr + ABIT_UGURU_CMD); ++ } ++ ++ /* Last put the chip back in ready state */ ++ abituguru_ready(data); ++ ++ return i; ++} ++ ++/* Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send ++ address part of the write is always retried ABIT_UGURU_MAX_RETRIES times. */ ++static int abituguru_write(struct abituguru_data *data, ++ u8 bank_addr, u8 sensor_addr, u8 *buf, int count) ++{ ++ int i; ++ ++ /* Send the address */ ++ i = abituguru_send_address(data, bank_addr, sensor_addr, ++ ABIT_UGURU_MAX_RETRIES); ++ if (i) ++ return i; ++ ++ /* And write the data */ ++ for (i = 0; i < count; i++) { ++ if (abituguru_wait(data, ABIT_UGURU_STATUS_WRITE)) { ++ ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for " ++ "write state (bank: %d, sensor: %d)\n", ++ (int)bank_addr, (int)sensor_addr); ++ break; ++ } ++ outb(buf[i], data->addr + ABIT_UGURU_CMD); ++ } ++ ++ /* Now we need to wait till the chip is ready to be read again, ++ don't ask why */ ++ if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) { ++ ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for read state " ++ "after write (bank: %d, sensor: %d)\n", (int)bank_addr, ++ (int)sensor_addr); ++ return -EIO; ++ } ++ ++ /* Cmd port MUST be read now and should contain 0xAC */ ++ if (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) { ++ ABIT_UGURU_DEBUG(1, "CMD reg does not hold 0xAC after write " ++ "(bank: %d, sensor: %d)\n", (int)bank_addr, ++ (int)sensor_addr); ++ return -EIO; ++ } ++ ++ /* Last put the chip back in ready state */ ++ abituguru_ready(data); ++ ++ return i; ++} ++ ++/* Detect sensor type. Temp and Volt sensors are enabled with ++ different masks and will ignore enable masks not meant for them. ++ This enables us to test what kind of sensor we're dealing with. ++ By setting the alarm thresholds so that we will always get an ++ alarm for sensor type X and then enabling the sensor as sensor type ++ X, if we then get an alarm it is a sensor of type X. */ ++static int __devinit ++abituguru_detect_bank1_sensor_type(struct abituguru_data *data, ++ u8 sensor_addr) ++{ ++ u8 val, buf[3]; ++ int ret = ABIT_UGURU_NC; ++ ++ /* First read the sensor and the current settings */ ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val, ++ 1, ABIT_UGURU_MAX_RETRIES) != 1) ++ return -EIO; ++ ++ /* Test val is sane / usable for sensor type detection. */ ++ if ((val < 10u) || (val > 240u)) { ++ printk(KERN_WARNING ABIT_UGURU_NAME ++ ": bank1-sensor: %d reading (%d) too close to limits, " ++ "unable to determine sensor type, skipping sensor\n", ++ (int)sensor_addr, (int)val); ++ /* assume no sensor is there for sensors for which we can't ++ determine the sensor type because their reading is too close ++ to their limits, this usually means no sensor is there. */ ++ return ABIT_UGURU_NC; ++ } ++ ++ ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr); ++ /* Volt sensor test, enable volt low alarm, set min value ridicously ++ high. If its a volt sensor this should always give us an alarm. */ ++ buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE; ++ buf[1] = 245; ++ buf[2] = 250; ++ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, ++ buf, 3) != 3) ++ return -EIO; ++ /* Now we need 20 ms to give the uguru time to read the sensors ++ and raise a voltage alarm */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(HZ/50); ++ /* Check for alarm and check the alarm is a volt low alarm. */ ++ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3, ++ ABIT_UGURU_MAX_RETRIES) != 3) ++ return -EIO; ++ if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) { ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, ++ sensor_addr, buf, 3, ++ ABIT_UGURU_MAX_RETRIES) != 3) ++ return -EIO; ++ if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) { ++ /* Restore original settings */ ++ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, ++ sensor_addr, ++ data->bank1_settings[sensor_addr], ++ 3) != 3) ++ return -EIO; ++ ABIT_UGURU_DEBUG(2, " found volt sensor\n"); ++ return ABIT_UGURU_IN_SENSOR; ++ } else ++ ABIT_UGURU_DEBUG(2, " alarm raised during volt " ++ "sensor test, but volt low flag not set\n"); ++ } else ++ ABIT_UGURU_DEBUG(2, " alarm not raised during volt sensor " ++ "test\n"); ++ ++ /* Temp sensor test, enable sensor as a temp sensor, set beep value ++ ridicously low (but not too low, otherwise uguru ignores it). ++ If its a temp sensor this should always give us an alarm. */ ++ buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE; ++ buf[1] = 5; ++ buf[2] = 10; ++ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, ++ buf, 3) != 3) ++ return -EIO; ++ /* Now we need 50 ms to give the uguru time to read the sensors ++ and raise a temp alarm */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(HZ/20); ++ /* Check for alarm and check the alarm is a temp high alarm. */ ++ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3, ++ ABIT_UGURU_MAX_RETRIES) != 3) ++ return -EIO; ++ if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) { ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, ++ sensor_addr, buf, 3, ++ ABIT_UGURU_MAX_RETRIES) != 3) ++ return -EIO; ++ if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) { ++ ret = ABIT_UGURU_TEMP_SENSOR; ++ ABIT_UGURU_DEBUG(2, " found temp sensor\n"); ++ } else ++ ABIT_UGURU_DEBUG(2, " alarm raised during temp " ++ "sensor test, but temp high flag not set\n"); ++ } else ++ ABIT_UGURU_DEBUG(2, " alarm not raised during temp sensor " ++ "test\n"); ++ ++ /* Restore original settings */ ++ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, ++ data->bank1_settings[sensor_addr], 3) != 3) ++ return -EIO; ++ ++ return ret; ++} ++ ++/* These functions try to find out how many sensors there are in bank2 and how ++ many pwms there are. The purpose of this is to make sure that we don't give ++ the user the possibility to change settings for non-existent sensors / pwm. ++ The uGuru will happily read / write whatever memory happens to be after the ++ memory storing the PWM settings when reading/writing to a PWM which is not ++ there. Notice even if we detect a PWM which doesn't exist we normally won't ++ write to it, unless the user tries to change the settings. ++ ++ Although the uGuru allows reading (settings) from non existing bank2 ++ sensors, my version of the uGuru does seem to stop writing to them, the ++ write function above aborts in this case with: ++ "CMD reg does not hold 0xAC after write" ++ ++ Notice these 2 tests are non destructive iow read-only tests, otherwise ++ they would defeat their purpose. Although for the bank2_sensors detection a ++ read/write test would be feasible because of the reaction above, I've ++ however opted to stay on the safe side. */ ++static void __devinit ++abituguru_detect_no_bank2_sensors(struct abituguru_data *data) ++{ ++ int i; ++ ++ if (fan_sensors) { ++ data->bank2_sensors = fan_sensors; ++ ABIT_UGURU_DEBUG(2, "assuming %d fan sensors because of " ++ "\"fan_sensors\" module param\n", ++ (int)data->bank2_sensors); ++ return; ++ } ++ ++ ABIT_UGURU_DEBUG(2, "detecting number of fan sensors\n"); ++ for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { ++ /* 0x89 are the known used bits: ++ -0x80 enable shutdown ++ -0x08 enable beep ++ -0x01 enable alarm ++ All other bits should be 0, but on some motherboards ++ 0x40 (bit 6) is also high, at least for fan1 */ ++ if ((!i && (data->bank2_settings[i][0] & ~0xC9)) || ++ (i && (data->bank2_settings[i][0] & ~0x89))) { ++ ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " ++ "to be a fan sensor: settings[0] = %02X\n", ++ i, (unsigned int)data->bank2_settings[i][0]); ++ break; ++ } ++ ++ /* check if the threshold is within the allowed range */ ++ if (data->bank2_settings[i][1] < ++ abituguru_bank2_min_threshold) { ++ ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " ++ "to be a fan sensor: the threshold (%d) is " ++ "below the minimum (%d)\n", i, ++ (int)data->bank2_settings[i][1], ++ (int)abituguru_bank2_min_threshold); ++ break; ++ } ++ if (data->bank2_settings[i][1] > ++ abituguru_bank2_max_threshold) { ++ ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " ++ "to be a fan sensor: the threshold (%d) is " ++ "above the maximum (%d)\n", i, ++ (int)data->bank2_settings[i][1], ++ (int)abituguru_bank2_max_threshold); ++ break; ++ } ++ } ++ ++ data->bank2_sensors = i; ++ ABIT_UGURU_DEBUG(2, " found: %d fan sensors\n", ++ (int)data->bank2_sensors); ++} ++ ++static void __devinit ++abituguru_detect_no_pwms(struct abituguru_data *data) ++{ ++ int i, j; ++ ++ if (pwms) { ++ data->pwms = pwms; ++ ABIT_UGURU_DEBUG(2, "assuming %d PWM outputs because of " ++ "\"pwms\" module param\n", (int)data->pwms); ++ return; ++ } ++ ++ ABIT_UGURU_DEBUG(2, "detecting number of PWM outputs\n"); ++ for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) { ++ /* 0x80 is the enable bit and the low ++ nibble is which temp sensor to use, ++ the other bits should be 0 */ ++ if (data->pwm_settings[i][0] & ~0x8F) { ++ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " ++ "to be a pwm channel: settings[0] = %02X\n", ++ i, (unsigned int)data->pwm_settings[i][0]); ++ break; ++ } ++ ++ /* the low nibble must correspond to one of the temp sensors ++ we've found */ ++ for (j = 0; j < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; ++ j++) { ++ if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][j] == ++ (data->pwm_settings[i][0] & 0x0F)) ++ break; ++ } ++ if (j == data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) { ++ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " ++ "to be a pwm channel: %d is not a valid temp " ++ "sensor address\n", i, ++ data->pwm_settings[i][0] & 0x0F); ++ break; ++ } ++ ++ /* check if all other settings are within the allowed range */ ++ for (j = 1; j < 5; j++) { ++ u8 min; ++ /* special case pwm1 min pwm% */ ++ if ((i == 0) && ((j == 1) || (j == 2))) ++ min = 77; ++ else ++ min = abituguru_pwm_min[j]; ++ if (data->pwm_settings[i][j] < min) { ++ ABIT_UGURU_DEBUG(2, " pwm channel %d does " ++ "not seem to be a pwm channel: " ++ "setting %d (%d) is below the minimum " ++ "value (%d)\n", i, j, ++ (int)data->pwm_settings[i][j], ++ (int)min); ++ goto abituguru_detect_no_pwms_exit; ++ } ++ if (data->pwm_settings[i][j] > abituguru_pwm_max[j]) { ++ ABIT_UGURU_DEBUG(2, " pwm channel %d does " ++ "not seem to be a pwm channel: " ++ "setting %d (%d) is above the maximum " ++ "value (%d)\n", i, j, ++ (int)data->pwm_settings[i][j], ++ (int)abituguru_pwm_max[j]); ++ goto abituguru_detect_no_pwms_exit; ++ } ++ } ++ ++ /* check that min temp < max temp and min pwm < max pwm */ ++ if (data->pwm_settings[i][1] >= data->pwm_settings[i][2]) { ++ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " ++ "to be a pwm channel: min pwm (%d) >= " ++ "max pwm (%d)\n", i, ++ (int)data->pwm_settings[i][1], ++ (int)data->pwm_settings[i][2]); ++ break; ++ } ++ if (data->pwm_settings[i][3] >= data->pwm_settings[i][4]) { ++ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " ++ "to be a pwm channel: min temp (%d) >= " ++ "max temp (%d)\n", i, ++ (int)data->pwm_settings[i][3], ++ (int)data->pwm_settings[i][4]); ++ break; ++ } ++ } ++ ++abituguru_detect_no_pwms_exit: ++ data->pwms = i; ++ ABIT_UGURU_DEBUG(2, " found: %d PWM outputs\n", (int)data->pwms); ++} ++ ++/* Following are the sysfs callback functions. These functions expect: ++ sensor_device_attribute_2->index: sensor address/offset in the bank ++ sensor_device_attribute_2->nr: register offset, bitmask or NA. */ ++static struct abituguru_data *abituguru_update_device(struct device *dev); ++ ++static ssize_t show_bank1_value(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = abituguru_update_device(dev); ++ if (!data) ++ return -EIO; ++ return sprintf(buf, "%d\n", (data->bank1_value[attr->index] * ++ data->bank1_max_value[attr->index] + 128) / 255); ++} ++ ++static ssize_t show_bank1_setting(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ return sprintf(buf, "%d\n", ++ (data->bank1_settings[attr->index][attr->nr] * ++ data->bank1_max_value[attr->index] + 128) / 255); ++} ++ ++static ssize_t show_bank2_value(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = abituguru_update_device(dev); ++ if (!data) ++ return -EIO; ++ return sprintf(buf, "%d\n", (data->bank2_value[attr->index] * ++ ABIT_UGURU_FAN_MAX + 128) / 255); ++} ++ ++static ssize_t show_bank2_setting(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ return sprintf(buf, "%d\n", ++ (data->bank2_settings[attr->index][attr->nr] * ++ ABIT_UGURU_FAN_MAX + 128) / 255); ++} ++ ++static ssize_t store_bank1_setting(struct device *dev, struct device_attribute ++ *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ u8 val = (simple_strtoul(buf, NULL, 10) * 255 + ++ data->bank1_max_value[attr->index]/2) / ++ data->bank1_max_value[attr->index]; ++ ssize_t ret = count; ++ ++ mutex_lock(&data->update_lock); ++ if (data->bank1_settings[attr->index][attr->nr] != val) { ++ u8 orig_val = data->bank1_settings[attr->index][attr->nr]; ++ data->bank1_settings[attr->index][attr->nr] = val; ++ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, ++ attr->index, data->bank1_settings[attr->index], ++ 3) <= attr->nr) { ++ data->bank1_settings[attr->index][attr->nr] = orig_val; ++ ret = -EIO; ++ } ++ } ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++static ssize_t store_bank2_setting(struct device *dev, struct device_attribute ++ *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ u8 val = (simple_strtoul(buf, NULL, 10)*255 + ABIT_UGURU_FAN_MAX/2) / ++ ABIT_UGURU_FAN_MAX; ++ ssize_t ret = count; ++ ++ /* this check can be done before taking the lock */ ++ if ((val < abituguru_bank2_min_threshold) || ++ (val > abituguru_bank2_max_threshold)) ++ return -EINVAL; ++ ++ mutex_lock(&data->update_lock); ++ if (data->bank2_settings[attr->index][attr->nr] != val) { ++ u8 orig_val = data->bank2_settings[attr->index][attr->nr]; ++ data->bank2_settings[attr->index][attr->nr] = val; ++ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK2 + 2, ++ attr->index, data->bank2_settings[attr->index], ++ 2) <= attr->nr) { ++ data->bank2_settings[attr->index][attr->nr] = orig_val; ++ ret = -EIO; ++ } ++ } ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++static ssize_t show_bank1_alarm(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = abituguru_update_device(dev); ++ if (!data) ++ return -EIO; ++ /* See if the alarm bit for this sensor is set, and if the ++ alarm matches the type of alarm we're looking for (for volt ++ it can be either low or high). The type is stored in a few ++ readonly bits in the settings part of the relevant sensor. ++ The bitmask of the type is passed to us in attr->nr. */ ++ if ((data->alarms[attr->index / 8] & (0x01 << (attr->index % 8))) && ++ (data->bank1_settings[attr->index][0] & attr->nr)) ++ return sprintf(buf, "1\n"); ++ else ++ return sprintf(buf, "0\n"); ++} ++ ++static ssize_t show_bank2_alarm(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = abituguru_update_device(dev); ++ if (!data) ++ return -EIO; ++ if (data->alarms[2] & (0x01 << attr->index)) ++ return sprintf(buf, "1\n"); ++ else ++ return sprintf(buf, "0\n"); ++} ++ ++static ssize_t show_bank1_mask(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ if (data->bank1_settings[attr->index][0] & attr->nr) ++ return sprintf(buf, "1\n"); ++ else ++ return sprintf(buf, "0\n"); ++} ++ ++static ssize_t show_bank2_mask(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ if (data->bank2_settings[attr->index][0] & attr->nr) ++ return sprintf(buf, "1\n"); ++ else ++ return sprintf(buf, "0\n"); ++} ++ ++static ssize_t store_bank1_mask(struct device *dev, ++ struct device_attribute *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ int mask = simple_strtoul(buf, NULL, 10); ++ ssize_t ret = count; ++ u8 orig_val; ++ ++ mutex_lock(&data->update_lock); ++ orig_val = data->bank1_settings[attr->index][0]; ++ ++ if (mask) ++ data->bank1_settings[attr->index][0] |= attr->nr; ++ else ++ data->bank1_settings[attr->index][0] &= ~attr->nr; ++ ++ if ((data->bank1_settings[attr->index][0] != orig_val) && ++ (abituguru_write(data, ++ ABIT_UGURU_SENSOR_BANK1 + 2, attr->index, ++ data->bank1_settings[attr->index], 3) < 1)) { ++ data->bank1_settings[attr->index][0] = orig_val; ++ ret = -EIO; ++ } ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++static ssize_t store_bank2_mask(struct device *dev, ++ struct device_attribute *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ int mask = simple_strtoul(buf, NULL, 10); ++ ssize_t ret = count; ++ u8 orig_val; ++ ++ mutex_lock(&data->update_lock); ++ orig_val = data->bank2_settings[attr->index][0]; ++ ++ if (mask) ++ data->bank2_settings[attr->index][0] |= attr->nr; ++ else ++ data->bank2_settings[attr->index][0] &= ~attr->nr; ++ ++ if ((data->bank2_settings[attr->index][0] != orig_val) && ++ (abituguru_write(data, ++ ABIT_UGURU_SENSOR_BANK2 + 2, attr->index, ++ data->bank2_settings[attr->index], 2) < 1)) { ++ data->bank2_settings[attr->index][0] = orig_val; ++ ret = -EIO; ++ } ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++/* Fan PWM (speed control) */ ++static ssize_t show_pwm_setting(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ return sprintf(buf, "%d\n", data->pwm_settings[attr->index][attr->nr] * ++ abituguru_pwm_settings_multiplier[attr->nr]); ++} ++ ++static ssize_t store_pwm_setting(struct device *dev, struct device_attribute ++ *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ u8 min, val = (simple_strtoul(buf, NULL, 10) + ++ abituguru_pwm_settings_multiplier[attr->nr]/2) / ++ abituguru_pwm_settings_multiplier[attr->nr]; ++ ssize_t ret = count; ++ ++ /* special case pwm1 min pwm% */ ++ if ((attr->index == 0) && ((attr->nr == 1) || (attr->nr == 2))) ++ min = 77; ++ else ++ min = abituguru_pwm_min[attr->nr]; ++ ++ /* this check can be done before taking the lock */ ++ if ((val < min) || (val > abituguru_pwm_max[attr->nr])) ++ return -EINVAL; ++ ++ mutex_lock(&data->update_lock); ++ /* this check needs to be done after taking the lock */ ++ if ((attr->nr & 1) && ++ (val >= data->pwm_settings[attr->index][attr->nr + 1])) ++ ret = -EINVAL; ++ else if (!(attr->nr & 1) && ++ (val <= data->pwm_settings[attr->index][attr->nr - 1])) ++ ret = -EINVAL; ++ else if (data->pwm_settings[attr->index][attr->nr] != val) { ++ u8 orig_val = data->pwm_settings[attr->index][attr->nr]; ++ data->pwm_settings[attr->index][attr->nr] = val; ++ if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, ++ attr->index, data->pwm_settings[attr->index], ++ 5) <= attr->nr) { ++ data->pwm_settings[attr->index][attr->nr] = ++ orig_val; ++ ret = -EIO; ++ } ++ } ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++static ssize_t show_pwm_sensor(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ int i; ++ /* We need to walk to the temp sensor addresses to find what ++ the userspace id of the configured temp sensor is. */ ++ for (i = 0; i < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++) ++ if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] == ++ (data->pwm_settings[attr->index][0] & 0x0F)) ++ return sprintf(buf, "%d\n", i+1); ++ ++ return -ENXIO; ++} ++ ++static ssize_t store_pwm_sensor(struct device *dev, struct device_attribute ++ *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ unsigned long val = simple_strtoul(buf, NULL, 10) - 1; ++ ssize_t ret = count; ++ ++ mutex_lock(&data->update_lock); ++ if (val < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) { ++ u8 orig_val = data->pwm_settings[attr->index][0]; ++ u8 address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val]; ++ data->pwm_settings[attr->index][0] &= 0xF0; ++ data->pwm_settings[attr->index][0] |= address; ++ if (data->pwm_settings[attr->index][0] != orig_val) { ++ if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, ++ attr->index, ++ data->pwm_settings[attr->index], ++ 5) < 1) { ++ data->pwm_settings[attr->index][0] = orig_val; ++ ret = -EIO; ++ } ++ } ++ } ++ else ++ ret = -EINVAL; ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++static ssize_t show_pwm_enable(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ int res = 0; ++ if (data->pwm_settings[attr->index][0] & ABIT_UGURU_FAN_PWM_ENABLE) ++ res = 2; ++ return sprintf(buf, "%d\n", res); ++} ++ ++static ssize_t store_pwm_enable(struct device *dev, struct device_attribute ++ *devattr, const char *buf, size_t count) ++{ ++ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ u8 orig_val, user_val = simple_strtoul(buf, NULL, 10); ++ ssize_t ret = count; ++ ++ mutex_lock(&data->update_lock); ++ orig_val = data->pwm_settings[attr->index][0]; ++ switch (user_val) { ++ case 0: ++ data->pwm_settings[attr->index][0] &= ++ ~ABIT_UGURU_FAN_PWM_ENABLE; ++ break; ++ case 2: ++ data->pwm_settings[attr->index][0] |= ++ ABIT_UGURU_FAN_PWM_ENABLE; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if ((data->pwm_settings[attr->index][0] != orig_val) && ++ (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, ++ attr->index, data->pwm_settings[attr->index], ++ 5) < 1)) { ++ data->pwm_settings[attr->index][0] = orig_val; ++ ret = -EIO; ++ } ++ mutex_unlock(&data->update_lock); ++ return ret; ++} ++ ++static ssize_t show_name(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ return sprintf(buf, "%s\n", ABIT_UGURU_NAME); ++} ++ ++/* Sysfs attr templates, the real entries are generated automatically. */ ++static const ++struct sensor_device_attribute_2 abituguru_sysfs_bank1_templ[2][9] = { ++ { ++ SENSOR_ATTR_2(in%d_input, 0444, show_bank1_value, NULL, 0, 0), ++ SENSOR_ATTR_2(in%d_min, 0644, show_bank1_setting, ++ store_bank1_setting, 1, 0), ++ SENSOR_ATTR_2(in%d_min_alarm, 0444, show_bank1_alarm, NULL, ++ ABIT_UGURU_VOLT_LOW_ALARM_FLAG, 0), ++ SENSOR_ATTR_2(in%d_max, 0644, show_bank1_setting, ++ store_bank1_setting, 2, 0), ++ SENSOR_ATTR_2(in%d_max_alarm, 0444, show_bank1_alarm, NULL, ++ ABIT_UGURU_VOLT_HIGH_ALARM_FLAG, 0), ++ SENSOR_ATTR_2(in%d_beep, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0), ++ SENSOR_ATTR_2(in%d_shutdown, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0), ++ SENSOR_ATTR_2(in%d_min_alarm_enable, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, 0), ++ SENSOR_ATTR_2(in%d_max_alarm_enable, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE, 0), ++ }, { ++ SENSOR_ATTR_2(temp%d_input, 0444, show_bank1_value, NULL, 0, 0), ++ SENSOR_ATTR_2(temp%d_alarm, 0444, show_bank1_alarm, NULL, ++ ABIT_UGURU_TEMP_HIGH_ALARM_FLAG, 0), ++ SENSOR_ATTR_2(temp%d_max, 0644, show_bank1_setting, ++ store_bank1_setting, 1, 0), ++ SENSOR_ATTR_2(temp%d_crit, 0644, show_bank1_setting, ++ store_bank1_setting, 2, 0), ++ SENSOR_ATTR_2(temp%d_beep, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0), ++ SENSOR_ATTR_2(temp%d_shutdown, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0), ++ SENSOR_ATTR_2(temp%d_alarm_enable, 0644, show_bank1_mask, ++ store_bank1_mask, ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, 0), ++ } ++}; ++ ++static const struct sensor_device_attribute_2 abituguru_sysfs_fan_templ[6] = { ++ SENSOR_ATTR_2(fan%d_input, 0444, show_bank2_value, NULL, 0, 0), ++ SENSOR_ATTR_2(fan%d_alarm, 0444, show_bank2_alarm, NULL, 0, 0), ++ SENSOR_ATTR_2(fan%d_min, 0644, show_bank2_setting, ++ store_bank2_setting, 1, 0), ++ SENSOR_ATTR_2(fan%d_beep, 0644, show_bank2_mask, ++ store_bank2_mask, ABIT_UGURU_BEEP_ENABLE, 0), ++ SENSOR_ATTR_2(fan%d_shutdown, 0644, show_bank2_mask, ++ store_bank2_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0), ++ SENSOR_ATTR_2(fan%d_alarm_enable, 0644, show_bank2_mask, ++ store_bank2_mask, ABIT_UGURU_FAN_LOW_ALARM_ENABLE, 0), ++}; ++ ++static const struct sensor_device_attribute_2 abituguru_sysfs_pwm_templ[6] = { ++ SENSOR_ATTR_2(pwm%d_enable, 0644, show_pwm_enable, ++ store_pwm_enable, 0, 0), ++ SENSOR_ATTR_2(pwm%d_auto_channels_temp, 0644, show_pwm_sensor, ++ store_pwm_sensor, 0, 0), ++ SENSOR_ATTR_2(pwm%d_auto_point1_pwm, 0644, show_pwm_setting, ++ store_pwm_setting, 1, 0), ++ SENSOR_ATTR_2(pwm%d_auto_point2_pwm, 0644, show_pwm_setting, ++ store_pwm_setting, 2, 0), ++ SENSOR_ATTR_2(pwm%d_auto_point1_temp, 0644, show_pwm_setting, ++ store_pwm_setting, 3, 0), ++ SENSOR_ATTR_2(pwm%d_auto_point2_temp, 0644, show_pwm_setting, ++ store_pwm_setting, 4, 0), ++}; ++ ++static const struct sensor_device_attribute_2 abituguru_sysfs_attr[] = { ++ SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0), ++}; ++ ++static int __devinit abituguru_probe(struct platform_device *pdev) ++{ ++ struct abituguru_data *data; ++ int i, j, res; ++ char *sysfs_filename; ++ int sysfs_attr_i = 0; ++ ++ /* El weirdo probe order, to keep the sysfs order identical to the ++ BIOS and window-appliction listing order. */ ++ const u8 probe_order[16] = { 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, ++ 0x02, 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C }; ++ ++ if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; ++ mutex_init(&data->update_lock); ++ platform_set_drvdata(pdev, data); ++ ++ /* See if the uGuru is ready */ ++ if (inb_p(data->addr + ABIT_UGURU_DATA) == ABIT_UGURU_STATUS_INPUT) ++ data->uguru_ready = 1; ++ ++ /* Completely read the uGuru this has 2 purposes: ++ - testread / see if one really is there. ++ - make an in memory copy of all the uguru settings for future use. */ ++ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, ++ data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) { ++ kfree(data); ++ return -ENODEV; ++ } ++ ++ for (i = 0; i < 16; i++) { ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i, ++ &data->bank1_value[i], 1, ++ ABIT_UGURU_MAX_RETRIES) != 1) { ++ kfree(data); ++ return -ENODEV; ++ } ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i, ++ data->bank1_settings[i], 3, ++ ABIT_UGURU_MAX_RETRIES) != 3) { ++ kfree(data); ++ return -ENODEV; ++ } ++ } ++ /* Note: We don't know how many bank2 sensors / pwms there really are, ++ but in order to "detect" this we need to read the maximum amount ++ anyways. If we read sensors/pwms not there we'll just read crap ++ this can't hurt. We need the detection because we don't want ++ unwanted writes, which will hurt! */ ++ for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i, ++ &data->bank2_value[i], 1, ++ ABIT_UGURU_MAX_RETRIES) != 1) { ++ kfree(data); ++ return -ENODEV; ++ } ++ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i, ++ data->bank2_settings[i], 2, ++ ABIT_UGURU_MAX_RETRIES) != 2) { ++ kfree(data); ++ return -ENODEV; ++ } ++ } ++ for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) { ++ if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i, ++ data->pwm_settings[i], 5, ++ ABIT_UGURU_MAX_RETRIES) != 5) { ++ kfree(data); ++ return -ENODEV; ++ } ++ } ++ data->last_updated = jiffies; ++ ++ /* Detect sensor types and fill the sysfs attr for bank1 */ ++ sysfs_filename = data->bank1_names; ++ for (i = 0; i < 16; i++) { ++ res = abituguru_detect_bank1_sensor_type(data, probe_order[i]); ++ if (res < 0) { ++ kfree(data); ++ return -ENODEV; ++ } ++ if (res == ABIT_UGURU_NC) ++ continue; ++ ++ for (j = 0; j < (res ? 7 : 9); j++) { ++ const char *name_templ = abituguru_sysfs_bank1_templ[ ++ res][j].dev_attr.attr.name; ++ data->sysfs_attr[sysfs_attr_i] = ++ abituguru_sysfs_bank1_templ[res][j]; ++ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = ++ sysfs_filename; ++ sysfs_filename += sprintf(sysfs_filename, name_templ, ++ data->bank1_sensors[res] + res) + 1; ++ data->sysfs_attr[sysfs_attr_i].index = probe_order[i]; ++ sysfs_attr_i++; ++ } ++ data->bank1_max_value[probe_order[i]] = ++ abituguru_bank1_max_value[res]; ++ data->bank1_address[res][data->bank1_sensors[res]] = ++ probe_order[i]; ++ data->bank1_sensors[res]++; ++ } ++ /* Detect number of sensors and fill the sysfs attr for bank2 (fans) */ ++ abituguru_detect_no_bank2_sensors(data); ++ for (i = 0; i < data->bank2_sensors; i++) { ++ for (j = 0; j < 6; j++) { ++ const char *name_templ = abituguru_sysfs_fan_templ[j]. ++ dev_attr.attr.name; ++ data->sysfs_attr[sysfs_attr_i] = ++ abituguru_sysfs_fan_templ[j]; ++ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = ++ sysfs_filename; ++ sysfs_filename += sprintf(sysfs_filename, name_templ, ++ i + 1) + 1; ++ data->sysfs_attr[sysfs_attr_i].index = i; ++ sysfs_attr_i++; ++ } ++ } ++ /* Detect number of sensors and fill the sysfs attr for pwms */ ++ abituguru_detect_no_pwms(data); ++ for (i = 0; i < data->pwms; i++) { ++ for (j = 0; j < 6; j++) { ++ const char *name_templ = abituguru_sysfs_pwm_templ[j]. ++ dev_attr.attr.name; ++ data->sysfs_attr[sysfs_attr_i] = ++ abituguru_sysfs_pwm_templ[j]; ++ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = ++ sysfs_filename; ++ sysfs_filename += sprintf(sysfs_filename, name_templ, ++ i + 1) + 1; ++ data->sysfs_attr[sysfs_attr_i].index = i; ++ sysfs_attr_i++; ++ } ++ } ++ /* Last add any "generic" entries to sysfs */ ++ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) { ++ data->sysfs_attr[sysfs_attr_i] = abituguru_sysfs_attr[i]; ++ sysfs_attr_i++; ++ } ++ printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n"); ++ ++ /* Register sysfs hooks */ ++ data->class_dev = hwmon_device_register(&pdev->dev); ++ if (IS_ERR(data->class_dev)) { ++ kfree(data); ++ return PTR_ERR(data->class_dev); ++ } ++ for (i = 0; i < sysfs_attr_i; i++) ++ device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); ++ ++ return 0; ++} ++ ++static int __devexit abituguru_remove(struct platform_device *pdev) ++{ ++ struct abituguru_data *data = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ hwmon_device_unregister(data->class_dev); ++ kfree(data); ++ ++ return 0; ++} ++ ++static struct abituguru_data *abituguru_update_device(struct device *dev) ++{ ++ int i, err; ++ struct abituguru_data *data = dev_get_drvdata(dev); ++ /* fake a complete successful read if no update necessary. */ ++ char success = 1; ++ ++ mutex_lock(&data->update_lock); ++ if (time_after(jiffies, data->last_updated + HZ)) { ++ success = 0; ++ if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, ++ data->alarms, 3, 0)) != 3) ++ goto LEAVE_UPDATE; ++ for (i = 0; i < 16; i++) { ++ if ((err = abituguru_read(data, ++ ABIT_UGURU_SENSOR_BANK1, i, ++ &data->bank1_value[i], 1, 0)) != 1) ++ goto LEAVE_UPDATE; ++ if ((err = abituguru_read(data, ++ ABIT_UGURU_SENSOR_BANK1 + 1, i, ++ data->bank1_settings[i], 3, 0)) != 3) ++ goto LEAVE_UPDATE; ++ } ++ for (i = 0; i < data->bank2_sensors; i++) ++ if ((err = abituguru_read(data, ++ ABIT_UGURU_SENSOR_BANK2, i, ++ &data->bank2_value[i], 1, 0)) != 1) ++ goto LEAVE_UPDATE; ++ /* success! */ ++ success = 1; ++ data->update_timeouts = 0; ++LEAVE_UPDATE: ++ /* handle timeout condition */ ++ if (err == -EBUSY) { ++ /* No overflow please */ ++ if (data->update_timeouts < 255u) ++ data->update_timeouts++; ++ if (data->update_timeouts <= ABIT_UGURU_MAX_TIMEOUTS) { ++ ABIT_UGURU_DEBUG(3, "timeout exceeded, will " ++ "try again next update\n"); ++ /* Just a timeout, fake a successful read */ ++ success = 1; ++ } else ++ ABIT_UGURU_DEBUG(1, "timeout exceeded %d " ++ "times waiting for more input state\n", ++ (int)data->update_timeouts); ++ } ++ /* On success set last_updated */ ++ if (success) ++ data->last_updated = jiffies; ++ } ++ mutex_unlock(&data->update_lock); ++ ++ if (success) ++ return data; ++ else ++ return NULL; ++} ++ ++static struct platform_driver abituguru_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = ABIT_UGURU_NAME, ++ }, ++ .probe = abituguru_probe, ++ .remove = __devexit_p(abituguru_remove), ++}; ++ ++static int __init abituguru_detect(void) ++{ ++ /* See if there is an uguru there. After a reboot uGuru will hold 0x00 ++ at DATA and 0xAC, when this driver has already been loaded once ++ DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either ++ scenario but some will hold 0x00. ++ Some uGuru's initally hold 0x09 at DATA and will only hold 0x08 ++ after reading CMD first, so CMD must be read first! */ ++ u8 cmd_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD); ++ u8 data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA); ++ if (((data_val == 0x00) || (data_val == 0x08)) && ++ ((cmd_val == 0x00) || (cmd_val == 0xAC))) ++ return ABIT_UGURU_BASE; ++ ++ ABIT_UGURU_DEBUG(2, "no Abit uGuru found, data = 0x%02X, cmd = " ++ "0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val); ++ ++ if (force) { ++ printk(KERN_INFO ABIT_UGURU_NAME ": Assuming Abit uGuru is " ++ "present because of \"force\" parameter\n"); ++ return ABIT_UGURU_BASE; ++ } ++ ++ /* No uGuru found */ ++ return -ENODEV; ++} ++ ++static struct platform_device *abituguru_pdev; ++ ++static int __init abituguru_init(void) ++{ ++ int address, err; ++ struct resource res = { .flags = IORESOURCE_IO }; ++ ++ address = abituguru_detect(); ++ if (address < 0) ++ return address; ++ ++ err = platform_driver_register(&abituguru_driver); ++ if (err) ++ goto exit; ++ ++ abituguru_pdev = platform_device_alloc(ABIT_UGURU_NAME, address); ++ if (!abituguru_pdev) { ++ printk(KERN_ERR ABIT_UGURU_NAME ++ ": Device allocation failed\n"); ++ err = -ENOMEM; ++ goto exit_driver_unregister; ++ } ++ ++ res.start = address; ++ res.end = address + ABIT_UGURU_REGION_LENGTH - 1; ++ res.name = ABIT_UGURU_NAME; ++ ++ err = platform_device_add_resources(abituguru_pdev, &res, 1); ++ if (err) { ++ printk(KERN_ERR ABIT_UGURU_NAME ++ ": Device resource addition failed (%d)\n", err); ++ goto exit_device_put; ++ } ++ ++ err = platform_device_add(abituguru_pdev); ++ if (err) { ++ printk(KERN_ERR ABIT_UGURU_NAME ++ ": Device addition failed (%d)\n", err); ++ goto exit_device_put; ++ } ++ ++ return 0; ++ ++exit_device_put: ++ platform_device_put(abituguru_pdev); ++exit_driver_unregister: ++ platform_driver_unregister(&abituguru_driver); ++exit: ++ return err; ++} ++ ++static void __exit abituguru_exit(void) ++{ ++ platform_device_unregister(abituguru_pdev); ++ platform_driver_unregister(&abituguru_driver); ++} ++ ++MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); ++MODULE_DESCRIPTION("Abit uGuru Sensor device"); ++MODULE_LICENSE("GPL"); ++ ++module_init(abituguru_init); ++module_exit(abituguru_exit); diff --git a/i2c/hwmon-abituguru-nofans-detect-fix.patch b/i2c/hwmon-abituguru-nofans-detect-fix.patch new file mode 100644 index 0000000000000..0953a2fe07a45 --- /dev/null +++ b/i2c/hwmon-abituguru-nofans-detect-fix.patch @@ -0,0 +1,39 @@ +From khali@linux-fr.org Sun Jun 4 11:24:17 2006 +Date: Sun, 4 Jun 2006 20:24:11 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Hans de Goede <j.w.r.degoede@hhs.nl> +Subject: abituguru: Fix fan detection +Message-Id: <20060604202411.59d7dd3f.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-abituguru-nofans-detect-fix.patch + +From: Hans de Goede <j.w.r.degoede@hhs.nl> + +One of my testers had a problem where the driver only saw 2 of the 4 fan +sensors his uGuru has, this fixes this. + -accept 0x40 (bit 6) being high as a valid fan sensor setting for all fans + not just fan 1, I have a feeling this bit indicates whether or not a fan is + actually connected . + +Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/abituguru.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/abituguru.c ++++ gregkh-2.6/drivers/hwmon/abituguru.c +@@ -529,9 +529,8 @@ abituguru_detect_no_bank2_sensors(struct + -0x08 enable beep + -0x01 enable alarm + All other bits should be 0, but on some motherboards +- 0x40 (bit 6) is also high, at least for fan1 */ +- if ((!i && (data->bank2_settings[i][0] & ~0xC9)) || +- (i && (data->bank2_settings[i][0] & ~0x89))) { ++ 0x40 (bit 6) is also high for some of the fans?? */ ++ if (data->bank2_settings[i][0] & ~0xC9) { + ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " + "to be a fan sensor: settings[0] = %02X\n", + i, (unsigned int)data->bank2_settings[i][0]); diff --git a/i2c/hwmon-hdaps-typo.patch b/i2c/hwmon-hdaps-typo.patch new file mode 100644 index 0000000000000..1701ce413f112 --- /dev/null +++ b/i2c/hwmon-hdaps-typo.patch @@ -0,0 +1,30 @@ +From khali@linux-fr.org Sun Jun 4 11:10:57 2006 +Date: Sun, 4 Jun 2006 20:10:55 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Robert Love <rlove@rlove.org> +Subject: hwmon: Fix a typo in the hdaps driver +Message-Id: <20060604201055.93571684.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-hdaps-typo.patch + +Fix a typo in the hdaps driver. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Acked-by: Robert Love <rml@novell.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/hdaps.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- gregkh-2.6.orig/drivers/hwmon/hdaps.c ++++ gregkh-2.6/drivers/hwmon/hdaps.c +@@ -41,7 +41,7 @@ + #define HDAPS_PORT_STATE 0x1611 /* device state */ + #define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ + #define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ +-#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in celcius */ ++#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ + #define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ + #define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ + #define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ diff --git a/i2c/hwmon-maintenance-update.patch b/i2c/hwmon-maintenance-update.patch new file mode 100644 index 0000000000000..eb44784fd5572 --- /dev/null +++ b/i2c/hwmon-maintenance-update.patch @@ -0,0 +1,47 @@ +From khali@linux-fr.org Sun Jun 4 11:13:04 2006 +Date: Sun, 4 Jun 2006 20:13:01 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Subject: hwmon: Drop some maintainers entries +Message-Id: <20060604201301.0bc5da17.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-maintenance-update.patch + +I no more wish to be listed as the maintainer for the smsc47m1 and +w83l785ts drivers. I have no test device, and people will fallback +to me as the general hardware monitoring maintainer anyway. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + MAINTAINERS | 12 ------------ + 1 file changed, 12 deletions(-) + +--- gregkh-2.6.orig/MAINTAINERS ++++ gregkh-2.6/MAINTAINERS +@@ -2516,12 +2516,6 @@ M: thomas@winischhofer.net + W: http://www.winischhofer.at/linuxsisusbvga.shtml + S: Maintained + +-SMSC47M1 HARDWARE MONITOR DRIVER +-P: Jean Delvare +-M: khali@linux-fr.org +-L: lm-sensors@lm-sensors.org +-S: Odd Fixes +- + SMB FILESYSTEM + P: Urban Widmark + M: urban@teststation.com +@@ -3134,12 +3128,6 @@ L: wbsd-devel@list.drzeus.cx + W: http://projects.drzeus.cx/wbsd + S: Maintained + +-W83L785TS HARDWARE MONITOR DRIVER +-P: Jean Delvare +-M: khali@linux-fr.org +-L: lm-sensors@lm-sensors.org +-S: Odd Fixes +- + WATCHDOG DEVICE DRIVERS + P: Wim Van Sebroeck + M: wim@iguana.be diff --git a/i2c/hwmon-sysfs-interface-update-1.patch b/i2c/hwmon-sysfs-interface-update-1.patch new file mode 100644 index 0000000000000..8278021e4281e --- /dev/null +++ b/i2c/hwmon-sysfs-interface-update-1.patch @@ -0,0 +1,379 @@ +From khali@linux-fr.org Sun Jun 4 11:03:42 2006 +Date: Sun, 4 Jun 2006 20:03:39 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Rudolf Marek <r.marek@sh.cvut.cz> +Subject: hwmon: Sysfs interface documentation update, 1 of 2 +Message-Id: <20060604200339.4ba872f7.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-sysfs-interface-update-1.patch + +From: Rudolf Marek <r.marek@sh.cvut.cz> + +This patch cleans up hwmon sysfs documentation file, plus introduces +the description of DC/PWM selection for fan speed control. + +Signed-off-by: Rudolf Marek <r.marek@sh.cvut.cz> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/hwmon/sysfs-interface | 183 +++++++++++++++++++++--------------- + 1 file changed, 108 insertions(+), 75 deletions(-) + +--- gregkh-2.6.orig/Documentation/hwmon/sysfs-interface ++++ gregkh-2.6/Documentation/hwmon/sysfs-interface +@@ -69,28 +69,37 @@ to cause an alarm) is chip-dependent. + + ------------------------------------------------------------------------- + ++[0-*] denotes any positive number starting from 0 ++[1-*] denotes any positive number starting from 1 ++RO read only value ++RW read/write value ++ ++Read/write values may be read-only for some chips, depending on the ++hardware implementation. ++ + ************ + * Voltages * + ************ + +-in[0-8]_min Voltage min value. ++in[0-*]_min Voltage min value. + Unit: millivolt +- Read/Write ++ RW + +-in[0-8]_max Voltage max value. ++in[0-*]_max Voltage max value. + Unit: millivolt +- Read/Write ++ RW + +-in[0-8]_input Voltage input value. ++in[0-*]_input Voltage input value. + Unit: millivolt +- Read only ++ RO ++ Voltage measured on the chip pin. + Actual voltage depends on the scaling resistors on the + motherboard, as recommended in the chip datasheet. + This varies by chip and by motherboard. + Because of this variation, values are generally NOT scaled + by the chip driver, and must be done by the application. + However, some drivers (notably lm87 and via686a) +- do scale, with various degrees of success. ++ do scale, because of internal resistors built into a chip. + These drivers will output the actual voltage. + + Typical usage: +@@ -104,58 +113,72 @@ in[0-8]_input Voltage input value. + in7_* varies + in8_* varies + +-cpu[0-1]_vid CPU core reference voltage. ++cpu[0-*]_vid CPU core reference voltage. + Unit: millivolt +- Read only. ++ RO + Not always correct. + + vrm Voltage Regulator Module version number. +- Read only. +- Two digit number, first is major version, second is +- minor version. ++ RW (but changing it should no more be necessary) ++ Originally the VRM standard version multiplied by 10, but now ++ an arbitrary number, as not all standards have a version ++ number. + Affects the way the driver calculates the CPU core reference + voltage from the vid pins. + ++Also see the Alarms section for status flags associated with voltages. ++ + + ******** + * Fans * + ******** + +-fan[1-3]_min Fan minimum value ++fan[1-*]_min Fan minimum value + Unit: revolution/min (RPM) +- Read/Write. ++ RW + +-fan[1-3]_input Fan input value. ++fan[1-*]_input Fan input value. + Unit: revolution/min (RPM) +- Read only. ++ RO + +-fan[1-3]_div Fan divisor. ++fan[1-*]_div Fan divisor. + Integer value in powers of two (1, 2, 4, 8, 16, 32, 64, 128). ++ RW + Some chips only support values 1, 2, 4 and 8. + Note that this is actually an internal clock divisor, which + affects the measurable speed range, not the read value. + ++Also see the Alarms section for status flags associated with fans. ++ ++ + ******* + * PWM * + ******* + +-pwm[1-3] Pulse width modulation fan control. ++pwm[1-*] Pulse width modulation fan control. + Integer value in the range 0 to 255 +- Read/Write ++ RW + 255 is max or 100%. + +-pwm[1-3]_enable ++pwm[1-*]_enable + Switch PWM on and off. + Not always present even if fan*_pwm is. +- 0 to turn off +- 1 to turn on in manual mode +- 2 to turn on in automatic mode +- Read/Write ++ 0: turn off ++ 1: turn on in manual mode ++ 2+: turn on in automatic mode ++ Check individual chip documentation files for automatic mode details. ++ RW ++ ++pwm[1-*]_mode ++ 0: DC mode ++ 1: PWM mode ++ RW + + pwm[1-*]_auto_channels_temp + Select which temperature channels affect this PWM output in + auto mode. Bitfield, 1 is temp1, 2 is temp2, 4 is temp3 etc... + Which values are possible depend on the chip used. ++ RW + + pwm[1-*]_auto_point[1-*]_pwm + pwm[1-*]_auto_point[1-*]_temp +@@ -163,6 +186,7 @@ pwm[1-*]_auto_point[1-*]_temp_hyst + Define the PWM vs temperature curve. Number of trip points is + chip-dependent. Use this for chips which associate trip points + to PWM output channels. ++ RW + + OR + +@@ -172,51 +196,52 @@ temp[1-*]_auto_point[1-*]_temp_hyst + Define the PWM vs temperature curve. Number of trip points is + chip-dependent. Use this for chips which associate trip points + to temperature channels. ++ RW + + + **************** + * Temperatures * + **************** + +-temp[1-3]_type Sensor type selection. ++temp[1-*]_type Sensor type selection. + Integers 1 to 4 or thermistor Beta value (typically 3435) +- Read/Write. ++ RW + 1: PII/Celeron Diode + 2: 3904 transistor + 3: thermal diode + 4: thermistor (default/unknown Beta) + Not all types are supported by all chips + +-temp[1-4]_max Temperature max value. ++temp[1-*]_max Temperature max value. + Unit: millidegree Celcius +- Read/Write value. ++ RW + +-temp[1-3]_min Temperature min value. ++temp[1-*]_min Temperature min value. + Unit: millidegree Celcius +- Read/Write value. ++ RW + +-temp[1-3]_max_hyst ++temp[1-*]_max_hyst + Temperature hysteresis value for max limit. + Unit: millidegree Celcius + Must be reported as an absolute temperature, NOT a delta + from the max value. +- Read/Write value. ++ RW + +-temp[1-4]_input Temperature input value. ++temp[1-*]_input Temperature input value. + Unit: millidegree Celcius +- Read only value. ++ RO + +-temp[1-4]_crit Temperature critical value, typically greater than ++temp[1-*]_crit Temperature critical value, typically greater than + corresponding temp_max values. + Unit: millidegree Celcius +- Read/Write value. ++ RW + +-temp[1-2]_crit_hyst ++temp[1-*]_crit_hyst + Temperature hysteresis value for critical limit. + Unit: millidegree Celcius + Must be reported as an absolute temperature, NOT a delta + from the critical value. +- Read/Write value. ++ RW + + temp[1-4]_offset + Temperature offset which is added to the temperature reading +@@ -231,6 +256,8 @@ temp[1-4]_offset + itself, for example the thermal diode inside the CPU or + a thermistor nearby. + ++Also see the Alarms section for status flags associated with temperatures. ++ + + ************ + * Currents * +@@ -239,17 +266,17 @@ temp[1-4]_offset + Note that no known chip provides current measurements as of writing, + so this part is theoretical, so to say. + +-curr[1-n]_max Current max value ++curr[1-*]_max Current max value + Unit: milliampere +- Read/Write. ++ RW + +-curr[1-n]_min Current min value. ++curr[1-*]_min Current min value. + Unit: milliampere +- Read/Write. ++ RW + +-curr[1-n]_input Current input value ++curr[1-*]_input Current input value + Unit: milliampere +- Read only. ++ RO + + + ********** +@@ -263,50 +290,54 @@ Usually a given chip will either use cha + limit-related alarms, not both. The driver should just reflect the hardware + implementation. + +-in[0-n]_alarm +-fan[1-n]_alarm +-temp[1-n]_alarm ++in[0-*]_alarm ++fan[1-*]_alarm ++temp[1-*]_alarm + Channel alarm +- Boolean +- Read-only ++ 0: no alarm ++ 1: alarm ++ RO + + OR + +-in[0-n]_min_alarm +-in[0-n]_max_alarm +-fan[1-n]_min_alarm +-temp[1-n]_min_alarm +-temp[1-n]_max_alarm +-temp[1-n]_crit_alarm ++in[0-*]_min_alarm ++in[0-*]_max_alarm ++fan[1-*]_min_alarm ++temp[1-*]_min_alarm ++temp[1-*]_max_alarm ++temp[1-*]_crit_alarm + Limit alarm +- Boolean +- Read-only ++ 0: no alarm ++ 1: alarm ++ RO + + Each input channel may have an associated fault file. This can be used + to notify open diodes, unconnected fans etc. where the hardware + supports it. When this boolean has value 1, the measurement for that + channel should not be trusted. + +-in[0-n]_input_fault +-fan[1-n]_input_fault +-temp[1-n]_input_fault ++in[0-*]_input_fault ++fan[1-*]_input_fault ++temp[1-*]_input_fault + Input fault condition +- Boolean +- Read-only ++ 0: no fault occured ++ 1: fault condition ++ RO + + Some chips also offer the possibility to get beeped when an alarm occurs: + + beep_enable Master beep enable +- Boolean +- Read/Write +- +-in[0-n]_beep +-fan[1-n]_beep +-temp[1-n]_beep ++ 0: no beeps ++ 1: beeps ++ RW ++ ++in[0-*]_beep ++fan[1-*]_beep ++temp[1-*]_beep + Channel beep +- 0 to disable. +- 1 to enable. +- Read/write ++ 0: disable ++ 1: enable ++ RW + + In theory, a chip could provide per-limit beep masking, but no such chip + was seen so far. +@@ -316,7 +347,7 @@ beeps. These interface files are depreca + for compatibility reasons: + + alarms Alarm bitmask. +- Read only. ++ RO + Integer representation of one to four bytes. + A '1' bit means an alarm. + Chips should be programmed for 'comparator' mode so that +@@ -333,7 +364,7 @@ beep_mask Bitmask for beep. + Same format as 'alarms' with the same bit locations, + use discouraged for the same reason. Use individual + *_beep files instead. +- Read/Write ++ RW + + + ********* +@@ -341,7 +372,9 @@ beep_mask Bitmask for beep. + ********* + + eeprom Raw EEPROM data in binary form. +- Read only. ++ RO + + pec Enable or disable PEC (SMBus only) +- Read/Write ++ 0: disable ++ 1: enable ++ RW diff --git a/i2c/hwmon-sysfs-interface-update-2.patch b/i2c/hwmon-sysfs-interface-update-2.patch new file mode 100644 index 0000000000000..0853c07863332 --- /dev/null +++ b/i2c/hwmon-sysfs-interface-update-2.patch @@ -0,0 +1,138 @@ +From khali@linux-fr.org Mon Jun 5 11:31:25 2006 +Date: Mon, 5 Jun 2006 20:31:20 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Jim Cromie <jim.cromie@gmail.com> +Subject: hwmon: Sysfs interface documentation update, 2 of 2, take 2 +Message-Id: <20060605203120.b865347e.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-sysfs-interface-update-2.patch + +Reword and complete certain parts of the hwmon sysfs-interface +documentation file. Hopefully this will make things clearer for new +driver authors. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/hwmon/sysfs-interface | 45 +++++++++++++++++++++++------------- + 1 file changed, 29 insertions(+), 16 deletions(-) + +--- gregkh-2.6.orig/Documentation/hwmon/sysfs-interface ++++ gregkh-2.6/Documentation/hwmon/sysfs-interface +@@ -3,15 +3,15 @@ Naming and data format standards for sys + + The libsensors library offers an interface to the raw sensors data + through the sysfs interface. See libsensors documentation and source for +-more further information. As of writing this document, libsensors +-(from lm_sensors 2.8.3) is heavily chip-dependant. Adding or updating ++further information. As of writing this document, libsensors ++(from lm_sensors 2.8.3) is heavily chip-dependent. Adding or updating + support for any given chip requires modifying the library's code. + This is because libsensors was written for the procfs interface + older kernel modules were using, which wasn't standardized enough. + Recent versions of libsensors (from lm_sensors 2.8.2 and later) have + support for the sysfs interface, though. + +-The new sysfs interface was designed to be as chip-independant as ++The new sysfs interface was designed to be as chip-independent as + possible. + + Note that motherboards vary widely in the connections to sensor chips. +@@ -24,7 +24,7 @@ range using external resistors. Since th + can change from motherboard to motherboard, the conversions cannot be + hard coded into the driver and have to be done in user space. + +-For this reason, even if we aim at a chip-independant libsensors, it will ++For this reason, even if we aim at a chip-independent libsensors, it will + still require a configuration file (e.g. /etc/sensors.conf) for proper + values conversion, labeling of inputs and hiding of unused inputs. + +@@ -39,15 +39,16 @@ If you are developing a userspace applic + this standard. + + Note that this standard isn't completely established yet, so it is subject +-to changes, even important ones. One more reason to use the library instead +-of accessing sysfs files directly. ++to changes. If you are writing a new hardware monitoring driver those ++features can't seem to fit in this interface, please contact us with your ++extension proposal. Keep in mind that backward compatibility must be ++preserved. + + Each chip gets its own directory in the sysfs /sys/devices tree. To +-find all sensor chips, it is easier to follow the symlinks from +-/sys/i2c/devices/ ++find all sensor chips, it is easier to follow the device symlinks from ++/sys/class/hwmon/hwmon*. + +-All sysfs values are fixed point numbers. To get the true value of some +-of the values, you should divide by the specified value. ++All sysfs values are fixed point numbers. + + There is only one value per file, unlike the older /proc specification. + The common scheme for files naming is: <type><number>_<item>. Usual +@@ -77,6 +78,9 @@ RW read/write value + Read/write values may be read-only for some chips, depending on the + hardware implementation. + ++All entries are optional, and should only be created in a given driver ++if the chip has the feature. ++ + ************ + * Voltages * + ************ +@@ -213,32 +217,32 @@ temp[1-*]_type Sensor type selection. + Not all types are supported by all chips + + temp[1-*]_max Temperature max value. +- Unit: millidegree Celcius ++ Unit: millidegree Celsius (or millivolt, see below) + RW + + temp[1-*]_min Temperature min value. +- Unit: millidegree Celcius ++ Unit: millidegree Celsius + RW + + temp[1-*]_max_hyst + Temperature hysteresis value for max limit. +- Unit: millidegree Celcius ++ Unit: millidegree Celsius + Must be reported as an absolute temperature, NOT a delta + from the max value. + RW + + temp[1-*]_input Temperature input value. +- Unit: millidegree Celcius ++ Unit: millidegree Celsius + RO + + temp[1-*]_crit Temperature critical value, typically greater than + corresponding temp_max values. +- Unit: millidegree Celcius ++ Unit: millidegree Celsius + RW + + temp[1-*]_crit_hyst + Temperature hysteresis value for critical limit. +- Unit: millidegree Celcius ++ Unit: millidegree Celsius + Must be reported as an absolute temperature, NOT a delta + from the critical value. + RW +@@ -256,6 +260,15 @@ temp[1-4]_offset + itself, for example the thermal diode inside the CPU or + a thermistor nearby. + ++Some chips measure temperature using external thermistors and an ADC, and ++report the temperature measurement as a voltage. Converting this voltage ++back to a temperature (or the other way around for limits) requires ++mathematical functions not available in the kernel, so the conversion ++must occur in user space. For these chips, all temp* files described ++above should contain values expressed in millivolt instead of millidegree ++Celsius. In other words, such temperature channels are handled as voltage ++channels by the driver. ++ + Also see the Alarms section for status flags associated with temperatures. + + diff --git a/i2c/hwmon-w83792d-add-data-lock.patch b/i2c/hwmon-w83792d-add-data-lock.patch new file mode 100644 index 0000000000000..191f6c9144695 --- /dev/null +++ b/i2c/hwmon-w83792d-add-data-lock.patch @@ -0,0 +1,194 @@ +From khali@linux-fr.org Sun Jun 4 11:18:49 2006 +Date: Sun, 4 Jun 2006 20:18:45 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Yuan Mu <ymu@winbond.com.tw> +Subject: w83792d: Add missing data access locks +Message-Id: <20060604201845.12ea5bfe.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-w83792d-add-data-lock.patch + +From: Yuan Mu <ymu@winbond.com.tw> + +Add missing data lock in w83792d driver to avoid unexpected +data change. + +Signed-off-by: Yuan Mu <ymu@winbond.com.tw> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/w83792d.c | 30 +++++++++++++++++++++++++----- + 1 file changed, 25 insertions(+), 5 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/w83792d.c ++++ gregkh-2.6/drivers/hwmon/w83792d.c +@@ -372,8 +372,10 @@ static ssize_t store_in_##reg (struct de + u32 val; \ + \ + val = simple_strtoul(buf, NULL, 10); \ ++ mutex_lock(&data->update_lock); \ + data->in_##reg[nr] = SENSORS_LIMIT(IN_TO_REG(nr, val)/4, 0, 255); \ + w83792d_write_value(client, W83792D_REG_IN_##REG[nr], data->in_##reg[nr]); \ ++ mutex_unlock(&data->update_lock); \ + \ + return count; \ + } +@@ -440,9 +442,11 @@ store_fan_min(struct device *dev, struct + u32 val; + + val = simple_strtoul(buf, NULL, 10); ++ mutex_lock(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], + data->fan_min[nr]); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -475,6 +479,7 @@ store_fan_div(struct device *dev, struct + u8 tmp_fan_div; + + /* Save fan_min */ ++ mutex_lock(&data->update_lock); + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + +@@ -490,6 +495,7 @@ store_fan_div(struct device *dev, struct + /* Restore fan_min */ + data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -544,10 +550,11 @@ static ssize_t store_temp1(struct device + s32 val; + + val = simple_strtol(buf, NULL, 10); +- ++ mutex_lock(&data->update_lock); + data->temp1[nr] = TEMP1_TO_REG(val); + w83792d_write_value(client, W83792D_REG_TEMP1[nr], + data->temp1[nr]); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -577,13 +584,14 @@ static ssize_t store_temp23(struct devic + s32 val; + + val = simple_strtol(buf, NULL, 10); +- ++ mutex_lock(&data->update_lock); + data->temp_add[nr][index] = TEMP_ADD_TO_REG_HIGH(val); + data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val); + w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index], + data->temp_add[nr][index]); + w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1], + data->temp_add[nr][index+1]); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -682,6 +690,10 @@ store_pwmenable(struct device *dev, stru + u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp; + + val = simple_strtoul(buf, NULL, 10); ++ if (val < 1 || val > 3) ++ return -EINVAL; ++ ++ mutex_lock(&data->update_lock); + switch (val) { + case 1: + data->pwmenable[nr] = 0; /* manual mode */ +@@ -692,8 +704,6 @@ store_pwmenable(struct device *dev, stru + case 3: + data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */ + break; +- default: +- return -EINVAL; + } + cfg1_tmp = data->pwmenable[0]; + cfg2_tmp = (data->pwmenable[1]) << 2; +@@ -701,6 +711,7 @@ store_pwmenable(struct device *dev, stru + cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0; + fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp; + w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -794,12 +805,13 @@ store_chassis_clear(struct device *dev, + u8 temp1 = 0, temp2 = 0; + + val = simple_strtoul(buf, NULL, 10); +- ++ mutex_lock(&data->update_lock); + data->chassis_clear = SENSORS_LIMIT(val, 0 ,1); + temp1 = ((data->chassis_clear) << 7) & 0x80; + temp2 = w83792d_read_value(client, + W83792D_REG_CHASSIS_CLR) & 0x7f; + w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -832,10 +844,12 @@ store_thermal_cruise(struct device *dev, + val = simple_strtoul(buf, NULL, 10); + target_tmp = val; + target_tmp = target_tmp & 0x7f; ++ mutex_lock(&data->update_lock); + target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80; + data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255); + w83792d_write_value(client, W83792D_REG_THERMAL[nr], + (data->thermal_cruise[nr]) | target_mask); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -872,6 +886,7 @@ store_tolerance(struct device *dev, stru + u8 tol_tmp, tol_mask; + + val = simple_strtoul(buf, NULL, 10); ++ mutex_lock(&data->update_lock); + tol_mask = w83792d_read_value(client, + W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0); + tol_tmp = SENSORS_LIMIT(val, 0, 15); +@@ -882,6 +897,7 @@ store_tolerance(struct device *dev, stru + } + w83792d_write_value(client, W83792D_REG_TOLERANCE[nr], + tol_mask | tol_tmp); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -920,11 +936,13 @@ store_sf2_point(struct device *dev, stru + u8 mask_tmp = 0; + + val = simple_strtoul(buf, NULL, 10); ++ mutex_lock(&data->update_lock); + data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127); + mask_tmp = w83792d_read_value(client, + W83792D_REG_POINTS[index][nr]) & 0x80; + w83792d_write_value(client, W83792D_REG_POINTS[index][nr], + mask_tmp|data->sf2_points[index][nr]); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -984,6 +1002,7 @@ store_sf2_level(struct device *dev, stru + u8 mask_tmp=0, level_tmp=0; + + val = simple_strtoul(buf, NULL, 10); ++ mutex_lock(&data->update_lock); + data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15); + mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr]) + & ((nr==3) ? 0xf0 : 0x0f); +@@ -993,6 +1012,7 @@ store_sf2_level(struct device *dev, stru + level_tmp = data->sf2_levels[index][nr] << 4; + } + w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp); ++ mutex_unlock(&data->update_lock); + + return count; + } diff --git a/i2c/hwmon-w83792d-pwm-set-fix.patch b/i2c/hwmon-w83792d-pwm-set-fix.patch new file mode 100644 index 0000000000000..2f31e8478640c --- /dev/null +++ b/i2c/hwmon-w83792d-pwm-set-fix.patch @@ -0,0 +1,173 @@ +From khali@linux-fr.org Sun Jun 4 11:18:08 2006 +Date: Sun, 4 Jun 2006 20:18:05 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Yuan Mu <ymu@winbond.com.tw> +Subject: w83792d: Fix setting the PWM value +Message-Id: <20060604201805.49585bea.khali@linux-fr.org> +Content-Disposition: inline; filename=hwmon-w83792d-pwm-set-fix.patch + +From: Yuan Mu <ymu@winbond.com.tw> + +W83792D use pwm register low 4 bits to store PWM/DC value, bit 7 +is used to store fan PWM/DC mode. The store_pwm function did not +convert the pwm input correctly, so it may change the fan mode +when new value is set. + +This fix the problem. Change the "index" value of pwm*_mode +and pwm* SENSOR_ATTR to simplify code. + +Signed-off-by: Yuan Mu <ymu@winbond.com.tw> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hwmon/w83792d.c | 56 ++++++++++++++++++++++++------------------------ + 1 file changed, 29 insertions(+), 27 deletions(-) + +--- gregkh-2.6.orig/drivers/hwmon/w83792d.c ++++ gregkh-2.6/drivers/hwmon/w83792d.c +@@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div) + : (val)) / 1000, 0, 0xff)) + #define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00) + +-#define PWM_FROM_REG(val) (val) +-#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) + #define DIV_FROM_REG(val) (1 << (val)) + + static inline u8 +@@ -291,7 +289,6 @@ struct w83792d_data { + u8 pwm[7]; /* We only consider the first 3 set of pwm, + although 792 chip has 7 set of pwm. */ + u8 pwmenable[3]; +- u8 pwm_mode[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */ + u32 alarms; /* realtime status register encoding,combined */ + u8 chassis; /* Chassis status */ + u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */ +@@ -627,7 +624,7 @@ show_pwm(struct device *dev, struct devi + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83792d_data *data = w83792d_update_device(dev); +- return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr-1])); ++ return sprintf(buf, "%d\n", (data->pwm[nr] & 0x0f) << 4); + } + + static ssize_t +@@ -659,14 +656,16 @@ store_pwm(struct device *dev, struct dev + const char *buf, size_t count) + { + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); +- int nr = sensor_attr->index - 1; ++ int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83792d_data *data = i2c_get_clientdata(client); +- u32 val; ++ u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255) >> 4; + +- val = simple_strtoul(buf, NULL, 10); +- data->pwm[nr] = PWM_TO_REG(val); ++ mutex_lock(&data->update_lock); ++ val |= w83792d_read_value(client, W83792D_REG_PWM[nr]) & 0xf0; ++ data->pwm[nr] = val; + w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]); ++ mutex_unlock(&data->update_lock); + + return count; + } +@@ -707,9 +706,9 @@ store_pwmenable(struct device *dev, stru + } + + static struct sensor_device_attribute sda_pwm[] = { +- SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), +- SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), +- SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3), ++ 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), + }; + static struct sensor_device_attribute sda_pwm_enable[] = { + SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, +@@ -728,7 +727,7 @@ show_pwm_mode(struct device *dev, struct + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83792d_data *data = w83792d_update_device(dev); +- return sprintf(buf, "%d\n", data->pwm_mode[nr-1]); ++ return sprintf(buf, "%d\n", data->pwm[nr] >> 7); + } + + static ssize_t +@@ -736,29 +735,35 @@ store_pwm_mode(struct device *dev, struc + const char *buf, size_t count) + { + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); +- int nr = sensor_attr->index - 1; ++ int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83792d_data *data = i2c_get_clientdata(client); + u32 val; +- u8 pwm_mode_mask = 0; + + val = simple_strtoul(buf, NULL, 10); +- data->pwm_mode[nr] = SENSORS_LIMIT(val, 0, 1); +- pwm_mode_mask = w83792d_read_value(client, +- W83792D_REG_PWM[nr]) & 0x7f; +- w83792d_write_value(client, W83792D_REG_PWM[nr], +- ((data->pwm_mode[nr]) << 7) | pwm_mode_mask); ++ if (val != 0 && val != 1) ++ return -EINVAL; ++ ++ mutex_lock(&data->update_lock); ++ data->pwm[nr] = w83792d_read_value(client, W83792D_REG_PWM[nr]); ++ if (val) { /* PWM mode */ ++ data->pwm[nr] |= 0x80; ++ } else { /* DC mode */ ++ data->pwm[nr] &= 0x7f; ++ } ++ w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]); ++ mutex_unlock(&data->update_lock); + + return count; + } + + static struct sensor_device_attribute sda_pwm_mode[] = { + SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, +- show_pwm_mode, store_pwm_mode, 1), ++ show_pwm_mode, store_pwm_mode, 0), + SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, +- show_pwm_mode, store_pwm_mode, 2), ++ show_pwm_mode, store_pwm_mode, 1), + SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, +- show_pwm_mode, store_pwm_mode, 3), ++ show_pwm_mode, store_pwm_mode, 2), + }; + + +@@ -1373,7 +1378,7 @@ static struct w83792d_data *w83792d_upda + struct i2c_client *client = to_i2c_client(dev); + struct w83792d_data *data = i2c_get_clientdata(client); + int i, j; +- u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp; ++ u8 reg_array_tmp[4], reg_tmp; + + mutex_lock(&data->update_lock); + +@@ -1402,10 +1407,8 @@ static struct w83792d_data *w83792d_upda + data->fan_min[i] = w83792d_read_value(client, + W83792D_REG_FAN_MIN[i]); + /* Update the PWM/DC Value and PWM/DC flag */ +- pwm_array_tmp[i] = w83792d_read_value(client, ++ data->pwm[i] = w83792d_read_value(client, + W83792D_REG_PWM[i]); +- data->pwm[i] = pwm_array_tmp[i] & 0x0f; +- data->pwm_mode[i] = pwm_array_tmp[i] >> 7; + } + + reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG); +@@ -1513,7 +1516,6 @@ static void w83792d_print_debug(struct w + dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]); + dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]); + dev_dbg(dev, "pwm[%d] is: 0x%x\n", i, data->pwm[i]); +- dev_dbg(dev, "pwm_mode[%d] is: 0x%x\n", i, data->pwm_mode[i]); + } + dev_dbg(dev, "3 set of Temperatures: =====>\n"); + for (i=0; i<3; i++) { diff --git a/i2c/i2c-Kconfig-suggest-N-for-rare-devices.patch b/i2c/i2c-Kconfig-suggest-N-for-rare-devices.patch new file mode 100644 index 0000000000000..b129beaf081fb --- /dev/null +++ b/i2c/i2c-Kconfig-suggest-N-for-rare-devices.patch @@ -0,0 +1,88 @@ +From khali@linux-fr.org Sun Jun 4 11:00:01 2006 +Date: Sun, 4 Jun 2006 19:59:57 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Subject: i2c: Suggest N for rare devices in Kconfig +Message-Id: <20060604195957.ff0438f5.khali@linux-fr.org> +Content-Disposition: inline; filename=i2c-Kconfig-suggest-N-for-rare-devices.patch + +Improve the Kconfig help text of the follwing i2c drivers: +* busses/i2c-pca-isa.c +* chips/pcf8574.c +* chips/pcf8591.c +These are hard to detect and building them into the kernel +results in long delays at boot. + +March 2006, thread "I2C_PCA_ISA causes boot delays" +http://marc.theaimsgroup.com/?l=linux-kernel&m=114360399415744&w=2 + +April 2006, thread "i2c-related 1-minute hang during bootup" +http://marc.theaimsgroup.com/?l=linux-kernel&m=114640992330721&w=2 + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/i2c/busses/Kconfig | 6 ++++++ + drivers/i2c/chips/Kconfig | 8 ++++++++ + 2 files changed, 14 insertions(+) + +--- gregkh-2.6.orig/drivers/i2c/busses/Kconfig ++++ gregkh-2.6/drivers/i2c/busses/Kconfig +@@ -503,6 +503,7 @@ config I2C_PCA_ISA + tristate "PCA9564 on an ISA bus" + depends on I2C + select I2C_ALGOPCA ++ default n + help + This driver supports ISA boards using the Philips PCA 9564 + Parallel bus to I2C bus controller +@@ -510,6 +511,11 @@ config I2C_PCA_ISA + This driver can also be built as a module. If so, the module + will be called i2c-pca-isa. + ++ This device is almost undetectable and using this driver on a ++ system which doesn't have this device will result in long ++ delays when I2C/SMBus chip drivers are loaded (e.g. at boot ++ time). If unsure, say N. ++ + config I2C_MV64XXX + tristate "Marvell mv64xxx I2C Controller" + depends on I2C && MV64X60 && EXPERIMENTAL +--- gregkh-2.6.orig/drivers/i2c/chips/Kconfig ++++ gregkh-2.6/drivers/i2c/chips/Kconfig +@@ -39,6 +39,7 @@ config SENSORS_EEPROM + config SENSORS_PCF8574 + tristate "Philips PCF8574 and PCF8574A" + depends on I2C && EXPERIMENTAL ++ default n + help + If you say yes here you get support for Philips PCF8574 and + PCF8574A chips. +@@ -46,6 +47,9 @@ config SENSORS_PCF8574 + This driver can also be built as a module. If so, the module + will be called pcf8574. + ++ These devices are hard to detect and rarely found on mainstream ++ hardware. If unsure, say N. ++ + config SENSORS_PCA9539 + tristate "Philips PCA9539 16-bit I/O port" + depends on I2C && EXPERIMENTAL +@@ -59,12 +63,16 @@ config SENSORS_PCA9539 + config SENSORS_PCF8591 + tristate "Philips PCF8591" + depends on I2C && EXPERIMENTAL ++ default n + help + If you say yes here you get support for Philips PCF8591 chips. + + This driver can also be built as a module. If so, the module + will be called pcf8591. + ++ These devices are hard to detect and rarely found on mainstream ++ hardware. If unsure, say N. ++ + config ISP1301_OMAP + tristate "Philips ISP1301 with OMAP OTG" + depends on I2C && ARCH_OMAP_OTG diff --git a/i2c/i2c-opencores-new-driver.patch b/i2c/i2c-opencores-new-driver.patch new file mode 100644 index 0000000000000..b78d687bac51e --- /dev/null +++ b/i2c/i2c-opencores-new-driver.patch @@ -0,0 +1,479 @@ +From khali@linux-fr.org Sun Jun 4 11:01:11 2006 +Date: Sun, 4 Jun 2006 20:01:08 +0200 +From: Jean Delvare <khali@linux-fr.org> +To: Greg KH <greg@kroah.com> +Cc: Peter Korsgaard <jacmet@sunsite.dk> +Subject: i2c: New bus driver for the OpenCores I2C controller +Message-Id: <20060604200108.d3bca76d.khali@linux-fr.org> +Content-Disposition: inline; filename=i2c-opencores-new-driver.patch + +From: Peter Korsgaard <jacmet@sunsite.dk> + +The following patch adds support for the OpenCores I2C controller IP +core (See http://www.opencores.org/projects.cgi/web/i2c/overview). + +Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk> +Signed-off-by: Andrew Morton <akpm@osdl.org> +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/i2c/busses/i2c-ocores | 51 +++++ + drivers/i2c/busses/Kconfig | 11 + + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ocores.c | 343 ++++++++++++++++++++++++++++++++++++ + include/linux/i2c-ocores.h | 19 + + 5 files changed, 425 insertions(+) + +--- /dev/null ++++ gregkh-2.6/Documentation/i2c/busses/i2c-ocores +@@ -0,0 +1,51 @@ ++Kernel driver i2c-ocores ++ ++Supported adapters: ++ * OpenCores.org I2C controller by Richard Herveille (see datasheet link) ++ Datasheet: http://www.opencores.org/projects.cgi/web/i2c/overview ++ ++Author: Peter Korsgaard <jacmet@sunsite.dk> ++ ++Description ++----------- ++ ++i2c-ocores is an i2c bus driver for the OpenCores.org I2C controller ++IP core by Richard Herveille. ++ ++Usage ++----- ++ ++i2c-ocores uses the platform bus, so you need to provide a struct ++platform_device with the base address and interrupt number. The ++dev.platform_data of the device should also point to a struct ++ocores_i2c_platform_data (see linux/i2c-ocores.h) describing the ++distance between registers and the input clock speed. ++ ++E.G. something like: ++ ++static struct resource ocores_resources[] = { ++ [0] = { ++ .start = MYI2C_BASEADDR, ++ .end = MYI2C_BASEADDR + 8, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = MYI2C_IRQ, ++ .end = MYI2C_IRQ, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct ocores_i2c_platform_data myi2c_data = { ++ .regstep = 2, /* two bytes between registers */ ++ .clock_khz = 50000, /* input clock of 50MHz */ ++}; ++ ++static struct platform_device myi2c = { ++ .name = "ocores-i2c", ++ .dev = { ++ .platform_data = &myi2c_data, ++ }, ++ .num_resources = ARRAY_SIZE(ocores_resources), ++ .resource = ocores_resources, ++}; +--- gregkh-2.6.orig/drivers/i2c/busses/Kconfig ++++ gregkh-2.6/drivers/i2c/busses/Kconfig +@@ -276,6 +276,17 @@ config I2C_NFORCE2 + This driver can also be built as a module. If so, the module + will be called i2c-nforce2. + ++config I2C_OCORES ++ tristate "OpenCores I2C Controller" ++ depends on I2C && EXPERIMENTAL ++ help ++ If you say yes to this option, support will be included for the ++ OpenCores I2C controller. For details see ++ http://www.opencores.org/projects.cgi/web/i2c/overview ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-ocores. ++ + config I2C_PARPORT + tristate "Parallel port adapter" + depends on I2C && PARPORT +--- gregkh-2.6.orig/drivers/i2c/busses/Makefile ++++ gregkh-2.6/drivers/i2c/busses/Makefile +@@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powerm + obj-$(CONFIG_I2C_MPC) += i2c-mpc.o + obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o + obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o ++obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o + obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o + obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o + obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o +--- /dev/null ++++ gregkh-2.6/drivers/i2c/busses/i2c-ocores.c +@@ -0,0 +1,343 @@ ++/* ++ * i2c-ocores.c: I2C bus driver for OpenCores I2C controller ++ * (http://www.opencores.org/projects.cgi/web/i2c/overview). ++ * ++ * Peter Korsgaard <jacmet@sunsite.dk> ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ */ ++ ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/platform_device.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/wait.h> ++#include <linux/i2c-ocores.h> ++#include <asm/io.h> ++ ++struct ocores_i2c { ++ void __iomem *base; ++ int regstep; ++ wait_queue_head_t wait; ++ struct i2c_adapter adap; ++ struct i2c_msg *msg; ++ int pos; ++ int nmsgs; ++ int state; /* see STATE_ */ ++}; ++ ++/* registers */ ++#define OCI2C_PRELOW 0 ++#define OCI2C_PREHIGH 1 ++#define OCI2C_CONTROL 2 ++#define OCI2C_DATA 3 ++#define OCI2C_CMD 4 ++#define OCI2C_STATUS 4 ++ ++#define OCI2C_CTRL_IEN 0x40 ++#define OCI2C_CTRL_EN 0x80 ++ ++#define OCI2C_CMD_START 0x91 ++#define OCI2C_CMD_STOP 0x41 ++#define OCI2C_CMD_READ 0x21 ++#define OCI2C_CMD_WRITE 0x11 ++#define OCI2C_CMD_READ_ACK 0x21 ++#define OCI2C_CMD_READ_NACK 0x29 ++#define OCI2C_CMD_IACK 0x01 ++ ++#define OCI2C_STAT_IF 0x01 ++#define OCI2C_STAT_TIP 0x02 ++#define OCI2C_STAT_ARBLOST 0x20 ++#define OCI2C_STAT_BUSY 0x40 ++#define OCI2C_STAT_NACK 0x80 ++ ++#define STATE_DONE 0 ++#define STATE_START 1 ++#define STATE_WRITE 2 ++#define STATE_READ 3 ++#define STATE_ERROR 4 ++ ++static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) ++{ ++ iowrite8(value, i2c->base + reg * i2c->regstep); ++} ++ ++static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) ++{ ++ return ioread8(i2c->base + reg * i2c->regstep); ++} ++ ++static void ocores_process(struct ocores_i2c *i2c) ++{ ++ struct i2c_msg *msg = i2c->msg; ++ u8 stat = oc_getreg(i2c, OCI2C_STATUS); ++ ++ if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { ++ /* stop has been sent */ ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); ++ wake_up(&i2c->wait); ++ return; ++ } ++ ++ /* error? */ ++ if (stat & OCI2C_STAT_ARBLOST) { ++ i2c->state = STATE_ERROR; ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); ++ return; ++ } ++ ++ if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { ++ i2c->state = ++ (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; ++ ++ if (stat & OCI2C_STAT_NACK) { ++ i2c->state = STATE_ERROR; ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); ++ return; ++ } ++ } else ++ msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); ++ ++ /* end of msg? */ ++ if (i2c->pos == msg->len) { ++ i2c->nmsgs--; ++ i2c->msg++; ++ i2c->pos = 0; ++ msg = i2c->msg; ++ ++ if (i2c->nmsgs) { /* end? */ ++ /* send start? */ ++ if (!(msg->flags & I2C_M_NOSTART)) { ++ u8 addr = (msg->addr << 1); ++ ++ if (msg->flags & I2C_M_RD) ++ addr |= 1; ++ ++ i2c->state = STATE_START; ++ ++ oc_setreg(i2c, OCI2C_DATA, addr); ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); ++ return; ++ } else ++ i2c->state = (msg->flags & I2C_M_RD) ++ ? STATE_READ : STATE_WRITE; ++ } else { ++ i2c->state = STATE_DONE; ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); ++ return; ++ } ++ } ++ ++ if (i2c->state == STATE_READ) { ++ oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ? ++ OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK); ++ } else { ++ oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); ++ } ++} ++ ++static irqreturn_t ocores_isr(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct ocores_i2c *i2c = dev_id; ++ ++ ocores_process(i2c); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ++{ ++ struct ocores_i2c *i2c = i2c_get_adapdata(adap); ++ ++ i2c->msg = msgs; ++ i2c->pos = 0; ++ i2c->nmsgs = num; ++ i2c->state = STATE_START; ++ ++ oc_setreg(i2c, OCI2C_DATA, ++ (i2c->msg->addr << 1) | ++ ((i2c->msg->flags & I2C_M_RD) ? 1:0)); ++ ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); ++ ++ if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || ++ (i2c->state == STATE_DONE), HZ)) ++ return (i2c->state == STATE_DONE) ? num : -EIO; ++ else ++ return -ETIMEDOUT; ++} ++ ++static void ocores_init(struct ocores_i2c *i2c, ++ struct ocores_i2c_platform_data *pdata) ++{ ++ int prescale; ++ u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); ++ ++ /* make sure the device is disabled */ ++ oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); ++ ++ prescale = (pdata->clock_khz / (5*100)) - 1; ++ oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff); ++ oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8); ++ ++ /* Init the device */ ++ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); ++ oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN); ++} ++ ++ ++static u32 ocores_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm ocores_algorithm = { ++ .master_xfer = ocores_xfer, ++ .functionality = ocores_func, ++}; ++ ++static struct i2c_adapter ocores_adapter = { ++ .owner = THIS_MODULE, ++ .name = "i2c-ocores", ++ .class = I2C_CLASS_HWMON, ++ .algo = &ocores_algorithm, ++ .timeout = 2, ++ .retries = 1, ++}; ++ ++ ++static int __devinit ocores_i2c_probe(struct platform_device *pdev) ++{ ++ struct ocores_i2c *i2c; ++ struct ocores_i2c_platform_data *pdata; ++ struct resource *res, *res2; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENODEV; ++ ++ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res2) ++ return -ENODEV; ++ ++ pdata = (struct ocores_i2c_platform_data*) pdev->dev.platform_data; ++ if (!pdata) ++ return -ENODEV; ++ ++ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); ++ if (!i2c) ++ return -ENOMEM; ++ ++ if (!request_mem_region(res->start, res->end - res->start + 1, ++ pdev->name)) { ++ dev_err(&pdev->dev, "Memory region busy\n"); ++ ret = -EBUSY; ++ goto request_mem_failed; ++ } ++ ++ i2c->base = ioremap(res->start, res->end - res->start + 1); ++ if (!i2c->base) { ++ dev_err(&pdev->dev, "Unable to map registers\n"); ++ ret = -EIO; ++ goto map_failed; ++ } ++ ++ i2c->regstep = pdata->regstep; ++ ocores_init(i2c, pdata); ++ ++ init_waitqueue_head(&i2c->wait); ++ ret = request_irq(res2->start, ocores_isr, 0, pdev->name, i2c); ++ if (ret) { ++ dev_err(&pdev->dev, "Cannot claim IRQ\n"); ++ goto request_irq_failed; ++ } ++ ++ /* hook up driver to tree */ ++ platform_set_drvdata(pdev, i2c); ++ i2c->adap = ocores_adapter; ++ i2c_set_adapdata(&i2c->adap, i2c); ++ i2c->adap.dev.parent = &pdev->dev; ++ ++ /* add i2c adapter to i2c tree */ ++ ret = i2c_add_adapter(&i2c->adap); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to add adapter\n"); ++ goto add_adapter_failed; ++ } ++ ++ return 0; ++ ++add_adapter_failed: ++ free_irq(res2->start, i2c); ++request_irq_failed: ++ iounmap(i2c->base); ++map_failed: ++ release_mem_region(res->start, res->end - res->start + 1); ++request_mem_failed: ++ kfree(i2c); ++ ++ return ret; ++} ++ ++static int __devexit ocores_i2c_remove(struct platform_device* pdev) ++{ ++ struct ocores_i2c *i2c = platform_get_drvdata(pdev); ++ struct resource *res; ++ ++ /* disable i2c logic */ ++ oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL) ++ & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); ++ ++ /* remove adapter & data */ ++ i2c_del_adapter(&i2c->adap); ++ platform_set_drvdata(pdev, NULL); ++ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (res) ++ free_irq(res->start, i2c); ++ ++ iounmap(i2c->base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res) ++ release_mem_region(res->start, res->end - res->start + 1); ++ ++ kfree(i2c); ++ ++ return 0; ++} ++ ++static struct platform_driver ocores_i2c_driver = { ++ .probe = ocores_i2c_probe, ++ .remove = __devexit_p(ocores_i2c_remove), ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ocores-i2c", ++ }, ++}; ++ ++static int __init ocores_i2c_init(void) ++{ ++ return platform_driver_register(&ocores_i2c_driver); ++} ++ ++static void __exit ocores_i2c_exit(void) ++{ ++ platform_driver_unregister(&ocores_i2c_driver); ++} ++ ++module_init(ocores_i2c_init); ++module_exit(ocores_i2c_exit); ++ ++MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); ++MODULE_DESCRIPTION("OpenCores I2C bus driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ gregkh-2.6/include/linux/i2c-ocores.h +@@ -0,0 +1,19 @@ ++/* ++ * i2c-ocores.h - definitions for the i2c-ocores interface ++ * ++ * Peter Korsgaard <jacmet@sunsite.dk> ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ */ ++ ++#ifndef _LINUX_I2C_OCORES_H ++#define _LINUX_I2C_OCORES_H ++ ++struct ocores_i2c_platform_data { ++ u32 regstep; /* distance between registers */ ++ u32 clock_khz; /* input clock in kHz */ ++}; ++ ++#endif /* _LINUX_I2C_OCORES_H */ |