aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2009-12-16 15:38:26 -0500
committerJean Delvare <khali@linux-fr.org>2009-12-16 15:38:26 -0500
commita0e92d70f35b5fd7da8ec2160cda78b98e2113bc (patch)
tree81ace7f9afc762a016da2de26e62f6bea153cdf8 /drivers
parent3c57e89b467d1db6fda74d5c97112c8b9466dd97 (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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/smsc47m1.c99
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 */
496static 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
484static int __devinit smsc47m1_probe(struct platform_device *pdev) 565static 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);
639error_release: 718error_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