diff options
author | Jean Delvare <khali@linux-fr.org> | 2009-12-16 15:38:26 -0500 |
---|---|---|
committer | Jean Delvare <khali@linux-fr.org> | 2009-12-16 15:38:26 -0500 |
commit | a0e92d70f35b5fd7da8ec2160cda78b98e2113bc (patch) | |
tree | 81ace7f9afc762a016da2de26e62f6bea153cdf8 | |
parent | 3c57e89b467d1db6fda74d5c97112c8b9466dd97 (diff) |
hwmon: (smsc47m1) Only request I/O ports we really use
The I/O area of the SMSC LPC47M1xx chips which we use, gives access to
a lot of registers, some of which are related to fan speed monitoring
and control, but many are not. At the moment, the smsc47m1 driver
requests the whole I/O port range. This could easily result in
resource conflicts with either ACPI or other drivers.
Request only the I/O ports we really use, to prevent such conflicts.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Tested-by: Sean Fidler <fidlersean@gmail.com>
-rw-r--r-- | drivers/hwmon/smsc47m1.c | 99 |
1 files changed, 89 insertions, 10 deletions
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 8ad50fdba00d..bfef22395772 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c | |||
@@ -481,13 +481,94 @@ static int __init smsc47m1_find(unsigned short *addr, | |||
481 | return 0; | 481 | return 0; |
482 | } | 482 | } |
483 | 483 | ||
484 | #define CHECK 1 | ||
485 | #define REQUEST 2 | ||
486 | #define RELEASE 3 | ||
487 | |||
488 | /* | ||
489 | * This function can be used to: | ||
490 | * - test for resource conflicts with ACPI | ||
491 | * - request the resources | ||
492 | * - release the resources | ||
493 | * We only allocate the I/O ports we really need, to minimize the risk of | ||
494 | * conflicts with ACPI or with other drivers. | ||
495 | */ | ||
496 | static int smsc47m1_handle_resources(unsigned short address, enum chips type, | ||
497 | int action, struct device *dev) | ||
498 | { | ||
499 | static const u8 ports_m1[] = { | ||
500 | /* register, region length */ | ||
501 | 0x04, 1, | ||
502 | 0x33, 4, | ||
503 | 0x56, 7, | ||
504 | }; | ||
505 | |||
506 | static const u8 ports_m2[] = { | ||
507 | /* register, region length */ | ||
508 | 0x04, 1, | ||
509 | 0x09, 1, | ||
510 | 0x2c, 2, | ||
511 | 0x35, 4, | ||
512 | 0x56, 7, | ||
513 | 0x69, 4, | ||
514 | }; | ||
515 | |||
516 | int i, ports_size, err; | ||
517 | const u8 *ports; | ||
518 | |||
519 | switch (type) { | ||
520 | case smsc47m1: | ||
521 | default: | ||
522 | ports = ports_m1; | ||
523 | ports_size = ARRAY_SIZE(ports_m1); | ||
524 | break; | ||
525 | case smsc47m2: | ||
526 | ports = ports_m2; | ||
527 | ports_size = ARRAY_SIZE(ports_m2); | ||
528 | break; | ||
529 | } | ||
530 | |||
531 | for (i = 0; i + 1 < ports_size; i += 2) { | ||
532 | unsigned short start = address + ports[i]; | ||
533 | unsigned short len = ports[i + 1]; | ||
534 | |||
535 | switch (action) { | ||
536 | case CHECK: | ||
537 | /* Only check for conflicts */ | ||
538 | err = acpi_check_region(start, len, DRVNAME); | ||
539 | if (err) | ||
540 | return err; | ||
541 | break; | ||
542 | case REQUEST: | ||
543 | /* Request the resources */ | ||
544 | if (!request_region(start, len, DRVNAME)) { | ||
545 | dev_err(dev, "Region 0x%hx-0x%hx already in " | ||
546 | "use!\n", start, start + len); | ||
547 | |||
548 | /* Undo all requests */ | ||
549 | for (i -= 2; i >= 0; i -= 2) | ||
550 | release_region(address + ports[i], | ||
551 | ports[i + 1]); | ||
552 | return -EBUSY; | ||
553 | } | ||
554 | break; | ||
555 | case RELEASE: | ||
556 | /* Release the resources */ | ||
557 | release_region(start, len); | ||
558 | break; | ||
559 | } | ||
560 | } | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
484 | static int __devinit smsc47m1_probe(struct platform_device *pdev) | 565 | static int __devinit smsc47m1_probe(struct platform_device *pdev) |
485 | { | 566 | { |
486 | struct device *dev = &pdev->dev; | 567 | struct device *dev = &pdev->dev; |
487 | struct smsc47m1_sio_data *sio_data = dev->platform_data; | 568 | struct smsc47m1_sio_data *sio_data = dev->platform_data; |
488 | struct smsc47m1_data *data; | 569 | struct smsc47m1_data *data; |
489 | struct resource *res; | 570 | struct resource *res; |
490 | int err = 0; | 571 | int err; |
491 | int fan1, fan2, fan3, pwm1, pwm2, pwm3; | 572 | int fan1, fan2, fan3, pwm1, pwm2, pwm3; |
492 | 573 | ||
493 | static const char *names[] = { | 574 | static const char *names[] = { |
@@ -496,12 +577,10 @@ static int __devinit smsc47m1_probe(struct platform_device *pdev) | |||
496 | }; | 577 | }; |
497 | 578 | ||
498 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 579 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
499 | if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) { | 580 | err = smsc47m1_handle_resources(res->start, sio_data->type, |
500 | dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", | 581 | REQUEST, dev); |
501 | (unsigned long)res->start, | 582 | if (err < 0) |
502 | (unsigned long)res->end); | 583 | return err; |
503 | return -EBUSY; | ||
504 | } | ||
505 | 584 | ||
506 | if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { | 585 | if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { |
507 | err = -ENOMEM; | 586 | err = -ENOMEM; |
@@ -637,7 +716,7 @@ error_free: | |||
637 | platform_set_drvdata(pdev, NULL); | 716 | platform_set_drvdata(pdev, NULL); |
638 | kfree(data); | 717 | kfree(data); |
639 | error_release: | 718 | error_release: |
640 | release_region(res->start, SMSC_EXTENT); | 719 | smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev); |
641 | return err; | 720 | return err; |
642 | } | 721 | } |
643 | 722 | ||
@@ -650,7 +729,7 @@ static int __devexit smsc47m1_remove(struct platform_device *pdev) | |||
650 | sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); | 729 | sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); |
651 | 730 | ||
652 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 731 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
653 | release_region(res->start, SMSC_EXTENT); | 732 | smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev); |
654 | platform_set_drvdata(pdev, NULL); | 733 | platform_set_drvdata(pdev, NULL); |
655 | kfree(data); | 734 | kfree(data); |
656 | 735 | ||
@@ -717,7 +796,7 @@ static int __init smsc47m1_device_add(unsigned short address, | |||
717 | }; | 796 | }; |
718 | int err; | 797 | int err; |
719 | 798 | ||
720 | err = acpi_check_resource_conflict(&res); | 799 | err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL); |
721 | if (err) | 800 | if (err) |
722 | goto exit; | 801 | goto exit; |
723 | 802 | ||