[PARISC] Truncate overlapping PAT PDC reported ranges

Deal with overlapping LBA MMIO resources,

rp3440 PDC BUG: PDC reports lmmio range for the last rope that overlaps
with the CPU HPA. Console output was:

...
Found devices:
1. Storm Peak Fast at 0xfffffffffe798000 [152] { 0, 0x0, 0x889, 0x00004 }
2. Storm Peak Fast at 0xfffffffffe799000 [153] { 0, 0x0, 0x889, 0x00004 }
...
FAILED: lba_fixup_bus() request for lmmio_space
[fffffffff0000000/fffffffffecffffe]

Output is now:

LBA: Truncating lmmio_space [fffffffff0000000/fffffffffecffffe] to
[fffffffff0000000,fffffffffe797fff]

My only concern with this patch is how C8000 (PAT PDC) will report
elmmio ranges when a gfx card is installed. I'll have to test this
another day.

Signed-off-by: Grant Grundler <grundler@parisc-linux.org>
Signed-off-by: James Bottomley <jejb@parisc-linux.org>
Signed-off-by: Matthew Wilcox <willy@parisc-linux.org>
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>

authored by

Grant Grundler and committed by
Kyle McMartin
6ca45a24 110957f0

+90 -23
+90 -23
drivers/parisc/lba_pci.c
··· 695 695 } 696 696 } 697 697 } 698 - #else 699 - #define lba_claim_dev_resources(dev) 700 - #endif 701 698 699 + 700 + /* 701 + * truncate_pat_collision: Deal with overlaps or outright collisions 702 + * between PAT PDC reported ranges. 703 + * 704 + * Broken PA8800 firmware will report lmmio range that 705 + * overlaps with CPU HPA. Just truncate the lmmio range. 706 + * 707 + * BEWARE: conflicts with this lmmio range may be an 708 + * elmmio range which is pointing down another rope. 709 + * 710 + * FIXME: only deals with one collision per range...theoretically we 711 + * could have several. Supporting more than one collision will get messy. 712 + */ 713 + static unsigned long 714 + truncate_pat_collision(struct resource *root, struct resource *new) 715 + { 716 + unsigned long start = new->start; 717 + unsigned long end = new->end; 718 + struct resource *tmp = root->child; 719 + 720 + if (end <= start || start < root->start || !tmp) 721 + return 0; 722 + 723 + /* find first overlap */ 724 + while (tmp && tmp->end < start) 725 + tmp = tmp->sibling; 726 + 727 + /* no entries overlap */ 728 + if (!tmp) return 0; 729 + 730 + /* found one that starts behind the new one 731 + ** Don't need to do anything. 732 + */ 733 + if (tmp->start >= end) return 0; 734 + 735 + if (tmp->start <= start) { 736 + /* "front" of new one overlaps */ 737 + new->start = tmp->end + 1; 738 + 739 + if (tmp->end >= end) { 740 + /* AACCKK! totally overlaps! drop this range. */ 741 + return 1; 742 + } 743 + } 744 + 745 + if (tmp->end < end ) { 746 + /* "end" of new one overlaps */ 747 + new->end = tmp->start - 1; 748 + } 749 + 750 + printk(KERN_WARNING "LBA: Truncating lmmio_space [%lx/%lx] " 751 + "to [%lx,%lx]\n", 752 + start, end, 753 + new->start, new->end ); 754 + 755 + return 0; /* truncation successful */ 756 + } 757 + 758 + #else 759 + #define lba_claim_dev_resources(dev) do { } while (0) 760 + #define truncate_pat_collision(r,n) (0) 761 + #endif 702 762 703 763 /* 704 764 ** The algorithm is generic code. ··· 807 747 lba_dump_res(&ioport_resource, 2); 808 748 BUG(); 809 749 } 750 + /* advertize Host bridge resources to PCI bus */ 751 + bus->resource[0] = &(ldev->hba.io_space); 752 + i = 1; 810 753 811 754 if (ldev->hba.elmmio_space.start) { 812 755 err = request_resource(&iomem_resource, ··· 823 760 824 761 /* lba_dump_res(&iomem_resource, 2); */ 825 762 /* BUG(); */ 826 - } 763 + } else 764 + bus->resource[i++] = &(ldev->hba.elmmio_space); 827 765 } 828 766 829 - err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space)); 830 - if (err < 0) { 831 - /* FIXME overlaps with elmmio will fail here. 832 - * Need to prune (or disable) the distributed range. 833 - * 834 - * BEWARE: conflicts with this lmmio range may be 835 - * elmmio range which is pointing down another rope. 836 - */ 837 767 838 - printk("FAILED: lba_fixup_bus() request for " 768 + /* Overlaps with elmmio can (and should) fail here. 769 + * We will prune (or ignore) the distributed range. 770 + * 771 + * FIXME: SBA code should register all elmmio ranges first. 772 + * that would take care of elmmio ranges routed 773 + * to a different rope (already discovered) from 774 + * getting registered *after* LBA code has already 775 + * registered it's distributed lmmio range. 776 + */ 777 + if (truncate_pat_collision(&iomem_resource, 778 + &(ldev->hba.lmmio_space))) { 779 + 780 + printk(KERN_WARNING "LBA: lmmio_space [%lx/%lx] duplicate!\n", 781 + ldev->hba.lmmio_space.start, 782 + ldev->hba.lmmio_space.end); 783 + } else { 784 + err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space)); 785 + if (err < 0) { 786 + printk(KERN_ERR "FAILED: lba_fixup_bus() request for " 839 787 "lmmio_space [%lx/%lx]\n", 840 788 ldev->hba.lmmio_space.start, 841 789 ldev->hba.lmmio_space.end); 842 - /* lba_dump_res(&iomem_resource, 2); */ 790 + } else 791 + bus->resource[i++] = &(ldev->hba.lmmio_space); 843 792 } 844 793 845 794 #ifdef CONFIG_64BIT ··· 866 791 lba_dump_res(&iomem_resource, 2); 867 792 BUG(); 868 793 } 794 + bus->resource[i++] = &(ldev->hba.gmmio_space); 869 795 } 870 796 #endif 871 797 872 - /* advertize Host bridge resources to PCI bus */ 873 - bus->resource[0] = &(ldev->hba.io_space); 874 - bus->resource[1] = &(ldev->hba.lmmio_space); 875 - i=2; 876 - if (ldev->hba.elmmio_space.start) 877 - bus->resource[i++] = &(ldev->hba.elmmio_space); 878 - if (ldev->hba.gmmio_space.start) 879 - bus->resource[i++] = &(ldev->hba.gmmio_space); 880 - 881 798 } 882 799 883 800 list_for_each(ln, &bus->devices) {