diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-10-13 20:55:31 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-10-20 14:01:47 -0400 |
commit | e9f82cb75096ae30658a72d473bf170bf4d3bb2e (patch) | |
tree | b01ec524b3dfe34b520044bdb8e5b13fbe39f4f7 /arch/powerpc/kernel | |
parent | f19aeb1f3638b7bb4ca21eb361f004fac2bfe259 (diff) |
powerpc/PCI: Add legacy PCI access via sysfs
This patch adds support for legacy_io and legacy_mem files in
bus class directories in sysfs for powerpc
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/pci-common.c | 136 |
1 files changed, 135 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 01ce8c38bae6..3815d84a1ef4 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c | |||
@@ -451,7 +451,8 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, | |||
451 | pci_dev_put(pdev); | 451 | pci_dev_put(pdev); |
452 | } | 452 | } |
453 | 453 | ||
454 | DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); | 454 | DBG("non-PCI map for %llx, prot: %lx\n", |
455 | (unsigned long long)offset, prot); | ||
455 | 456 | ||
456 | return __pgprot(prot); | 457 | return __pgprot(prot); |
457 | } | 458 | } |
@@ -490,6 +491,131 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | |||
490 | return ret; | 491 | return ret; |
491 | } | 492 | } |
492 | 493 | ||
494 | /* This provides legacy IO read access on a bus */ | ||
495 | int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size) | ||
496 | { | ||
497 | unsigned long offset; | ||
498 | struct pci_controller *hose = pci_bus_to_host(bus); | ||
499 | struct resource *rp = &hose->io_resource; | ||
500 | void __iomem *addr; | ||
501 | |||
502 | /* Check if port can be supported by that bus. We only check | ||
503 | * the ranges of the PHB though, not the bus itself as the rules | ||
504 | * for forwarding legacy cycles down bridges are not our problem | ||
505 | * here. So if the host bridge supports it, we do it. | ||
506 | */ | ||
507 | offset = (unsigned long)hose->io_base_virt - _IO_BASE; | ||
508 | offset += port; | ||
509 | |||
510 | if (!(rp->flags & IORESOURCE_IO)) | ||
511 | return -ENXIO; | ||
512 | if (offset < rp->start || (offset + size) > rp->end) | ||
513 | return -ENXIO; | ||
514 | addr = hose->io_base_virt + port; | ||
515 | |||
516 | switch(size) { | ||
517 | case 1: | ||
518 | *((u8 *)val) = in_8(addr); | ||
519 | return 1; | ||
520 | case 2: | ||
521 | if (port & 1) | ||
522 | return -EINVAL; | ||
523 | *((u16 *)val) = in_le16(addr); | ||
524 | return 2; | ||
525 | case 4: | ||
526 | if (port & 3) | ||
527 | return -EINVAL; | ||
528 | *((u32 *)val) = in_le32(addr); | ||
529 | return 4; | ||
530 | } | ||
531 | return -EINVAL; | ||
532 | } | ||
533 | |||
534 | /* This provides legacy IO write access on a bus */ | ||
535 | int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) | ||
536 | { | ||
537 | unsigned long offset; | ||
538 | struct pci_controller *hose = pci_bus_to_host(bus); | ||
539 | struct resource *rp = &hose->io_resource; | ||
540 | void __iomem *addr; | ||
541 | |||
542 | /* Check if port can be supported by that bus. We only check | ||
543 | * the ranges of the PHB though, not the bus itself as the rules | ||
544 | * for forwarding legacy cycles down bridges are not our problem | ||
545 | * here. So if the host bridge supports it, we do it. | ||
546 | */ | ||
547 | offset = (unsigned long)hose->io_base_virt - _IO_BASE; | ||
548 | offset += port; | ||
549 | |||
550 | if (!(rp->flags & IORESOURCE_IO)) | ||
551 | return -ENXIO; | ||
552 | if (offset < rp->start || (offset + size) > rp->end) | ||
553 | return -ENXIO; | ||
554 | addr = hose->io_base_virt + port; | ||
555 | |||
556 | /* WARNING: The generic code is idiotic. It gets passed a pointer | ||
557 | * to what can be a 1, 2 or 4 byte quantity and always reads that | ||
558 | * as a u32, which means that we have to correct the location of | ||
559 | * the data read within those 32 bits for size 1 and 2 | ||
560 | */ | ||
561 | switch(size) { | ||
562 | case 1: | ||
563 | out_8(addr, val >> 24); | ||
564 | return 1; | ||
565 | case 2: | ||
566 | if (port & 1) | ||
567 | return -EINVAL; | ||
568 | out_le16(addr, val >> 16); | ||
569 | return 2; | ||
570 | case 4: | ||
571 | if (port & 3) | ||
572 | return -EINVAL; | ||
573 | out_le32(addr, val); | ||
574 | return 4; | ||
575 | } | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | /* This provides legacy IO or memory mmap access on a bus */ | ||
580 | int pci_mmap_legacy_page_range(struct pci_bus *bus, | ||
581 | struct vm_area_struct *vma, | ||
582 | enum pci_mmap_state mmap_state) | ||
583 | { | ||
584 | struct pci_controller *hose = pci_bus_to_host(bus); | ||
585 | resource_size_t offset = | ||
586 | ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT; | ||
587 | resource_size_t size = vma->vm_end - vma->vm_start; | ||
588 | struct resource *rp; | ||
589 | |||
590 | pr_debug("pci_mmap_legacy_page_range(%04x:%02x, %s @%llx..%llx)\n", | ||
591 | pci_domain_nr(bus), bus->number, | ||
592 | mmap_state == pci_mmap_mem ? "MEM" : "IO", | ||
593 | (unsigned long long)offset, | ||
594 | (unsigned long long)(offset + size - 1)); | ||
595 | |||
596 | if (mmap_state == pci_mmap_mem) { | ||
597 | if ((offset + size) > hose->isa_mem_size) | ||
598 | return -ENXIO; | ||
599 | offset += hose->isa_mem_phys; | ||
600 | } else { | ||
601 | unsigned long io_offset = (unsigned long)hose->io_base_virt - _IO_BASE; | ||
602 | unsigned long roffset = offset + io_offset; | ||
603 | rp = &hose->io_resource; | ||
604 | if (!(rp->flags & IORESOURCE_IO)) | ||
605 | return -ENXIO; | ||
606 | if (roffset < rp->start || (roffset + size) > rp->end) | ||
607 | return -ENXIO; | ||
608 | offset += hose->io_base_phys; | ||
609 | } | ||
610 | pr_debug(" -> mapping phys %llx\n", (unsigned long long)offset); | ||
611 | |||
612 | vma->vm_pgoff = offset >> PAGE_SHIFT; | ||
613 | vma->vm_page_prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; | ||
614 | return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | ||
615 | vma->vm_end - vma->vm_start, | ||
616 | vma->vm_page_prot); | ||
617 | } | ||
618 | |||
493 | void pci_resource_to_user(const struct pci_dev *dev, int bar, | 619 | void pci_resource_to_user(const struct pci_dev *dev, int bar, |
494 | const struct resource *rsrc, | 620 | const struct resource *rsrc, |
495 | resource_size_t *start, resource_size_t *end) | 621 | resource_size_t *start, resource_size_t *end) |
@@ -592,6 +718,12 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, | |||
592 | cpu_addr = of_translate_address(dev, ranges + 3); | 718 | cpu_addr = of_translate_address(dev, ranges + 3); |
593 | size = of_read_number(ranges + pna + 3, 2); | 719 | size = of_read_number(ranges + pna + 3, 2); |
594 | ranges += np; | 720 | ranges += np; |
721 | |||
722 | /* If we failed translation or got a zero-sized region | ||
723 | * (some FW try to feed us with non sensical zero sized regions | ||
724 | * such as power3 which look like some kind of attempt at exposing | ||
725 | * the VGA memory hole) | ||
726 | */ | ||
595 | if (cpu_addr == OF_BAD_ADDR || size == 0) | 727 | if (cpu_addr == OF_BAD_ADDR || size == 0) |
596 | continue; | 728 | continue; |
597 | 729 | ||
@@ -665,6 +797,8 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, | |||
665 | isa_hole = memno; | 797 | isa_hole = memno; |
666 | if (primary || isa_mem_base == 0) | 798 | if (primary || isa_mem_base == 0) |
667 | isa_mem_base = cpu_addr; | 799 | isa_mem_base = cpu_addr; |
800 | hose->isa_mem_phys = cpu_addr; | ||
801 | hose->isa_mem_size = size; | ||
668 | } | 802 | } |
669 | 803 | ||
670 | /* We get the PCI/Mem offset from the first range or | 804 | /* We get the PCI/Mem offset from the first range or |