diff options
Diffstat (limited to 'drivers/hwmon/smsc47m1.c')
-rw-r--r-- | drivers/hwmon/smsc47m1.c | 153 |
1 files changed, 127 insertions, 26 deletions
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 8ad50fdba00d..8fa462f2b570 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c | |||
@@ -136,11 +136,11 @@ struct smsc47m1_data { | |||
136 | 136 | ||
137 | struct smsc47m1_sio_data { | 137 | struct smsc47m1_sio_data { |
138 | enum chips type; | 138 | enum chips type; |
139 | u8 activate; /* Remember initial device state */ | ||
139 | }; | 140 | }; |
140 | 141 | ||
141 | 142 | ||
142 | static int smsc47m1_probe(struct platform_device *pdev); | 143 | static int __exit smsc47m1_remove(struct platform_device *pdev); |
143 | static int __devexit smsc47m1_remove(struct platform_device *pdev); | ||
144 | static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, | 144 | static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, |
145 | int init); | 145 | int init); |
146 | 146 | ||
@@ -160,8 +160,7 @@ static struct platform_driver smsc47m1_driver = { | |||
160 | .owner = THIS_MODULE, | 160 | .owner = THIS_MODULE, |
161 | .name = DRVNAME, | 161 | .name = DRVNAME, |
162 | }, | 162 | }, |
163 | .probe = smsc47m1_probe, | 163 | .remove = __exit_p(smsc47m1_remove), |
164 | .remove = __devexit_p(smsc47m1_remove), | ||
165 | }; | 164 | }; |
166 | 165 | ||
167 | static ssize_t get_fan(struct device *dev, struct device_attribute | 166 | static ssize_t get_fan(struct device *dev, struct device_attribute |
@@ -470,24 +469,126 @@ static int __init smsc47m1_find(unsigned short *addr, | |||
470 | superio_select(); | 469 | superio_select(); |
471 | *addr = (superio_inb(SUPERIO_REG_BASE) << 8) | 470 | *addr = (superio_inb(SUPERIO_REG_BASE) << 8) |
472 | | superio_inb(SUPERIO_REG_BASE + 1); | 471 | | superio_inb(SUPERIO_REG_BASE + 1); |
473 | val = superio_inb(SUPERIO_REG_ACT); | 472 | if (*addr == 0) { |
474 | if (*addr == 0 || (val & 0x01) == 0) { | 473 | pr_info(DRVNAME ": Device address not set, will not use\n"); |
475 | pr_info(DRVNAME ": Device is disabled, will not use\n"); | ||
476 | superio_exit(); | 474 | superio_exit(); |
477 | return -ENODEV; | 475 | return -ENODEV; |
478 | } | 476 | } |
479 | 477 | ||
478 | /* Enable only if address is set (needed at least on the | ||
479 | * Compaq Presario S4000NX) */ | ||
480 | sio_data->activate = superio_inb(SUPERIO_REG_ACT); | ||
481 | if ((sio_data->activate & 0x01) == 0) { | ||
482 | pr_info(DRVNAME ": Enabling device\n"); | ||
483 | superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01); | ||
484 | } | ||
485 | |||
480 | superio_exit(); | 486 | superio_exit(); |
481 | return 0; | 487 | return 0; |
482 | } | 488 | } |
483 | 489 | ||
484 | static int __devinit smsc47m1_probe(struct platform_device *pdev) | 490 | /* Restore device to its initial state */ |
491 | static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data) | ||
492 | { | ||
493 | if ((sio_data->activate & 0x01) == 0) { | ||
494 | superio_enter(); | ||
495 | superio_select(); | ||
496 | |||
497 | pr_info(DRVNAME ": Disabling device\n"); | ||
498 | superio_outb(SUPERIO_REG_ACT, sio_data->activate); | ||
499 | |||
500 | superio_exit(); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | #define CHECK 1 | ||
505 | #define REQUEST 2 | ||
506 | #define RELEASE 3 | ||
507 | |||
508 | /* | ||
509 | * This function can be used to: | ||
510 | * - test for resource conflicts with ACPI | ||
511 | * - request the resources | ||
512 | * - release the resources | ||
513 | * We only allocate the I/O ports we really need, to minimize the risk of | ||
514 | * conflicts with ACPI or with other drivers. | ||
515 | */ | ||
516 | static int smsc47m1_handle_resources(unsigned short address, enum chips type, | ||
517 | int action, struct device *dev) | ||
518 | { | ||
519 | static const u8 ports_m1[] = { | ||
520 | /* register, region length */ | ||
521 | 0x04, 1, | ||
522 | 0x33, 4, | ||
523 | 0x56, 7, | ||
524 | }; | ||
525 | |||
526 | static const u8 ports_m2[] = { | ||
527 | /* register, region length */ | ||
528 | 0x04, 1, | ||
529 | 0x09, 1, | ||
530 | 0x2c, 2, | ||
531 | 0x35, 4, | ||
532 | 0x56, 7, | ||
533 | 0x69, 4, | ||
534 | }; | ||
535 | |||
536 | int i, ports_size, err; | ||
537 | const u8 *ports; | ||
538 | |||
539 | switch (type) { | ||
540 | case smsc47m1: | ||
541 | default: | ||
542 | ports = ports_m1; | ||
543 | ports_size = ARRAY_SIZE(ports_m1); | ||
544 | break; | ||
545 | case smsc47m2: | ||
546 | ports = ports_m2; | ||
547 | ports_size = ARRAY_SIZE(ports_m2); | ||
548 | break; | ||
549 | } | ||
550 | |||
551 | for (i = 0; i + 1 < ports_size; i += 2) { | ||
552 | unsigned short start = address + ports[i]; | ||
553 | unsigned short len = ports[i + 1]; | ||
554 | |||
555 | switch (action) { | ||
556 | case CHECK: | ||
557 | /* Only check for conflicts */ | ||
558 | err = acpi_check_region(start, len, DRVNAME); | ||
559 | if (err) | ||
560 | return err; | ||
561 | break; | ||
562 | case REQUEST: | ||
563 | /* Request the resources */ | ||
564 | if (!request_region(start, len, DRVNAME)) { | ||
565 | dev_err(dev, "Region 0x%hx-0x%hx already in " | ||
566 | "use!\n", start, start + len); | ||
567 | |||
568 | /* Undo all requests */ | ||
569 | for (i -= 2; i >= 0; i -= 2) | ||
570 | release_region(address + ports[i], | ||
571 | ports[i + 1]); | ||
572 | return -EBUSY; | ||
573 | } | ||
574 | break; | ||
575 | case RELEASE: | ||
576 | /* Release the resources */ | ||
577 | release_region(start, len); | ||
578 | break; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | static int __init smsc47m1_probe(struct platform_device *pdev) | ||
485 | { | 586 | { |
486 | struct device *dev = &pdev->dev; | 587 | struct device *dev = &pdev->dev; |
487 | struct smsc47m1_sio_data *sio_data = dev->platform_data; | 588 | struct smsc47m1_sio_data *sio_data = dev->platform_data; |
488 | struct smsc47m1_data *data; | 589 | struct smsc47m1_data *data; |
489 | struct resource *res; | 590 | struct resource *res; |
490 | int err = 0; | 591 | int err; |
491 | int fan1, fan2, fan3, pwm1, pwm2, pwm3; | 592 | int fan1, fan2, fan3, pwm1, pwm2, pwm3; |
492 | 593 | ||
493 | static const char *names[] = { | 594 | static const char *names[] = { |
@@ -496,12 +597,10 @@ static int __devinit smsc47m1_probe(struct platform_device *pdev) | |||
496 | }; | 597 | }; |
497 | 598 | ||
498 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 599 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
499 | if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) { | 600 | err = smsc47m1_handle_resources(res->start, sio_data->type, |
500 | dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", | 601 | REQUEST, dev); |
501 | (unsigned long)res->start, | 602 | if (err < 0) |
502 | (unsigned long)res->end); | 603 | return err; |
503 | return -EBUSY; | ||
504 | } | ||
505 | 604 | ||
506 | if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { | 605 | if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { |
507 | err = -ENOMEM; | 606 | err = -ENOMEM; |
@@ -637,11 +736,11 @@ error_free: | |||
637 | platform_set_drvdata(pdev, NULL); | 736 | platform_set_drvdata(pdev, NULL); |
638 | kfree(data); | 737 | kfree(data); |
639 | error_release: | 738 | error_release: |
640 | release_region(res->start, SMSC_EXTENT); | 739 | smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev); |
641 | return err; | 740 | return err; |
642 | } | 741 | } |
643 | 742 | ||
644 | static int __devexit smsc47m1_remove(struct platform_device *pdev) | 743 | static int __exit smsc47m1_remove(struct platform_device *pdev) |
645 | { | 744 | { |
646 | struct smsc47m1_data *data = platform_get_drvdata(pdev); | 745 | struct smsc47m1_data *data = platform_get_drvdata(pdev); |
647 | struct resource *res; | 746 | struct resource *res; |
@@ -650,7 +749,7 @@ static int __devexit smsc47m1_remove(struct platform_device *pdev) | |||
650 | sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); | 749 | sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); |
651 | 750 | ||
652 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 751 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
653 | release_region(res->start, SMSC_EXTENT); | 752 | smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev); |
654 | platform_set_drvdata(pdev, NULL); | 753 | platform_set_drvdata(pdev, NULL); |
655 | kfree(data); | 754 | kfree(data); |
656 | 755 | ||
@@ -717,7 +816,7 @@ static int __init smsc47m1_device_add(unsigned short address, | |||
717 | }; | 816 | }; |
718 | int err; | 817 | int err; |
719 | 818 | ||
720 | err = acpi_check_resource_conflict(&res); | 819 | err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL); |
721 | if (err) | 820 | if (err) |
722 | goto exit; | 821 | goto exit; |
723 | 822 | ||
@@ -766,27 +865,29 @@ static int __init sm_smsc47m1_init(void) | |||
766 | if (smsc47m1_find(&address, &sio_data)) | 865 | if (smsc47m1_find(&address, &sio_data)) |
767 | return -ENODEV; | 866 | return -ENODEV; |
768 | 867 | ||
769 | err = platform_driver_register(&smsc47m1_driver); | 868 | /* Sets global pdev as a side effect */ |
869 | err = smsc47m1_device_add(address, &sio_data); | ||
770 | if (err) | 870 | if (err) |
771 | goto exit; | 871 | goto exit; |
772 | 872 | ||
773 | /* Sets global pdev as a side effect */ | 873 | err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe); |
774 | err = smsc47m1_device_add(address, &sio_data); | ||
775 | if (err) | 874 | if (err) |
776 | goto exit_driver; | 875 | goto exit_device; |
777 | 876 | ||
778 | return 0; | 877 | return 0; |
779 | 878 | ||
780 | exit_driver: | 879 | exit_device: |
781 | platform_driver_unregister(&smsc47m1_driver); | 880 | platform_device_unregister(pdev); |
881 | smsc47m1_restore(&sio_data); | ||
782 | exit: | 882 | exit: |
783 | return err; | 883 | return err; |
784 | } | 884 | } |
785 | 885 | ||
786 | static void __exit sm_smsc47m1_exit(void) | 886 | static void __exit sm_smsc47m1_exit(void) |
787 | { | 887 | { |
788 | platform_device_unregister(pdev); | ||
789 | platform_driver_unregister(&smsc47m1_driver); | 888 | platform_driver_unregister(&smsc47m1_driver); |
889 | smsc47m1_restore(pdev->dev.platform_data); | ||
890 | platform_device_unregister(pdev); | ||
790 | } | 891 | } |
791 | 892 | ||
792 | MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); | 893 | MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); |