aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/smsc47m1.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/smsc47m1.c')
-rw-r--r--drivers/hwmon/smsc47m1.c153
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
137struct smsc47m1_sio_data { 137struct smsc47m1_sio_data {
138 enum chips type; 138 enum chips type;
139 u8 activate; /* Remember initial device state */
139}; 140};
140 141
141 142
142static int smsc47m1_probe(struct platform_device *pdev); 143static int __exit smsc47m1_remove(struct platform_device *pdev);
143static int __devexit smsc47m1_remove(struct platform_device *pdev);
144static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, 144static 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
167static ssize_t get_fan(struct device *dev, struct device_attribute 166static 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
484static int __devinit smsc47m1_probe(struct platform_device *pdev) 490/* Restore device to its initial state */
491static 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 */
516static 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
585static 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);
639error_release: 738error_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
644static int __devexit smsc47m1_remove(struct platform_device *pdev) 743static 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
780exit_driver: 879exit_device:
781 platform_driver_unregister(&smsc47m1_driver); 880 platform_device_unregister(pdev);
881 smsc47m1_restore(&sio_data);
782exit: 882exit:
783 return err; 883 return err;
784} 884}
785 885
786static void __exit sm_smsc47m1_exit(void) 886static 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
792MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); 893MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");