diff options
Diffstat (limited to 'drivers')
| -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 | ||
