diff options
| -rw-r--r-- | Documentation/hwmon/pc87427 | 13 | ||||
| -rw-r--r-- | drivers/hwmon/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/hwmon/pc87427.c | 271 |
3 files changed, 282 insertions, 5 deletions
diff --git a/Documentation/hwmon/pc87427 b/Documentation/hwmon/pc87427 index db5cc1227a83..3282bf39d67d 100644 --- a/Documentation/hwmon/pc87427 +++ b/Documentation/hwmon/pc87427 | |||
| @@ -20,8 +20,8 @@ The National Semiconductor Super I/O chip includes complete hardware | |||
| 20 | monitoring capabilities. It can monitor up to 18 voltages, 8 fans and | 20 | monitoring capabilities. It can monitor up to 18 voltages, 8 fans and |
| 21 | 6 temperature sensors. Only the fans are supported at the moment. | 21 | 6 temperature sensors. Only the fans are supported at the moment. |
| 22 | 22 | ||
| 23 | This chip also has fan controlling features, which are not yet supported | 23 | This chip also has fan controlling features (up to 4 PWM outputs), |
| 24 | by this driver either. | 24 | which are partly supported by this driver. |
| 25 | 25 | ||
| 26 | The driver assumes that no more than one chip is present, which seems | 26 | The driver assumes that no more than one chip is present, which seems |
| 27 | reasonable. | 27 | reasonable. |
| @@ -36,3 +36,12 @@ signal. Speeds down to 83 RPM can be measured. | |||
| 36 | An alarm is triggered if the rotation speed drops below a programmable | 36 | An alarm is triggered if the rotation speed drops below a programmable |
| 37 | limit. Another alarm is triggered if the speed is too low to be measured | 37 | limit. Another alarm is triggered if the speed is too low to be measured |
| 38 | (including stalled or missing fan). | 38 | (including stalled or missing fan). |
| 39 | |||
| 40 | |||
| 41 | Fan Speed Control | ||
| 42 | ----------------- | ||
| 43 | |||
| 44 | Fan speed can be controlled by PWM outputs. There are 4 possible modes: | ||
| 45 | always off, always on, manual and automatic. The latter isn't supported | ||
| 46 | by the driver: you can only return to that mode if it was the original | ||
| 47 | setting, and the configuration interface is missing. | ||
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d50e9fa8016f..ea3d8dff684b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
| @@ -711,7 +711,8 @@ config SENSORS_PC87427 | |||
| 711 | functions of the National Semiconductor PC87427 Super-I/O chip. | 711 | functions of the National Semiconductor PC87427 Super-I/O chip. |
| 712 | The chip has two distinct logical devices, one for fan speed | 712 | The chip has two distinct logical devices, one for fan speed |
| 713 | monitoring and control, and one for voltage and temperature | 713 | monitoring and control, and one for voltage and temperature |
| 714 | monitoring. Only fan speed monitoring is supported right now. | 714 | monitoring. Only fan speed monitoring and control is supported |
| 715 | right now. | ||
| 715 | 716 | ||
| 716 | This driver can also be built as a module. If so, the module | 717 | This driver can also be built as a module. If so, the module |
| 717 | will be called pc87427. | 718 | will be called pc87427. |
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index 0ac55ba6c6b8..869822785b4f 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c | |||
| @@ -15,10 +15,10 @@ | |||
| 15 | * Supports the following chips: | 15 | * Supports the following chips: |
| 16 | * | 16 | * |
| 17 | * Chip #vin #fan #pwm #temp devid | 17 | * Chip #vin #fan #pwm #temp devid |
| 18 | * PC87427 - 8 - - 0xF2 | 18 | * PC87427 - 8 4 - 0xF2 |
| 19 | * | 19 | * |
| 20 | * This driver assumes that no more than one chip is present. | 20 | * This driver assumes that no more than one chip is present. |
| 21 | * Only fan inputs are supported so far, although the chip can do much more. | 21 | * Only fans are supported so far, although the chip can do much more. |
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
| @@ -57,10 +57,16 @@ struct pc87427_data { | |||
| 57 | u16 fan[8]; /* register values */ | 57 | u16 fan[8]; /* register values */ |
| 58 | u16 fan_min[8]; /* register values */ | 58 | u16 fan_min[8]; /* register values */ |
| 59 | u8 fan_status[8]; /* register values */ | 59 | u8 fan_status[8]; /* register values */ |
| 60 | |||
| 61 | u8 pwm_enabled; /* bit vector */ | ||
| 62 | u8 pwm_auto_ok; /* bit vector */ | ||
| 63 | u8 pwm_enable[4]; /* register values */ | ||
| 64 | u8 pwm[4]; /* register values */ | ||
| 60 | }; | 65 | }; |
| 61 | 66 | ||
| 62 | struct pc87427_sio_data { | 67 | struct pc87427_sio_data { |
| 63 | u8 has_fanin; | 68 | u8 has_fanin; |
| 69 | u8 has_fanout; | ||
| 64 | }; | 70 | }; |
| 65 | 71 | ||
| 66 | /* | 72 | /* |
| @@ -72,7 +78,9 @@ struct pc87427_sio_data { | |||
| 72 | #define SIOREG_CF2 0x22 /* Configuration 2 */ | 78 | #define SIOREG_CF2 0x22 /* Configuration 2 */ |
| 73 | #define SIOREG_CF3 0x23 /* Configuration 3 */ | 79 | #define SIOREG_CF3 0x23 /* Configuration 3 */ |
| 74 | #define SIOREG_CF4 0x24 /* Configuration 4 */ | 80 | #define SIOREG_CF4 0x24 /* Configuration 4 */ |
| 81 | #define SIOREG_CF5 0x25 /* Configuration 5 */ | ||
| 75 | #define SIOREG_CFB 0x2B /* Configuration B */ | 82 | #define SIOREG_CFB 0x2B /* Configuration B */ |
| 83 | #define SIOREG_CFC 0x2C /* Configuration C */ | ||
| 76 | #define SIOREG_CFD 0x2D /* Configuration D */ | 84 | #define SIOREG_CFD 0x2D /* Configuration D */ |
| 77 | #define SIOREG_ACT 0x30 /* Device activation */ | 85 | #define SIOREG_ACT 0x30 /* Device activation */ |
| 78 | #define SIOREG_MAP 0x50 /* I/O or memory mapping */ | 86 | #define SIOREG_MAP 0x50 /* I/O or memory mapping */ |
| @@ -188,6 +196,61 @@ static inline u16 fan_to_reg(unsigned long val) | |||
| 188 | } | 196 | } |
| 189 | 197 | ||
| 190 | /* | 198 | /* |
| 199 | * PWM registers and conversions | ||
| 200 | */ | ||
| 201 | |||
| 202 | #define PC87427_REG_PWM_ENABLE 0x10 | ||
| 203 | #define PC87427_REG_PWM_DUTY 0x12 | ||
| 204 | |||
| 205 | #define PWM_ENABLE_MODE_MASK (7 << 4) | ||
| 206 | #define PWM_ENABLE_CTLEN (1 << 0) | ||
| 207 | |||
| 208 | #define PWM_MODE_MANUAL (0 << 4) | ||
| 209 | #define PWM_MODE_AUTO (1 << 4) | ||
| 210 | #define PWM_MODE_OFF (2 << 4) | ||
| 211 | #define PWM_MODE_ON (7 << 4) | ||
| 212 | |||
| 213 | /* Dedicated function to read all registers related to a given PWM output. | ||
| 214 | This saves us quite a few locks and bank selections. | ||
| 215 | Must be called with data->lock held. | ||
| 216 | nr is from 0 to 3 */ | ||
| 217 | static void pc87427_readall_pwm(struct pc87427_data *data, u8 nr) | ||
| 218 | { | ||
| 219 | int iobase = data->address[LD_FAN]; | ||
| 220 | |||
| 221 | outb(BANK_FC(nr), iobase + PC87427_REG_BANK); | ||
| 222 | data->pwm_enable[nr] = inb(iobase + PC87427_REG_PWM_ENABLE); | ||
| 223 | data->pwm[nr] = inb(iobase + PC87427_REG_PWM_DUTY); | ||
| 224 | } | ||
| 225 | |||
| 226 | static inline int pwm_enable_from_reg(u8 reg) | ||
| 227 | { | ||
| 228 | switch (reg & PWM_ENABLE_MODE_MASK) { | ||
| 229 | case PWM_MODE_ON: | ||
| 230 | return 0; | ||
| 231 | case PWM_MODE_MANUAL: | ||
| 232 | case PWM_MODE_OFF: | ||
| 233 | return 1; | ||
| 234 | case PWM_MODE_AUTO: | ||
| 235 | return 2; | ||
| 236 | default: | ||
| 237 | return -EPROTO; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval) | ||
| 242 | { | ||
| 243 | switch (val) { | ||
| 244 | default: | ||
| 245 | return PWM_MODE_ON; | ||
| 246 | case 1: | ||
| 247 | return pwmval ? PWM_MODE_MANUAL : PWM_MODE_OFF; | ||
| 248 | case 2: | ||
| 249 | return PWM_MODE_AUTO; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /* | ||
| 191 | * Data interface | 254 | * Data interface |
| 192 | */ | 255 | */ |
| 193 | 256 | ||
| @@ -207,6 +270,14 @@ static struct pc87427_data *pc87427_update_device(struct device *dev) | |||
| 207 | continue; | 270 | continue; |
| 208 | pc87427_readall_fan(data, i); | 271 | pc87427_readall_fan(data, i); |
| 209 | } | 272 | } |
| 273 | |||
| 274 | /* PWM outputs */ | ||
| 275 | for (i = 0; i < 4; i++) { | ||
| 276 | if (!(data->pwm_enabled & (1 << i))) | ||
| 277 | continue; | ||
| 278 | pc87427_readall_pwm(data, i); | ||
| 279 | } | ||
| 280 | |||
| 210 | data->last_updated = jiffies; | 281 | data->last_updated = jiffies; |
| 211 | 282 | ||
| 212 | done: | 283 | done: |
| @@ -384,6 +455,145 @@ static const struct attribute_group pc87427_group_fan[8] = { | |||
| 384 | { .attrs = pc87427_attributes_fan[7] }, | 455 | { .attrs = pc87427_attributes_fan[7] }, |
| 385 | }; | 456 | }; |
| 386 | 457 | ||
| 458 | /* Must be called with data->lock held and pc87427_readall_pwm() freshly | ||
| 459 | called */ | ||
| 460 | static void update_pwm_enable(struct pc87427_data *data, int nr, u8 mode) | ||
| 461 | { | ||
| 462 | int iobase = data->address[LD_FAN]; | ||
| 463 | data->pwm_enable[nr] &= ~PWM_ENABLE_MODE_MASK; | ||
| 464 | data->pwm_enable[nr] |= mode; | ||
| 465 | outb(data->pwm_enable[nr], iobase + PC87427_REG_PWM_ENABLE); | ||
| 466 | } | ||
| 467 | |||
| 468 | static ssize_t show_pwm_enable(struct device *dev, struct device_attribute | ||
| 469 | *devattr, char *buf) | ||
| 470 | { | ||
| 471 | struct pc87427_data *data = pc87427_update_device(dev); | ||
| 472 | int nr = to_sensor_dev_attr(devattr)->index; | ||
| 473 | int pwm_enable; | ||
| 474 | |||
| 475 | pwm_enable = pwm_enable_from_reg(data->pwm_enable[nr]); | ||
| 476 | if (pwm_enable < 0) | ||
| 477 | return pwm_enable; | ||
| 478 | return sprintf(buf, "%d\n", pwm_enable); | ||
| 479 | } | ||
| 480 | |||
| 481 | static ssize_t set_pwm_enable(struct device *dev, struct device_attribute | ||
| 482 | *devattr, const char *buf, size_t count) | ||
| 483 | { | ||
| 484 | struct pc87427_data *data = dev_get_drvdata(dev); | ||
| 485 | int nr = to_sensor_dev_attr(devattr)->index; | ||
| 486 | unsigned long val; | ||
| 487 | |||
| 488 | if (strict_strtoul(buf, 10, &val) < 0 || val > 2) | ||
| 489 | return -EINVAL; | ||
| 490 | /* Can't go to automatic mode if it isn't configured */ | ||
| 491 | if (val == 2 && !(data->pwm_auto_ok & (1 << nr))) | ||
| 492 | return -EINVAL; | ||
| 493 | |||
| 494 | mutex_lock(&data->lock); | ||
| 495 | pc87427_readall_pwm(data, nr); | ||
| 496 | update_pwm_enable(data, nr, pwm_enable_to_reg(val, data->pwm[nr])); | ||
| 497 | mutex_unlock(&data->lock); | ||
| 498 | |||
| 499 | return count; | ||
| 500 | } | ||
| 501 | |||
| 502 | static ssize_t show_pwm(struct device *dev, struct device_attribute | ||
| 503 | *devattr, char *buf) | ||
| 504 | { | ||
| 505 | struct pc87427_data *data = pc87427_update_device(dev); | ||
| 506 | int nr = to_sensor_dev_attr(devattr)->index; | ||
| 507 | |||
| 508 | return sprintf(buf, "%d\n", (int)data->pwm[nr]); | ||
| 509 | } | ||
| 510 | |||
| 511 | static ssize_t set_pwm(struct device *dev, struct device_attribute | ||
| 512 | *devattr, const char *buf, size_t count) | ||
| 513 | { | ||
| 514 | struct pc87427_data *data = dev_get_drvdata(dev); | ||
| 515 | int nr = to_sensor_dev_attr(devattr)->index; | ||
| 516 | unsigned long val; | ||
| 517 | int iobase = data->address[LD_FAN]; | ||
| 518 | u8 mode; | ||
| 519 | |||
| 520 | if (strict_strtoul(buf, 10, &val) < 0 || val > 0xff) | ||
| 521 | return -EINVAL; | ||
| 522 | |||
| 523 | mutex_lock(&data->lock); | ||
| 524 | pc87427_readall_pwm(data, nr); | ||
| 525 | mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK; | ||
| 526 | if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) { | ||
| 527 | dev_notice(dev, "Can't set PWM%d duty cycle while not in " | ||
| 528 | "manual mode\n", nr + 1); | ||
| 529 | mutex_unlock(&data->lock); | ||
| 530 | return -EPERM; | ||
| 531 | } | ||
| 532 | |||
| 533 | /* We may have to change the mode */ | ||
| 534 | if (mode == PWM_MODE_MANUAL && val == 0) { | ||
| 535 | /* Transition from Manual to Off */ | ||
| 536 | update_pwm_enable(data, nr, PWM_MODE_OFF); | ||
| 537 | mode = PWM_MODE_OFF; | ||
| 538 | dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1, | ||
| 539 | "manual", "off"); | ||
| 540 | } else if (mode == PWM_MODE_OFF && val != 0) { | ||
| 541 | /* Transition from Off to Manual */ | ||
| 542 | update_pwm_enable(data, nr, PWM_MODE_MANUAL); | ||
| 543 | mode = PWM_MODE_MANUAL; | ||
| 544 | dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1, | ||
| 545 | "off", "manual"); | ||
| 546 | } | ||
| 547 | |||
| 548 | data->pwm[nr] = val; | ||
| 549 | if (mode == PWM_MODE_MANUAL) | ||
| 550 | outb(val, iobase + PC87427_REG_PWM_DUTY); | ||
| 551 | mutex_unlock(&data->lock); | ||
| 552 | |||
| 553 | return count; | ||
| 554 | } | ||
| 555 | |||
| 556 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, | ||
| 557 | show_pwm_enable, set_pwm_enable, 0); | ||
| 558 | static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, | ||
| 559 | show_pwm_enable, set_pwm_enable, 1); | ||
| 560 | static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, | ||
| 561 | show_pwm_enable, set_pwm_enable, 2); | ||
| 562 | static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, | ||
| 563 | show_pwm_enable, set_pwm_enable, 3); | ||
| 564 | |||
| 565 | static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); | ||
| 566 | static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); | ||
| 567 | static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); | ||
| 568 | static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); | ||
| 569 | |||
| 570 | static struct attribute *pc87427_attributes_pwm[4][3] = { | ||
| 571 | { | ||
| 572 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
| 573 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
| 574 | NULL | ||
| 575 | }, { | ||
| 576 | &sensor_dev_attr_pwm2_enable.dev_attr.attr, | ||
| 577 | &sensor_dev_attr_pwm2.dev_attr.attr, | ||
| 578 | NULL | ||
| 579 | }, { | ||
| 580 | &sensor_dev_attr_pwm3_enable.dev_attr.attr, | ||
| 581 | &sensor_dev_attr_pwm3.dev_attr.attr, | ||
| 582 | NULL | ||
| 583 | }, { | ||
| 584 | &sensor_dev_attr_pwm4_enable.dev_attr.attr, | ||
| 585 | &sensor_dev_attr_pwm4.dev_attr.attr, | ||
| 586 | NULL | ||
| 587 | } | ||
| 588 | }; | ||
| 589 | |||
| 590 | static const struct attribute_group pc87427_group_pwm[4] = { | ||
| 591 | { .attrs = pc87427_attributes_pwm[0] }, | ||
| 592 | { .attrs = pc87427_attributes_pwm[1] }, | ||
| 593 | { .attrs = pc87427_attributes_pwm[2] }, | ||
| 594 | { .attrs = pc87427_attributes_pwm[3] }, | ||
| 595 | }; | ||
| 596 | |||
| 387 | static ssize_t show_name(struct device *dev, struct device_attribute | 597 | static ssize_t show_name(struct device *dev, struct device_attribute |
| 388 | *devattr, char *buf) | 598 | *devattr, char *buf) |
| 389 | { | 599 | { |
| @@ -431,6 +641,25 @@ static void __devinit pc87427_init_device(struct device *dev) | |||
| 431 | } | 641 | } |
| 432 | data->fan_enabled = sio_data->has_fanin; | 642 | data->fan_enabled = sio_data->has_fanin; |
| 433 | } | 643 | } |
| 644 | |||
| 645 | /* Check which PWM outputs are enabled */ | ||
| 646 | for (i = 0; i < 4; i++) { | ||
| 647 | if (!(sio_data->has_fanout & (1 << i))) /* Not wired */ | ||
| 648 | continue; | ||
| 649 | reg = pc87427_read8_bank(data, LD_FAN, BANK_FC(i), | ||
| 650 | PC87427_REG_PWM_ENABLE); | ||
| 651 | if (reg & PWM_ENABLE_CTLEN) | ||
| 652 | data->pwm_enabled |= (1 << i); | ||
| 653 | |||
| 654 | /* We don't expose an interface to reconfigure the automatic | ||
| 655 | fan control mode, so only allow to return to this mode if | ||
| 656 | it was originally set. */ | ||
| 657 | if ((reg & PWM_ENABLE_MODE_MASK) == PWM_MODE_AUTO) { | ||
| 658 | dev_dbg(dev, "PWM%d is in automatic control mode\n", | ||
| 659 | i + 1); | ||
| 660 | data->pwm_auto_ok |= (1 << i); | ||
| 661 | } | ||
| 662 | } | ||
| 434 | } | 663 | } |
| 435 | 664 | ||
| 436 | static int __devinit pc87427_probe(struct platform_device *pdev) | 665 | static int __devinit pc87427_probe(struct platform_device *pdev) |
| @@ -474,6 +703,14 @@ static int __devinit pc87427_probe(struct platform_device *pdev) | |||
| 474 | if (err) | 703 | if (err) |
| 475 | goto exit_remove_files; | 704 | goto exit_remove_files; |
| 476 | } | 705 | } |
| 706 | for (i = 0; i < 4; i++) { | ||
| 707 | if (!(data->pwm_enabled & (1 << i))) | ||
| 708 | continue; | ||
| 709 | err = sysfs_create_group(&pdev->dev.kobj, | ||
| 710 | &pc87427_group_pwm[i]); | ||
| 711 | if (err) | ||
| 712 | goto exit_remove_files; | ||
| 713 | } | ||
| 477 | 714 | ||
| 478 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | 715 | data->hwmon_dev = hwmon_device_register(&pdev->dev); |
| 479 | if (IS_ERR(data->hwmon_dev)) { | 716 | if (IS_ERR(data->hwmon_dev)) { |
| @@ -490,6 +727,11 @@ exit_remove_files: | |||
| 490 | continue; | 727 | continue; |
| 491 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); | 728 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); |
| 492 | } | 729 | } |
| 730 | for (i = 0; i < 4; i++) { | ||
| 731 | if (!(data->pwm_enabled & (1 << i))) | ||
| 732 | continue; | ||
| 733 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_pwm[i]); | ||
| 734 | } | ||
| 493 | exit_release_region: | 735 | exit_release_region: |
| 494 | release_region(res->start, resource_size(res)); | 736 | release_region(res->start, resource_size(res)); |
| 495 | exit_kfree: | 737 | exit_kfree: |
| @@ -512,6 +754,11 @@ static int __devexit pc87427_remove(struct platform_device *pdev) | |||
| 512 | continue; | 754 | continue; |
| 513 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); | 755 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); |
| 514 | } | 756 | } |
| 757 | for (i = 0; i < 4; i++) { | ||
| 758 | if (!(data->pwm_enabled & (1 << i))) | ||
| 759 | continue; | ||
| 760 | sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_pwm[i]); | ||
| 761 | } | ||
| 515 | platform_set_drvdata(pdev, NULL); | 762 | platform_set_drvdata(pdev, NULL); |
| 516 | kfree(data); | 763 | kfree(data); |
| 517 | 764 | ||
| @@ -648,6 +895,26 @@ static int __init pc87427_find(int sioaddr, unsigned short *address, | |||
| 648 | if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5))) | 895 | if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5))) |
| 649 | sio_data->has_fanin |= (1 << 6); /* FANIN6 */ | 896 | sio_data->has_fanin |= (1 << 6); /* FANIN6 */ |
| 650 | 897 | ||
| 898 | /* Check which fan outputs are wired */ | ||
| 899 | sio_data->has_fanout = (1 << 0); /* FANOUT0 */ | ||
| 900 | if (cfg_b & (1 << 0)) | ||
| 901 | sio_data->has_fanout |= (1 << 3); /* FANOUT3 */ | ||
| 902 | |||
| 903 | cfg = superio_inb(sioaddr, SIOREG_CFC); | ||
| 904 | if (!(cfg & (1 << 4))) { | ||
| 905 | if (cfg_b & (1 << 1)) | ||
| 906 | sio_data->has_fanout |= (1 << 1); /* FANOUT1 */ | ||
| 907 | if (cfg_b & (1 << 2)) | ||
| 908 | sio_data->has_fanout |= (1 << 2); /* FANOUT2 */ | ||
| 909 | } | ||
| 910 | |||
| 911 | /* FANOUT1 and FANOUT2 can each be routed to 2 different pins */ | ||
| 912 | cfg = superio_inb(sioaddr, SIOREG_CF5); | ||
| 913 | if (cfg & (1 << 6)) | ||
| 914 | sio_data->has_fanout |= (1 << 1); /* FANOUT1 */ | ||
| 915 | if (cfg & (1 << 5)) | ||
| 916 | sio_data->has_fanout |= (1 << 2); /* FANOUT2 */ | ||
| 917 | |||
| 651 | exit: | 918 | exit: |
| 652 | superio_exit(sioaddr); | 919 | superio_exit(sioaddr); |
| 653 | return err; | 920 | return err; |
