aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWitold Szczeponik <Witold.Szczeponik@gmx.net>2011-07-25 20:13:36 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-25 23:57:17 -0400
commit29df8d8f8702f0f53c1375015f09f04bc8d023c1 (patch)
tree32a7f6d0190bfdbe602bb969192010c91a0fbf6a
parente57ee01750c4954fd0b5e3c6109cd4b870880eb9 (diff)
pnpacpi: register disabled resources
When parsing PnP ACPI resource structures, it may happen that some of the resources are disabled (in which case "the size" of the resource equals zero). The current solution is to skip these resources completely - with the unfortunate side effect that they are not registered despite the fact that they exist, after all. (The downside of this approach is that these resources cannot be used as templates for setting the actual device's resources because they are missing from the template.) The kernel's APM implementation does not suffer from this problem and registers all resources regardless of "their size". This patch fixes a problem with (at least) the vintage IBM ThinkPad 600E (and most likely also with the 600, 600X, and 770X which have a very similar layout) where some of its PnP devices support options where either an IRQ, a DMA, or an IO port is disabled. Without this patch, the devices can not be configured using the "/sys/bus/pnp/devices/*/resources" interface. The manipulation of these resources is important because the 600E has very demanding requirements. For instance, the number of IRQs is not sufficient to support all devices of the 600E. Fortunately, some of the devices, like the sound card's MPU-401 UART, can be configured to not use any IRQ, hence freeing an IRQ for a device that requires one. (Still, the device's "ResourceTemplate" requires an IRQ resource descriptor which cannot be created if the resource has not been registered in the first place.) As an example, the dependent sets of the 600E's CSC0103 device (the MPU-401 UART) are listed, with the patch applied, as: Dependent: 00 - Priority preferred port 0x300-0x330, align 0xf, size 0x4, 16-bit address decoding irq <none> High-Edge Dependent: 01 - Priority acceptable port 0x300-0x330, align 0xf, size 0x4, 16-bit address decoding irq 5,7,2/9,10,11,15 High-Edge (The same result is obtained when PNPBIOS is used instead of PnP ACPI.) Without the patch, the IRQ resource in the preferred option is not listed at all: Dependent: 00 - Priority preferred port 0x300-0x330, align 0xf, size 0x4, 16-bit address decoding Dependent: 01 - Priority acceptable port 0x300-0x330, align 0xf, size 0x4, 16-bit address decoding irq 5,7,2/9,10,11,15 High-Edge And in fact, the 600E's DSDT lists the disabled IRQ as an option, as can be seen from the following excerpt from the DSDT: Name (_PRS, ResourceTemplate () { StartDependentFn (0x00, 0x00) { IO (Decode16, 0x0300, 0x0330, 0x10, 0x04) IRQNoFlags () {} } StartDependentFn (0x01, 0x00) { IO (Decode16, 0x0300, 0x0330, 0x10, 0x04) IRQNoFlags () {5,7,9,10,11,15} } EndDependentFn () }) With this patch applied, a user space program - or maybe even the kernel - can allocate all devices' resources optimally. For the 600E, this means to find optimal resources for (at least) the serial port, the parallel port, the infrared port, the MWAVE modem, the sound card, and the MPU-401 UART. The patch applies the idea to register disabled resources to all types of resources, not just to IRQs, DMAs, and IO ports. At the same time, it mimics the behavior of the "pnp_assign_xxx" functions from "drivers/pnp/manager.c" where resources with "no size" are considered disabled. No regressions were observed on hardware that does not require this patch. The patch is applied against 2.6.39. NB: The kernel's current PnP interface does not allow for disabling individual resources using the "/sys/bus/pnp/devices/$device/resources" file. Assuming this could be done, a device could be configured to use a disabled resource using a simple series of calls: echo disable > /sys/bus/pnp/devices/$device/resources echo clear > /sys/bus/pnp/devices/$device/resources echo set irq disabled > /sys/bus/pnp/devices/$device/resources echo fill > /sys/bus/pnp/devices/$device/resources echo activate > /sys/bus/pnp/devices/$device/resources This patch addresses only the parsing of PnP ACPI devices. ChangeLog (v1 -> v2): - extend patch description - fix typo in patch itself Signed-off-by: Witold Szczeponik <Witold.Szczeponik@gmx.net> Cc: Len Brown <lenb@kernel.org> Cc: Adam Belay <abelay@mit.edu> Cc: Bjorn Helgaas <bjorn.helgaas@hp.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Matthew Garrett <mjg@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c52
1 files changed, 27 insertions, 25 deletions
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
index 100e4d9372f1..4a07cd96f787 100644
--- a/drivers/pnp/pnpacpi/rsparser.c
+++ b/drivers/pnp/pnpacpi/rsparser.c
@@ -509,15 +509,15 @@ static __init void pnpacpi_parse_dma_option(struct pnp_dev *dev,
509 struct acpi_resource_dma *p) 509 struct acpi_resource_dma *p)
510{ 510{
511 int i; 511 int i;
512 unsigned char map = 0, flags; 512 unsigned char map = 0, flags = 0;
513 513
514 if (p->channel_count == 0) 514 if (p->channel_count == 0)
515 return; 515 flags |= IORESOURCE_DISABLED;
516 516
517 for (i = 0; i < p->channel_count; i++) 517 for (i = 0; i < p->channel_count; i++)
518 map |= 1 << p->channels[i]; 518 map |= 1 << p->channels[i];
519 519
520 flags = dma_flags(dev, p->type, p->bus_master, p->transfer); 520 flags |= dma_flags(dev, p->type, p->bus_master, p->transfer);
521 pnp_register_dma_resource(dev, option_flags, map, flags); 521 pnp_register_dma_resource(dev, option_flags, map, flags);
522} 522}
523 523
@@ -527,17 +527,17 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev,
527{ 527{
528 int i; 528 int i;
529 pnp_irq_mask_t map; 529 pnp_irq_mask_t map;
530 unsigned char flags; 530 unsigned char flags = 0;
531 531
532 if (p->interrupt_count == 0) 532 if (p->interrupt_count == 0)
533 return; 533 flags |= IORESOURCE_DISABLED;
534 534
535 bitmap_zero(map.bits, PNP_IRQ_NR); 535 bitmap_zero(map.bits, PNP_IRQ_NR);
536 for (i = 0; i < p->interrupt_count; i++) 536 for (i = 0; i < p->interrupt_count; i++)
537 if (p->interrupts[i]) 537 if (p->interrupts[i])
538 __set_bit(p->interrupts[i], map.bits); 538 __set_bit(p->interrupts[i], map.bits);
539 539
540 flags = irq_flags(p->triggering, p->polarity, p->sharable); 540 flags |= irq_flags(p->triggering, p->polarity, p->sharable);
541 pnp_register_irq_resource(dev, option_flags, &map, flags); 541 pnp_register_irq_resource(dev, option_flags, &map, flags);
542} 542}
543 543
@@ -547,10 +547,10 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
547{ 547{
548 int i; 548 int i;
549 pnp_irq_mask_t map; 549 pnp_irq_mask_t map;
550 unsigned char flags; 550 unsigned char flags = 0;
551 551
552 if (p->interrupt_count == 0) 552 if (p->interrupt_count == 0)
553 return; 553 flags |= IORESOURCE_DISABLED;
554 554
555 bitmap_zero(map.bits, PNP_IRQ_NR); 555 bitmap_zero(map.bits, PNP_IRQ_NR);
556 for (i = 0; i < p->interrupt_count; i++) { 556 for (i = 0; i < p->interrupt_count; i++) {
@@ -564,7 +564,7 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
564 } 564 }
565 } 565 }
566 566
567 flags = irq_flags(p->triggering, p->polarity, p->sharable); 567 flags |= irq_flags(p->triggering, p->polarity, p->sharable);
568 pnp_register_irq_resource(dev, option_flags, &map, flags); 568 pnp_register_irq_resource(dev, option_flags, &map, flags);
569} 569}
570 570
@@ -575,10 +575,10 @@ static __init void pnpacpi_parse_port_option(struct pnp_dev *dev,
575 unsigned char flags = 0; 575 unsigned char flags = 0;
576 576
577 if (io->address_length == 0) 577 if (io->address_length == 0)
578 return; 578 flags |= IORESOURCE_DISABLED;
579 579
580 if (io->io_decode == ACPI_DECODE_16) 580 if (io->io_decode == ACPI_DECODE_16)
581 flags = IORESOURCE_IO_16BIT_ADDR; 581 flags |= IORESOURCE_IO_16BIT_ADDR;
582 pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum, 582 pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum,
583 io->alignment, io->address_length, flags); 583 io->alignment, io->address_length, flags);
584} 584}
@@ -587,11 +587,13 @@ static __init void pnpacpi_parse_fixed_port_option(struct pnp_dev *dev,
587 unsigned int option_flags, 587 unsigned int option_flags,
588 struct acpi_resource_fixed_io *io) 588 struct acpi_resource_fixed_io *io)
589{ 589{
590 unsigned char flags = 0;
591
590 if (io->address_length == 0) 592 if (io->address_length == 0)
591 return; 593 flags |= IORESOURCE_DISABLED;
592 594
593 pnp_register_port_resource(dev, option_flags, io->address, io->address, 595 pnp_register_port_resource(dev, option_flags, io->address, io->address,
594 0, io->address_length, IORESOURCE_IO_FIXED); 596 0, io->address_length, flags | IORESOURCE_IO_FIXED);
595} 597}
596 598
597static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev, 599static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
@@ -601,10 +603,10 @@ static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
601 unsigned char flags = 0; 603 unsigned char flags = 0;
602 604
603 if (p->address_length == 0) 605 if (p->address_length == 0)
604 return; 606 flags |= IORESOURCE_DISABLED;
605 607
606 if (p->write_protect == ACPI_READ_WRITE_MEMORY) 608 if (p->write_protect == ACPI_READ_WRITE_MEMORY)
607 flags = IORESOURCE_MEM_WRITEABLE; 609 flags |= IORESOURCE_MEM_WRITEABLE;
608 pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum, 610 pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
609 p->alignment, p->address_length, flags); 611 p->alignment, p->address_length, flags);
610} 612}
@@ -616,10 +618,10 @@ static __init void pnpacpi_parse_mem32_option(struct pnp_dev *dev,
616 unsigned char flags = 0; 618 unsigned char flags = 0;
617 619
618 if (p->address_length == 0) 620 if (p->address_length == 0)
619 return; 621 flags |= IORESOURCE_DISABLED;
620 622
621 if (p->write_protect == ACPI_READ_WRITE_MEMORY) 623 if (p->write_protect == ACPI_READ_WRITE_MEMORY)
622 flags = IORESOURCE_MEM_WRITEABLE; 624 flags |= IORESOURCE_MEM_WRITEABLE;
623 pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum, 625 pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
624 p->alignment, p->address_length, flags); 626 p->alignment, p->address_length, flags);
625} 627}
@@ -631,10 +633,10 @@ static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_dev *dev,
631 unsigned char flags = 0; 633 unsigned char flags = 0;
632 634
633 if (p->address_length == 0) 635 if (p->address_length == 0)
634 return; 636 flags |= IORESOURCE_DISABLED;
635 637
636 if (p->write_protect == ACPI_READ_WRITE_MEMORY) 638 if (p->write_protect == ACPI_READ_WRITE_MEMORY)
637 flags = IORESOURCE_MEM_WRITEABLE; 639 flags |= IORESOURCE_MEM_WRITEABLE;
638 pnp_register_mem_resource(dev, option_flags, p->address, p->address, 640 pnp_register_mem_resource(dev, option_flags, p->address, p->address,
639 0, p->address_length, flags); 641 0, p->address_length, flags);
640} 642}
@@ -655,18 +657,18 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev,
655 } 657 }
656 658
657 if (p->address_length == 0) 659 if (p->address_length == 0)
658 return; 660 flags |= IORESOURCE_DISABLED;
659 661
660 if (p->resource_type == ACPI_MEMORY_RANGE) { 662 if (p->resource_type == ACPI_MEMORY_RANGE) {
661 if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) 663 if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
662 flags = IORESOURCE_MEM_WRITEABLE; 664 flags |= IORESOURCE_MEM_WRITEABLE;
663 pnp_register_mem_resource(dev, option_flags, p->minimum, 665 pnp_register_mem_resource(dev, option_flags, p->minimum,
664 p->minimum, 0, p->address_length, 666 p->minimum, 0, p->address_length,
665 flags); 667 flags);
666 } else if (p->resource_type == ACPI_IO_RANGE) 668 } else if (p->resource_type == ACPI_IO_RANGE)
667 pnp_register_port_resource(dev, option_flags, p->minimum, 669 pnp_register_port_resource(dev, option_flags, p->minimum,
668 p->minimum, 0, p->address_length, 670 p->minimum, 0, p->address_length,
669 IORESOURCE_IO_FIXED); 671 flags | IORESOURCE_IO_FIXED);
670} 672}
671 673
672static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev, 674static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
@@ -677,18 +679,18 @@ static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
677 unsigned char flags = 0; 679 unsigned char flags = 0;
678 680
679 if (p->address_length == 0) 681 if (p->address_length == 0)
680 return; 682 flags |= IORESOURCE_DISABLED;
681 683
682 if (p->resource_type == ACPI_MEMORY_RANGE) { 684 if (p->resource_type == ACPI_MEMORY_RANGE) {
683 if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) 685 if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
684 flags = IORESOURCE_MEM_WRITEABLE; 686 flags |= IORESOURCE_MEM_WRITEABLE;
685 pnp_register_mem_resource(dev, option_flags, p->minimum, 687 pnp_register_mem_resource(dev, option_flags, p->minimum,
686 p->minimum, 0, p->address_length, 688 p->minimum, 0, p->address_length,
687 flags); 689 flags);
688 } else if (p->resource_type == ACPI_IO_RANGE) 690 } else if (p->resource_type == ACPI_IO_RANGE)
689 pnp_register_port_resource(dev, option_flags, p->minimum, 691 pnp_register_port_resource(dev, option_flags, p->minimum,
690 p->minimum, 0, p->address_length, 692 p->minimum, 0, p->address_length,
691 IORESOURCE_IO_FIXED); 693 flags | IORESOURCE_IO_FIXED);
692} 694}
693 695
694struct acpipnp_parse_option_s { 696struct acpipnp_parse_option_s {