| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Copyright (C) 2006 Olof Johansson <olof@lixom.net> |
|---|
| 8 | 9 | * |
|---|
| 9 | 10 | * Dynamic DMA mapping support, pSeries-specific parts, both SMP and LPAR. |
|---|
| 10 | | - * |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 13 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 14 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 15 | | - * (at your option) any later version. |
|---|
| 16 | | - * |
|---|
| 17 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 18 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 19 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 20 | | - * GNU General Public License for more details. |
|---|
| 21 | | - * |
|---|
| 22 | | - * You should have received a copy of the GNU General Public License |
|---|
| 23 | | - * along with this program; if not, write to the Free Software |
|---|
| 24 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 25 | 11 | */ |
|---|
| 26 | 12 | |
|---|
| 27 | 13 | #include <linux/init.h> |
|---|
| .. | .. |
|---|
| 53 | 39 | |
|---|
| 54 | 40 | #include "pseries.h" |
|---|
| 55 | 41 | |
|---|
| 42 | +enum { |
|---|
| 43 | + DDW_QUERY_PE_DMA_WIN = 0, |
|---|
| 44 | + DDW_CREATE_PE_DMA_WIN = 1, |
|---|
| 45 | + DDW_REMOVE_PE_DMA_WIN = 2, |
|---|
| 46 | + |
|---|
| 47 | + DDW_APPLICABLE_SIZE |
|---|
| 48 | +}; |
|---|
| 49 | + |
|---|
| 50 | +enum { |
|---|
| 51 | + DDW_EXT_SIZE = 0, |
|---|
| 52 | + DDW_EXT_RESET_DMA_WIN = 1, |
|---|
| 53 | + DDW_EXT_QUERY_OUT_SIZE = 2 |
|---|
| 54 | +}; |
|---|
| 55 | + |
|---|
| 56 | 56 | static struct iommu_table_group *iommu_pseries_alloc_group(int node) |
|---|
| 57 | 57 | { |
|---|
| 58 | 58 | struct iommu_table_group *table_group; |
|---|
| 59 | 59 | struct iommu_table *tbl; |
|---|
| 60 | | - struct iommu_table_group_link *tgl; |
|---|
| 61 | 60 | |
|---|
| 62 | 61 | table_group = kzalloc_node(sizeof(struct iommu_table_group), GFP_KERNEL, |
|---|
| 63 | 62 | node); |
|---|
| .. | .. |
|---|
| 68 | 67 | if (!tbl) |
|---|
| 69 | 68 | goto free_group; |
|---|
| 70 | 69 | |
|---|
| 71 | | - tgl = kzalloc_node(sizeof(struct iommu_table_group_link), GFP_KERNEL, |
|---|
| 72 | | - node); |
|---|
| 73 | | - if (!tgl) |
|---|
| 74 | | - goto free_table; |
|---|
| 75 | | - |
|---|
| 76 | 70 | INIT_LIST_HEAD_RCU(&tbl->it_group_list); |
|---|
| 77 | 71 | kref_init(&tbl->it_kref); |
|---|
| 78 | | - tgl->table_group = table_group; |
|---|
| 79 | | - list_add_rcu(&tgl->next, &tbl->it_group_list); |
|---|
| 80 | 72 | |
|---|
| 81 | 73 | table_group->tables[0] = tbl; |
|---|
| 82 | 74 | |
|---|
| 83 | 75 | return table_group; |
|---|
| 84 | 76 | |
|---|
| 85 | | -free_table: |
|---|
| 86 | | - kfree(tbl); |
|---|
| 87 | 77 | free_group: |
|---|
| 88 | 78 | kfree(table_group); |
|---|
| 89 | 79 | return NULL; |
|---|
| .. | .. |
|---|
| 93 | 83 | const char *node_name) |
|---|
| 94 | 84 | { |
|---|
| 95 | 85 | struct iommu_table *tbl; |
|---|
| 96 | | -#ifdef CONFIG_IOMMU_API |
|---|
| 97 | | - struct iommu_table_group_link *tgl; |
|---|
| 98 | | -#endif |
|---|
| 99 | 86 | |
|---|
| 100 | 87 | if (!table_group) |
|---|
| 101 | 88 | return; |
|---|
| 102 | 89 | |
|---|
| 103 | 90 | tbl = table_group->tables[0]; |
|---|
| 104 | 91 | #ifdef CONFIG_IOMMU_API |
|---|
| 105 | | - tgl = list_first_entry_or_null(&tbl->it_group_list, |
|---|
| 106 | | - struct iommu_table_group_link, next); |
|---|
| 107 | | - |
|---|
| 108 | | - WARN_ON_ONCE(!tgl); |
|---|
| 109 | | - if (tgl) { |
|---|
| 110 | | - list_del_rcu(&tgl->next); |
|---|
| 111 | | - kfree(tgl); |
|---|
| 112 | | - } |
|---|
| 113 | 92 | if (table_group->group) { |
|---|
| 114 | 93 | iommu_group_put(table_group->group); |
|---|
| 115 | 94 | BUG_ON(table_group->group); |
|---|
| .. | .. |
|---|
| 126 | 105 | unsigned long attrs) |
|---|
| 127 | 106 | { |
|---|
| 128 | 107 | u64 proto_tce; |
|---|
| 129 | | - __be64 *tcep, *tces; |
|---|
| 108 | + __be64 *tcep; |
|---|
| 130 | 109 | u64 rpn; |
|---|
| 131 | 110 | |
|---|
| 132 | 111 | proto_tce = TCE_PCI_READ; // Read allowed |
|---|
| .. | .. |
|---|
| 134 | 113 | if (direction != DMA_TO_DEVICE) |
|---|
| 135 | 114 | proto_tce |= TCE_PCI_WRITE; |
|---|
| 136 | 115 | |
|---|
| 137 | | - tces = tcep = ((__be64 *)tbl->it_base) + index; |
|---|
| 116 | + tcep = ((__be64 *)tbl->it_base) + index; |
|---|
| 138 | 117 | |
|---|
| 139 | 118 | while (npages--) { |
|---|
| 140 | 119 | /* can't move this out since we might cross MEMBLOCK boundary */ |
|---|
| .. | .. |
|---|
| 150 | 129 | |
|---|
| 151 | 130 | static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) |
|---|
| 152 | 131 | { |
|---|
| 153 | | - __be64 *tcep, *tces; |
|---|
| 132 | + __be64 *tcep; |
|---|
| 154 | 133 | |
|---|
| 155 | | - tces = tcep = ((__be64 *)tbl->it_base) + index; |
|---|
| 134 | + tcep = ((__be64 *)tbl->it_base) + index; |
|---|
| 156 | 135 | |
|---|
| 157 | 136 | while (npages--) |
|---|
| 158 | 137 | *(tcep++) = 0; |
|---|
| .. | .. |
|---|
| 227 | 206 | int ret = 0; |
|---|
| 228 | 207 | unsigned long flags; |
|---|
| 229 | 208 | |
|---|
| 230 | | - if ((npages == 1) || !firmware_has_feature(FW_FEATURE_MULTITCE)) { |
|---|
| 209 | + if ((npages == 1) || !firmware_has_feature(FW_FEATURE_PUT_TCE_IND)) { |
|---|
| 231 | 210 | return tce_build_pSeriesLP(tbl->it_index, tcenum, |
|---|
| 232 | 211 | tbl->it_page_shift, npages, uaddr, |
|---|
| 233 | 212 | direction, attrs); |
|---|
| .. | .. |
|---|
| 321 | 300 | { |
|---|
| 322 | 301 | u64 rc; |
|---|
| 323 | 302 | |
|---|
| 324 | | - if (!firmware_has_feature(FW_FEATURE_MULTITCE)) |
|---|
| 303 | + if (!firmware_has_feature(FW_FEATURE_STUFF_TCE)) |
|---|
| 325 | 304 | return tce_free_pSeriesLP(tbl->it_index, tcenum, npages); |
|---|
| 326 | 305 | |
|---|
| 327 | 306 | rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages); |
|---|
| .. | .. |
|---|
| 369 | 348 | /* Dynamic DMA Window support */ |
|---|
| 370 | 349 | struct ddw_query_response { |
|---|
| 371 | 350 | u32 windows_available; |
|---|
| 372 | | - u32 largest_available_block; |
|---|
| 351 | + u64 largest_available_block; |
|---|
| 373 | 352 | u32 page_size; |
|---|
| 374 | 353 | u32 migration_capable; |
|---|
| 375 | 354 | }; |
|---|
| .. | .. |
|---|
| 437 | 416 | u64 rc = 0; |
|---|
| 438 | 417 | long l, limit; |
|---|
| 439 | 418 | |
|---|
| 440 | | - if (!firmware_has_feature(FW_FEATURE_MULTITCE)) { |
|---|
| 419 | + if (!firmware_has_feature(FW_FEATURE_PUT_TCE_IND)) { |
|---|
| 441 | 420 | unsigned long tceshift = be32_to_cpu(maprange->tce_shift); |
|---|
| 442 | 421 | unsigned long dmastart = (start_pfn << PAGE_SHIFT) + |
|---|
| 443 | 422 | be64_to_cpu(maprange->dma_base); |
|---|
| .. | .. |
|---|
| 659 | 638 | |
|---|
| 660 | 639 | iommu_table_setparms(pci->phb, dn, tbl); |
|---|
| 661 | 640 | tbl->it_ops = &iommu_table_pseries_ops; |
|---|
| 662 | | - iommu_init_table(tbl, pci->phb->node); |
|---|
| 663 | | - iommu_register_group(pci->table_group, pci_domain_nr(bus), 0); |
|---|
| 641 | + iommu_init_table(tbl, pci->phb->node, 0, 0); |
|---|
| 664 | 642 | |
|---|
| 665 | 643 | /* Divide the rest (1.75GB) among the children */ |
|---|
| 666 | 644 | pci->phb->dma_window_size = 0x80000000ul; |
|---|
| .. | .. |
|---|
| 672 | 650 | |
|---|
| 673 | 651 | #ifdef CONFIG_IOMMU_API |
|---|
| 674 | 652 | static int tce_exchange_pseries(struct iommu_table *tbl, long index, unsigned |
|---|
| 675 | | - long *tce, enum dma_data_direction *direction) |
|---|
| 653 | + long *tce, enum dma_data_direction *direction, |
|---|
| 654 | + bool realmode) |
|---|
| 676 | 655 | { |
|---|
| 677 | 656 | long rc; |
|---|
| 678 | 657 | unsigned long ioba = (unsigned long) index << tbl->it_page_shift; |
|---|
| .. | .. |
|---|
| 700 | 679 | struct iommu_table_ops iommu_table_lpar_multi_ops = { |
|---|
| 701 | 680 | .set = tce_buildmulti_pSeriesLP, |
|---|
| 702 | 681 | #ifdef CONFIG_IOMMU_API |
|---|
| 703 | | - .exchange = tce_exchange_pseries, |
|---|
| 682 | + .xchg_no_kill = tce_exchange_pseries, |
|---|
| 704 | 683 | #endif |
|---|
| 705 | 684 | .clear = tce_freemulti_pSeriesLP, |
|---|
| 706 | 685 | .get = tce_get_pSeriesLP |
|---|
| .. | .. |
|---|
| 741 | 720 | iommu_table_setparms_lpar(ppci->phb, pdn, tbl, |
|---|
| 742 | 721 | ppci->table_group, dma_window); |
|---|
| 743 | 722 | tbl->it_ops = &iommu_table_lpar_multi_ops; |
|---|
| 744 | | - iommu_init_table(tbl, ppci->phb->node); |
|---|
| 723 | + iommu_init_table(tbl, ppci->phb->node, 0, 0); |
|---|
| 745 | 724 | iommu_register_group(ppci->table_group, |
|---|
| 746 | 725 | pci_domain_nr(bus), 0); |
|---|
| 747 | 726 | pr_debug(" created table: %p\n", ppci->table_group); |
|---|
| .. | .. |
|---|
| 770 | 749 | tbl = PCI_DN(dn)->table_group->tables[0]; |
|---|
| 771 | 750 | iommu_table_setparms(phb, dn, tbl); |
|---|
| 772 | 751 | tbl->it_ops = &iommu_table_pseries_ops; |
|---|
| 773 | | - iommu_init_table(tbl, phb->node); |
|---|
| 774 | | - iommu_register_group(PCI_DN(dn)->table_group, |
|---|
| 775 | | - pci_domain_nr(phb->bus), 0); |
|---|
| 752 | + iommu_init_table(tbl, phb->node, 0, 0); |
|---|
| 776 | 753 | set_iommu_table_base(&dev->dev, tbl); |
|---|
| 777 | | - iommu_add_device(&dev->dev); |
|---|
| 778 | 754 | return; |
|---|
| 779 | 755 | } |
|---|
| 780 | 756 | |
|---|
| .. | .. |
|---|
| 785 | 761 | while (dn && PCI_DN(dn) && PCI_DN(dn)->table_group == NULL) |
|---|
| 786 | 762 | dn = dn->parent; |
|---|
| 787 | 763 | |
|---|
| 788 | | - if (dn && PCI_DN(dn)) { |
|---|
| 764 | + if (dn && PCI_DN(dn)) |
|---|
| 789 | 765 | set_iommu_table_base(&dev->dev, |
|---|
| 790 | 766 | PCI_DN(dn)->table_group->tables[0]); |
|---|
| 791 | | - iommu_add_device(&dev->dev); |
|---|
| 792 | | - } else |
|---|
| 767 | + else |
|---|
| 793 | 768 | printk(KERN_WARNING "iommu: Device %s has no iommu table\n", |
|---|
| 794 | 769 | pci_name(dev)); |
|---|
| 795 | 770 | } |
|---|
| .. | .. |
|---|
| 806 | 781 | |
|---|
| 807 | 782 | early_param("disable_ddw", disable_ddw_setup); |
|---|
| 808 | 783 | |
|---|
| 809 | | -static void remove_ddw(struct device_node *np, bool remove_prop) |
|---|
| 784 | +static void remove_dma_window(struct device_node *np, u32 *ddw_avail, |
|---|
| 785 | + struct property *win) |
|---|
| 810 | 786 | { |
|---|
| 811 | 787 | struct dynamic_dma_window_prop *dwp; |
|---|
| 812 | | - struct property *win64; |
|---|
| 813 | | - u32 ddw_avail[3]; |
|---|
| 814 | 788 | u64 liobn; |
|---|
| 815 | | - int ret = 0; |
|---|
| 789 | + int ret; |
|---|
| 816 | 790 | |
|---|
| 817 | | - ret = of_property_read_u32_array(np, "ibm,ddw-applicable", |
|---|
| 818 | | - &ddw_avail[0], 3); |
|---|
| 819 | | - |
|---|
| 820 | | - win64 = of_find_property(np, DIRECT64_PROPNAME, NULL); |
|---|
| 821 | | - if (!win64) |
|---|
| 822 | | - return; |
|---|
| 823 | | - |
|---|
| 824 | | - if (ret || win64->length < sizeof(*dwp)) |
|---|
| 825 | | - goto delprop; |
|---|
| 826 | | - |
|---|
| 827 | | - dwp = win64->value; |
|---|
| 791 | + dwp = win->value; |
|---|
| 828 | 792 | liobn = (u64)be32_to_cpu(dwp->liobn); |
|---|
| 829 | 793 | |
|---|
| 830 | 794 | /* clear the whole window, note the arg is in kernel pages */ |
|---|
| .. | .. |
|---|
| 837 | 801 | pr_debug("%pOF successfully cleared tces in window.\n", |
|---|
| 838 | 802 | np); |
|---|
| 839 | 803 | |
|---|
| 840 | | - ret = rtas_call(ddw_avail[2], 1, 1, NULL, liobn); |
|---|
| 804 | + ret = rtas_call(ddw_avail[DDW_REMOVE_PE_DMA_WIN], 1, 1, NULL, liobn); |
|---|
| 841 | 805 | if (ret) |
|---|
| 842 | 806 | pr_warn("%pOF: failed to remove direct window: rtas returned " |
|---|
| 843 | 807 | "%d to ibm,remove-pe-dma-window(%x) %llx\n", |
|---|
| 844 | | - np, ret, ddw_avail[2], liobn); |
|---|
| 808 | + np, ret, ddw_avail[DDW_REMOVE_PE_DMA_WIN], liobn); |
|---|
| 845 | 809 | else |
|---|
| 846 | 810 | pr_debug("%pOF: successfully removed direct window: rtas returned " |
|---|
| 847 | 811 | "%d to ibm,remove-pe-dma-window(%x) %llx\n", |
|---|
| 848 | | - np, ret, ddw_avail[2], liobn); |
|---|
| 812 | + np, ret, ddw_avail[DDW_REMOVE_PE_DMA_WIN], liobn); |
|---|
| 813 | +} |
|---|
| 849 | 814 | |
|---|
| 850 | | -delprop: |
|---|
| 851 | | - if (remove_prop) |
|---|
| 852 | | - ret = of_remove_property(np, win64); |
|---|
| 815 | +static void remove_ddw(struct device_node *np, bool remove_prop) |
|---|
| 816 | +{ |
|---|
| 817 | + struct property *win; |
|---|
| 818 | + u32 ddw_avail[DDW_APPLICABLE_SIZE]; |
|---|
| 819 | + int ret = 0; |
|---|
| 820 | + |
|---|
| 821 | + ret = of_property_read_u32_array(np, "ibm,ddw-applicable", |
|---|
| 822 | + &ddw_avail[0], DDW_APPLICABLE_SIZE); |
|---|
| 823 | + if (ret) |
|---|
| 824 | + return; |
|---|
| 825 | + |
|---|
| 826 | + win = of_find_property(np, DIRECT64_PROPNAME, NULL); |
|---|
| 827 | + if (!win) |
|---|
| 828 | + return; |
|---|
| 829 | + |
|---|
| 830 | + if (win->length >= sizeof(struct dynamic_dma_window_prop)) |
|---|
| 831 | + remove_dma_window(np, ddw_avail, win); |
|---|
| 832 | + |
|---|
| 833 | + if (!remove_prop) |
|---|
| 834 | + return; |
|---|
| 835 | + |
|---|
| 836 | + ret = of_remove_property(np, win); |
|---|
| 853 | 837 | if (ret) |
|---|
| 854 | 838 | pr_warn("%pOF: failed to remove direct window property: %d\n", |
|---|
| 855 | 839 | np, ret); |
|---|
| .. | .. |
|---|
| 908 | 892 | } |
|---|
| 909 | 893 | machine_arch_initcall(pseries, find_existing_ddw_windows); |
|---|
| 910 | 894 | |
|---|
| 895 | +/** |
|---|
| 896 | + * ddw_read_ext - Get the value of an DDW extension |
|---|
| 897 | + * @np: device node from which the extension value is to be read. |
|---|
| 898 | + * @extnum: index number of the extension. |
|---|
| 899 | + * @value: pointer to return value, modified when extension is available. |
|---|
| 900 | + * |
|---|
| 901 | + * Checks if "ibm,ddw-extensions" exists for this node, and get the value |
|---|
| 902 | + * on index 'extnum'. |
|---|
| 903 | + * It can be used only to check if a property exists, passing value == NULL. |
|---|
| 904 | + * |
|---|
| 905 | + * Returns: |
|---|
| 906 | + * 0 if extension successfully read |
|---|
| 907 | + * -EINVAL if the "ibm,ddw-extensions" does not exist, |
|---|
| 908 | + * -ENODATA if "ibm,ddw-extensions" does not have a value, and |
|---|
| 909 | + * -EOVERFLOW if "ibm,ddw-extensions" does not contain this extension. |
|---|
| 910 | + */ |
|---|
| 911 | +static inline int ddw_read_ext(const struct device_node *np, int extnum, |
|---|
| 912 | + u32 *value) |
|---|
| 913 | +{ |
|---|
| 914 | + static const char propname[] = "ibm,ddw-extensions"; |
|---|
| 915 | + u32 count; |
|---|
| 916 | + int ret; |
|---|
| 917 | + |
|---|
| 918 | + ret = of_property_read_u32_index(np, propname, DDW_EXT_SIZE, &count); |
|---|
| 919 | + if (ret) |
|---|
| 920 | + return ret; |
|---|
| 921 | + |
|---|
| 922 | + if (count < extnum) |
|---|
| 923 | + return -EOVERFLOW; |
|---|
| 924 | + |
|---|
| 925 | + if (!value) |
|---|
| 926 | + value = &count; |
|---|
| 927 | + |
|---|
| 928 | + return of_property_read_u32_index(np, propname, extnum, value); |
|---|
| 929 | +} |
|---|
| 930 | + |
|---|
| 911 | 931 | static int query_ddw(struct pci_dev *dev, const u32 *ddw_avail, |
|---|
| 912 | | - struct ddw_query_response *query) |
|---|
| 932 | + struct ddw_query_response *query, |
|---|
| 933 | + struct device_node *parent) |
|---|
| 913 | 934 | { |
|---|
| 914 | 935 | struct device_node *dn; |
|---|
| 915 | 936 | struct pci_dn *pdn; |
|---|
| 916 | | - u32 cfg_addr; |
|---|
| 937 | + u32 cfg_addr, ext_query, query_out[5]; |
|---|
| 917 | 938 | u64 buid; |
|---|
| 918 | | - int ret; |
|---|
| 939 | + int ret, out_sz; |
|---|
| 940 | + |
|---|
| 941 | + /* |
|---|
| 942 | + * From LoPAR level 2.8, "ibm,ddw-extensions" index 3 can rule how many |
|---|
| 943 | + * output parameters ibm,query-pe-dma-windows will have, ranging from |
|---|
| 944 | + * 5 to 6. |
|---|
| 945 | + */ |
|---|
| 946 | + ret = ddw_read_ext(parent, DDW_EXT_QUERY_OUT_SIZE, &ext_query); |
|---|
| 947 | + if (!ret && ext_query == 1) |
|---|
| 948 | + out_sz = 6; |
|---|
| 949 | + else |
|---|
| 950 | + out_sz = 5; |
|---|
| 919 | 951 | |
|---|
| 920 | 952 | /* |
|---|
| 921 | 953 | * Get the config address and phb buid of the PE window. |
|---|
| .. | .. |
|---|
| 928 | 960 | buid = pdn->phb->buid; |
|---|
| 929 | 961 | cfg_addr = ((pdn->busno << 16) | (pdn->devfn << 8)); |
|---|
| 930 | 962 | |
|---|
| 931 | | - ret = rtas_call(ddw_avail[0], 3, 5, (u32 *)query, |
|---|
| 932 | | - cfg_addr, BUID_HI(buid), BUID_LO(buid)); |
|---|
| 933 | | - dev_info(&dev->dev, "ibm,query-pe-dma-windows(%x) %x %x %x" |
|---|
| 934 | | - " returned %d\n", ddw_avail[0], cfg_addr, BUID_HI(buid), |
|---|
| 935 | | - BUID_LO(buid), ret); |
|---|
| 963 | + ret = rtas_call(ddw_avail[DDW_QUERY_PE_DMA_WIN], 3, out_sz, query_out, |
|---|
| 964 | + cfg_addr, BUID_HI(buid), BUID_LO(buid)); |
|---|
| 965 | + dev_info(&dev->dev, "ibm,query-pe-dma-windows(%x) %x %x %x returned %d\n", |
|---|
| 966 | + ddw_avail[DDW_QUERY_PE_DMA_WIN], cfg_addr, BUID_HI(buid), |
|---|
| 967 | + BUID_LO(buid), ret); |
|---|
| 968 | + |
|---|
| 969 | + switch (out_sz) { |
|---|
| 970 | + case 5: |
|---|
| 971 | + query->windows_available = query_out[0]; |
|---|
| 972 | + query->largest_available_block = query_out[1]; |
|---|
| 973 | + query->page_size = query_out[2]; |
|---|
| 974 | + query->migration_capable = query_out[3]; |
|---|
| 975 | + break; |
|---|
| 976 | + case 6: |
|---|
| 977 | + query->windows_available = query_out[0]; |
|---|
| 978 | + query->largest_available_block = ((u64)query_out[1] << 32) | |
|---|
| 979 | + query_out[2]; |
|---|
| 980 | + query->page_size = query_out[3]; |
|---|
| 981 | + query->migration_capable = query_out[4]; |
|---|
| 982 | + break; |
|---|
| 983 | + } |
|---|
| 984 | + |
|---|
| 936 | 985 | return ret; |
|---|
| 937 | 986 | } |
|---|
| 938 | 987 | |
|---|
| .. | .. |
|---|
| 959 | 1008 | |
|---|
| 960 | 1009 | do { |
|---|
| 961 | 1010 | /* extra outputs are LIOBN and dma-addr (hi, lo) */ |
|---|
| 962 | | - ret = rtas_call(ddw_avail[1], 5, 4, (u32 *)create, |
|---|
| 963 | | - cfg_addr, BUID_HI(buid), BUID_LO(buid), |
|---|
| 964 | | - page_shift, window_shift); |
|---|
| 1011 | + ret = rtas_call(ddw_avail[DDW_CREATE_PE_DMA_WIN], 5, 4, |
|---|
| 1012 | + (u32 *)create, cfg_addr, BUID_HI(buid), |
|---|
| 1013 | + BUID_LO(buid), page_shift, window_shift); |
|---|
| 965 | 1014 | } while (rtas_busy_delay(ret)); |
|---|
| 966 | 1015 | dev_info(&dev->dev, |
|---|
| 967 | 1016 | "ibm,create-pe-dma-window(%x) %x %x %x %x %x returned %d " |
|---|
| 968 | | - "(liobn = 0x%x starting addr = %x %x)\n", ddw_avail[1], |
|---|
| 969 | | - cfg_addr, BUID_HI(buid), BUID_LO(buid), page_shift, |
|---|
| 970 | | - window_shift, ret, create->liobn, create->addr_hi, create->addr_lo); |
|---|
| 1017 | + "(liobn = 0x%x starting addr = %x %x)\n", |
|---|
| 1018 | + ddw_avail[DDW_CREATE_PE_DMA_WIN], cfg_addr, BUID_HI(buid), |
|---|
| 1019 | + BUID_LO(buid), page_shift, window_shift, ret, create->liobn, |
|---|
| 1020 | + create->addr_hi, create->addr_lo); |
|---|
| 971 | 1021 | |
|---|
| 972 | 1022 | return ret; |
|---|
| 973 | 1023 | } |
|---|
| .. | .. |
|---|
| 979 | 1029 | |
|---|
| 980 | 1030 | static LIST_HEAD(failed_ddw_pdn_list); |
|---|
| 981 | 1031 | |
|---|
| 1032 | +static phys_addr_t ddw_memory_hotplug_max(void) |
|---|
| 1033 | +{ |
|---|
| 1034 | + phys_addr_t max_addr = memory_hotplug_max(); |
|---|
| 1035 | + struct device_node *memory; |
|---|
| 1036 | + |
|---|
| 1037 | + for_each_node_by_type(memory, "memory") { |
|---|
| 1038 | + unsigned long start, size; |
|---|
| 1039 | + int n_mem_addr_cells, n_mem_size_cells, len; |
|---|
| 1040 | + const __be32 *memcell_buf; |
|---|
| 1041 | + |
|---|
| 1042 | + memcell_buf = of_get_property(memory, "reg", &len); |
|---|
| 1043 | + if (!memcell_buf || len <= 0) |
|---|
| 1044 | + continue; |
|---|
| 1045 | + |
|---|
| 1046 | + n_mem_addr_cells = of_n_addr_cells(memory); |
|---|
| 1047 | + n_mem_size_cells = of_n_size_cells(memory); |
|---|
| 1048 | + |
|---|
| 1049 | + start = of_read_number(memcell_buf, n_mem_addr_cells); |
|---|
| 1050 | + memcell_buf += n_mem_addr_cells; |
|---|
| 1051 | + size = of_read_number(memcell_buf, n_mem_size_cells); |
|---|
| 1052 | + memcell_buf += n_mem_size_cells; |
|---|
| 1053 | + |
|---|
| 1054 | + max_addr = max_t(phys_addr_t, max_addr, start + size); |
|---|
| 1055 | + } |
|---|
| 1056 | + |
|---|
| 1057 | + return max_addr; |
|---|
| 1058 | +} |
|---|
| 1059 | + |
|---|
| 1060 | +/* |
|---|
| 1061 | + * Platforms supporting the DDW option starting with LoPAR level 2.7 implement |
|---|
| 1062 | + * ibm,ddw-extensions, which carries the rtas token for |
|---|
| 1063 | + * ibm,reset-pe-dma-windows. |
|---|
| 1064 | + * That rtas-call can be used to restore the default DMA window for the device. |
|---|
| 1065 | + */ |
|---|
| 1066 | +static void reset_dma_window(struct pci_dev *dev, struct device_node *par_dn) |
|---|
| 1067 | +{ |
|---|
| 1068 | + int ret; |
|---|
| 1069 | + u32 cfg_addr, reset_dma_win; |
|---|
| 1070 | + u64 buid; |
|---|
| 1071 | + struct device_node *dn; |
|---|
| 1072 | + struct pci_dn *pdn; |
|---|
| 1073 | + |
|---|
| 1074 | + ret = ddw_read_ext(par_dn, DDW_EXT_RESET_DMA_WIN, &reset_dma_win); |
|---|
| 1075 | + if (ret) |
|---|
| 1076 | + return; |
|---|
| 1077 | + |
|---|
| 1078 | + dn = pci_device_to_OF_node(dev); |
|---|
| 1079 | + pdn = PCI_DN(dn); |
|---|
| 1080 | + buid = pdn->phb->buid; |
|---|
| 1081 | + cfg_addr = (pdn->busno << 16) | (pdn->devfn << 8); |
|---|
| 1082 | + |
|---|
| 1083 | + ret = rtas_call(reset_dma_win, 3, 1, NULL, cfg_addr, BUID_HI(buid), |
|---|
| 1084 | + BUID_LO(buid)); |
|---|
| 1085 | + if (ret) |
|---|
| 1086 | + dev_info(&dev->dev, |
|---|
| 1087 | + "ibm,reset-pe-dma-windows(%x) %x %x %x returned %d ", |
|---|
| 1088 | + reset_dma_win, cfg_addr, BUID_HI(buid), BUID_LO(buid), |
|---|
| 1089 | + ret); |
|---|
| 1090 | +} |
|---|
| 1091 | + |
|---|
| 982 | 1092 | /* |
|---|
| 983 | 1093 | * If the PE supports dynamic dma windows, and there is space for a table |
|---|
| 984 | 1094 | * that can map all pages in a linear offset, then setup such a table, |
|---|
| .. | .. |
|---|
| 988 | 1098 | * pdn: the parent pe node with the ibm,dma_window property |
|---|
| 989 | 1099 | * Future: also check if we can remap the base window for our base page size |
|---|
| 990 | 1100 | * |
|---|
| 991 | | - * returns the dma offset for use by dma_set_mask |
|---|
| 1101 | + * returns the dma offset for use by the direct mapped DMA code. |
|---|
| 992 | 1102 | */ |
|---|
| 993 | 1103 | static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) |
|---|
| 994 | 1104 | { |
|---|
| .. | .. |
|---|
| 998 | 1108 | int page_shift; |
|---|
| 999 | 1109 | u64 dma_addr, max_addr; |
|---|
| 1000 | 1110 | struct device_node *dn; |
|---|
| 1001 | | - u32 ddw_avail[3]; |
|---|
| 1111 | + u32 ddw_avail[DDW_APPLICABLE_SIZE]; |
|---|
| 1002 | 1112 | struct direct_window *window; |
|---|
| 1003 | 1113 | struct property *win64; |
|---|
| 1004 | 1114 | struct dynamic_dma_window_prop *ddwprop; |
|---|
| 1005 | 1115 | struct failed_ddw_pdn *fpdn; |
|---|
| 1116 | + bool default_win_removed = false; |
|---|
| 1006 | 1117 | |
|---|
| 1007 | 1118 | mutex_lock(&direct_window_init_mutex); |
|---|
| 1008 | 1119 | |
|---|
| .. | .. |
|---|
| 1031 | 1142 | * the property is actually in the parent, not the PE |
|---|
| 1032 | 1143 | */ |
|---|
| 1033 | 1144 | ret = of_property_read_u32_array(pdn, "ibm,ddw-applicable", |
|---|
| 1034 | | - &ddw_avail[0], 3); |
|---|
| 1145 | + &ddw_avail[0], DDW_APPLICABLE_SIZE); |
|---|
| 1035 | 1146 | if (ret) |
|---|
| 1036 | 1147 | goto out_failed; |
|---|
| 1037 | 1148 | |
|---|
| .. | .. |
|---|
| 1042 | 1153 | * of page sizes: supported and supported for migrate-dma. |
|---|
| 1043 | 1154 | */ |
|---|
| 1044 | 1155 | dn = pci_device_to_OF_node(dev); |
|---|
| 1045 | | - ret = query_ddw(dev, ddw_avail, &query); |
|---|
| 1156 | + ret = query_ddw(dev, ddw_avail, &query, pdn); |
|---|
| 1046 | 1157 | if (ret != 0) |
|---|
| 1047 | 1158 | goto out_failed; |
|---|
| 1048 | 1159 | |
|---|
| 1160 | + /* |
|---|
| 1161 | + * If there is no window available, remove the default DMA window, |
|---|
| 1162 | + * if it's present. This will make all the resources available to the |
|---|
| 1163 | + * new DDW window. |
|---|
| 1164 | + * If anything fails after this, we need to restore it, so also check |
|---|
| 1165 | + * for extensions presence. |
|---|
| 1166 | + */ |
|---|
| 1049 | 1167 | if (query.windows_available == 0) { |
|---|
| 1050 | | - /* |
|---|
| 1051 | | - * no additional windows are available for this device. |
|---|
| 1052 | | - * We might be able to reallocate the existing window, |
|---|
| 1053 | | - * trading in for a larger page size. |
|---|
| 1054 | | - */ |
|---|
| 1055 | | - dev_dbg(&dev->dev, "no free dynamic windows"); |
|---|
| 1056 | | - goto out_failed; |
|---|
| 1168 | + struct property *default_win; |
|---|
| 1169 | + int reset_win_ext; |
|---|
| 1170 | + |
|---|
| 1171 | + default_win = of_find_property(pdn, "ibm,dma-window", NULL); |
|---|
| 1172 | + if (!default_win) |
|---|
| 1173 | + goto out_failed; |
|---|
| 1174 | + |
|---|
| 1175 | + reset_win_ext = ddw_read_ext(pdn, DDW_EXT_RESET_DMA_WIN, NULL); |
|---|
| 1176 | + if (reset_win_ext) |
|---|
| 1177 | + goto out_failed; |
|---|
| 1178 | + |
|---|
| 1179 | + remove_dma_window(pdn, ddw_avail, default_win); |
|---|
| 1180 | + default_win_removed = true; |
|---|
| 1181 | + |
|---|
| 1182 | + /* Query again, to check if the window is available */ |
|---|
| 1183 | + ret = query_ddw(dev, ddw_avail, &query, pdn); |
|---|
| 1184 | + if (ret != 0) |
|---|
| 1185 | + goto out_failed; |
|---|
| 1186 | + |
|---|
| 1187 | + if (query.windows_available == 0) { |
|---|
| 1188 | + /* no windows are available for this device. */ |
|---|
| 1189 | + dev_dbg(&dev->dev, "no free dynamic windows"); |
|---|
| 1190 | + goto out_failed; |
|---|
| 1191 | + } |
|---|
| 1057 | 1192 | } |
|---|
| 1058 | 1193 | if (query.page_size & 4) { |
|---|
| 1059 | 1194 | page_shift = 24; /* 16MB */ |
|---|
| .. | .. |
|---|
| 1068 | 1203 | } |
|---|
| 1069 | 1204 | /* verify the window * number of ptes will map the partition */ |
|---|
| 1070 | 1205 | /* check largest block * page size > max memory hotplug addr */ |
|---|
| 1071 | | - max_addr = memory_hotplug_max(); |
|---|
| 1206 | + max_addr = ddw_memory_hotplug_max(); |
|---|
| 1072 | 1207 | if (query.largest_available_block < (max_addr >> page_shift)) { |
|---|
| 1073 | | - dev_dbg(&dev->dev, "can't map partition max 0x%llx with %u " |
|---|
| 1208 | + dev_dbg(&dev->dev, "can't map partition max 0x%llx with %llu " |
|---|
| 1074 | 1209 | "%llu-sized pages\n", max_addr, query.largest_available_block, |
|---|
| 1075 | 1210 | 1ULL << page_shift); |
|---|
| 1076 | 1211 | goto out_failed; |
|---|
| .. | .. |
|---|
| 1144 | 1279 | kfree(win64); |
|---|
| 1145 | 1280 | |
|---|
| 1146 | 1281 | out_failed: |
|---|
| 1282 | + if (default_win_removed) |
|---|
| 1283 | + reset_dma_window(dev, pdn); |
|---|
| 1147 | 1284 | |
|---|
| 1148 | 1285 | fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL); |
|---|
| 1149 | 1286 | if (!fpdn) |
|---|
| .. | .. |
|---|
| 1196 | 1333 | iommu_table_setparms_lpar(pci->phb, pdn, tbl, |
|---|
| 1197 | 1334 | pci->table_group, dma_window); |
|---|
| 1198 | 1335 | tbl->it_ops = &iommu_table_lpar_multi_ops; |
|---|
| 1199 | | - iommu_init_table(tbl, pci->phb->node); |
|---|
| 1336 | + iommu_init_table(tbl, pci->phb->node, 0, 0); |
|---|
| 1200 | 1337 | iommu_register_group(pci->table_group, |
|---|
| 1201 | 1338 | pci_domain_nr(pci->phb->bus), 0); |
|---|
| 1202 | 1339 | pr_debug(" created table: %p\n", pci->table_group); |
|---|
| .. | .. |
|---|
| 1205 | 1342 | } |
|---|
| 1206 | 1343 | |
|---|
| 1207 | 1344 | set_iommu_table_base(&dev->dev, pci->table_group->tables[0]); |
|---|
| 1208 | | - iommu_add_device(&dev->dev); |
|---|
| 1345 | + iommu_add_device(pci->table_group, &dev->dev); |
|---|
| 1209 | 1346 | } |
|---|
| 1210 | 1347 | |
|---|
| 1211 | | -static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask) |
|---|
| 1348 | +static bool iommu_bypass_supported_pSeriesLP(struct pci_dev *pdev, u64 dma_mask) |
|---|
| 1212 | 1349 | { |
|---|
| 1213 | | - bool ddw_enabled = false; |
|---|
| 1214 | | - struct device_node *pdn, *dn; |
|---|
| 1215 | | - struct pci_dev *pdev; |
|---|
| 1350 | + struct device_node *dn = pci_device_to_OF_node(pdev), *pdn; |
|---|
| 1216 | 1351 | const __be32 *dma_window = NULL; |
|---|
| 1217 | | - u64 dma_offset; |
|---|
| 1218 | | - |
|---|
| 1219 | | - if (!dev->dma_mask) |
|---|
| 1220 | | - return -EIO; |
|---|
| 1221 | | - |
|---|
| 1222 | | - if (!dev_is_pci(dev)) |
|---|
| 1223 | | - goto check_mask; |
|---|
| 1224 | | - |
|---|
| 1225 | | - pdev = to_pci_dev(dev); |
|---|
| 1226 | 1352 | |
|---|
| 1227 | 1353 | /* only attempt to use a new window if 64-bit DMA is requested */ |
|---|
| 1228 | | - if (!disable_ddw && dma_mask == DMA_BIT_MASK(64)) { |
|---|
| 1229 | | - dn = pci_device_to_OF_node(pdev); |
|---|
| 1230 | | - dev_dbg(dev, "node is %pOF\n", dn); |
|---|
| 1354 | + if (dma_mask < DMA_BIT_MASK(64)) |
|---|
| 1355 | + return false; |
|---|
| 1231 | 1356 | |
|---|
| 1232 | | - /* |
|---|
| 1233 | | - * the device tree might contain the dma-window properties |
|---|
| 1234 | | - * per-device and not necessarily for the bus. So we need to |
|---|
| 1235 | | - * search upwards in the tree until we either hit a dma-window |
|---|
| 1236 | | - * property, OR find a parent with a table already allocated. |
|---|
| 1237 | | - */ |
|---|
| 1238 | | - for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->table_group; |
|---|
| 1239 | | - pdn = pdn->parent) { |
|---|
| 1240 | | - dma_window = of_get_property(pdn, "ibm,dma-window", NULL); |
|---|
| 1241 | | - if (dma_window) |
|---|
| 1242 | | - break; |
|---|
| 1243 | | - } |
|---|
| 1244 | | - if (pdn && PCI_DN(pdn)) { |
|---|
| 1245 | | - dma_offset = enable_ddw(pdev, pdn); |
|---|
| 1246 | | - if (dma_offset != 0) { |
|---|
| 1247 | | - dev_info(dev, "Using 64-bit direct DMA at offset %llx\n", dma_offset); |
|---|
| 1248 | | - set_dma_offset(dev, dma_offset); |
|---|
| 1249 | | - set_dma_ops(dev, &dma_nommu_ops); |
|---|
| 1250 | | - ddw_enabled = true; |
|---|
| 1251 | | - } |
|---|
| 1252 | | - } |
|---|
| 1357 | + dev_dbg(&pdev->dev, "node is %pOF\n", dn); |
|---|
| 1358 | + |
|---|
| 1359 | + /* |
|---|
| 1360 | + * the device tree might contain the dma-window properties |
|---|
| 1361 | + * per-device and not necessarily for the bus. So we need to |
|---|
| 1362 | + * search upwards in the tree until we either hit a dma-window |
|---|
| 1363 | + * property, OR find a parent with a table already allocated. |
|---|
| 1364 | + */ |
|---|
| 1365 | + for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->table_group; |
|---|
| 1366 | + pdn = pdn->parent) { |
|---|
| 1367 | + dma_window = of_get_property(pdn, "ibm,dma-window", NULL); |
|---|
| 1368 | + if (dma_window) |
|---|
| 1369 | + break; |
|---|
| 1253 | 1370 | } |
|---|
| 1254 | 1371 | |
|---|
| 1255 | | - /* fall back on iommu ops */ |
|---|
| 1256 | | - if (!ddw_enabled && get_dma_ops(dev) != &dma_iommu_ops) { |
|---|
| 1257 | | - dev_info(dev, "Restoring 32-bit DMA via iommu\n"); |
|---|
| 1258 | | - set_dma_ops(dev, &dma_iommu_ops); |
|---|
| 1372 | + if (pdn && PCI_DN(pdn)) { |
|---|
| 1373 | + pdev->dev.archdata.dma_offset = enable_ddw(pdev, pdn); |
|---|
| 1374 | + if (pdev->dev.archdata.dma_offset) |
|---|
| 1375 | + return true; |
|---|
| 1259 | 1376 | } |
|---|
| 1260 | 1377 | |
|---|
| 1261 | | -check_mask: |
|---|
| 1262 | | - if (!dma_supported(dev, dma_mask)) |
|---|
| 1263 | | - return -EIO; |
|---|
| 1264 | | - |
|---|
| 1265 | | - *dev->dma_mask = dma_mask; |
|---|
| 1266 | | - return 0; |
|---|
| 1267 | | -} |
|---|
| 1268 | | - |
|---|
| 1269 | | -static u64 dma_get_required_mask_pSeriesLP(struct device *dev) |
|---|
| 1270 | | -{ |
|---|
| 1271 | | - if (!dev->dma_mask) |
|---|
| 1272 | | - return 0; |
|---|
| 1273 | | - |
|---|
| 1274 | | - if (!disable_ddw && dev_is_pci(dev)) { |
|---|
| 1275 | | - struct pci_dev *pdev = to_pci_dev(dev); |
|---|
| 1276 | | - struct device_node *dn; |
|---|
| 1277 | | - |
|---|
| 1278 | | - dn = pci_device_to_OF_node(pdev); |
|---|
| 1279 | | - |
|---|
| 1280 | | - /* search upwards for ibm,dma-window */ |
|---|
| 1281 | | - for (; dn && PCI_DN(dn) && !PCI_DN(dn)->table_group; |
|---|
| 1282 | | - dn = dn->parent) |
|---|
| 1283 | | - if (of_get_property(dn, "ibm,dma-window", NULL)) |
|---|
| 1284 | | - break; |
|---|
| 1285 | | - /* if there is a ibm,ddw-applicable property require 64 bits */ |
|---|
| 1286 | | - if (dn && PCI_DN(dn) && |
|---|
| 1287 | | - of_get_property(dn, "ibm,ddw-applicable", NULL)) |
|---|
| 1288 | | - return DMA_BIT_MASK(64); |
|---|
| 1289 | | - } |
|---|
| 1290 | | - |
|---|
| 1291 | | - return dma_iommu_ops.get_required_mask(dev); |
|---|
| 1378 | + return false; |
|---|
| 1292 | 1379 | } |
|---|
| 1293 | 1380 | |
|---|
| 1294 | 1381 | static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action, |
|---|
| .. | .. |
|---|
| 1383 | 1470 | if (firmware_has_feature(FW_FEATURE_LPAR)) { |
|---|
| 1384 | 1471 | pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeriesLP; |
|---|
| 1385 | 1472 | pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeriesLP; |
|---|
| 1386 | | - ppc_md.dma_set_mask = dma_set_mask_pSeriesLP; |
|---|
| 1387 | | - ppc_md.dma_get_required_mask = dma_get_required_mask_pSeriesLP; |
|---|
| 1473 | + if (!disable_ddw) |
|---|
| 1474 | + pseries_pci_controller_ops.iommu_bypass_supported = |
|---|
| 1475 | + iommu_bypass_supported_pSeriesLP; |
|---|
| 1388 | 1476 | } else { |
|---|
| 1389 | 1477 | pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeries; |
|---|
| 1390 | 1478 | pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeries; |
|---|
| .. | .. |
|---|
| 1401 | 1489 | { |
|---|
| 1402 | 1490 | if (strcmp(str, "off") == 0 && |
|---|
| 1403 | 1491 | firmware_has_feature(FW_FEATURE_LPAR) && |
|---|
| 1404 | | - firmware_has_feature(FW_FEATURE_MULTITCE)) { |
|---|
| 1492 | + (firmware_has_feature(FW_FEATURE_PUT_TCE_IND) || |
|---|
| 1493 | + firmware_has_feature(FW_FEATURE_STUFF_TCE))) { |
|---|
| 1405 | 1494 | printk(KERN_INFO "Disabling MULTITCE firmware feature\n"); |
|---|
| 1406 | | - powerpc_firmware_features &= ~FW_FEATURE_MULTITCE; |
|---|
| 1495 | + powerpc_firmware_features &= |
|---|
| 1496 | + ~(FW_FEATURE_PUT_TCE_IND | FW_FEATURE_STUFF_TCE); |
|---|
| 1407 | 1497 | } |
|---|
| 1408 | 1498 | return 1; |
|---|
| 1409 | 1499 | } |
|---|
| 1410 | 1500 | |
|---|
| 1411 | 1501 | __setup("multitce=", disable_multitce); |
|---|
| 1412 | 1502 | |
|---|
| 1503 | +static int tce_iommu_bus_notifier(struct notifier_block *nb, |
|---|
| 1504 | + unsigned long action, void *data) |
|---|
| 1505 | +{ |
|---|
| 1506 | + struct device *dev = data; |
|---|
| 1507 | + |
|---|
| 1508 | + switch (action) { |
|---|
| 1509 | + case BUS_NOTIFY_DEL_DEVICE: |
|---|
| 1510 | + iommu_del_device(dev); |
|---|
| 1511 | + return 0; |
|---|
| 1512 | + default: |
|---|
| 1513 | + return 0; |
|---|
| 1514 | + } |
|---|
| 1515 | +} |
|---|
| 1516 | + |
|---|
| 1517 | +static struct notifier_block tce_iommu_bus_nb = { |
|---|
| 1518 | + .notifier_call = tce_iommu_bus_notifier, |
|---|
| 1519 | +}; |
|---|
| 1520 | + |
|---|
| 1521 | +static int __init tce_iommu_bus_notifier_init(void) |
|---|
| 1522 | +{ |
|---|
| 1523 | + bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb); |
|---|
| 1524 | + return 0; |
|---|
| 1525 | +} |
|---|
| 1413 | 1526 | machine_subsys_initcall_sync(pseries, tce_iommu_bus_notifier_init); |
|---|