]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation |
3 | * | |
4 | * Rewrite, cleanup: | |
5 | * | |
91f14480 | 6 | * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation |
bc97ce95 | 7 | * Copyright (C) 2006 Olof Johansson <olof@lixom.net> |
1da177e4 LT |
8 | * |
9 | * Dynamic DMA mapping support, iSeries-specific parts. | |
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 | */ | |
26 | ||
27 | #include <linux/types.h> | |
28 | #include <linux/dma-mapping.h> | |
29 | #include <linux/list.h> | |
12d04eef | 30 | #include <linux/pci.h> |
1670b2b2 | 31 | #include <linux/module.h> |
5a0e3ad6 | 32 | #include <linux/slab.h> |
1da177e4 LT |
33 | |
34 | #include <asm/iommu.h> | |
dd9b67ab | 35 | #include <asm/vio.h> |
c707ffcf | 36 | #include <asm/tce.h> |
1da177e4 | 37 | #include <asm/machdep.h> |
426c1a11 | 38 | #include <asm/abs_addr.h> |
96ff6afa | 39 | #include <asm/prom.h> |
bffa8fc3 | 40 | #include <asm/pci-bridge.h> |
8021b8a7 | 41 | #include <asm/iseries/hv_call_xm.h> |
1670b2b2 | 42 | #include <asm/iseries/hv_call_event.h> |
c7f0e8cb | 43 | #include <asm/iseries/iommu.h> |
9bd7ea60 | 44 | |
6490c490 | 45 | static int tce_build_iSeries(struct iommu_table *tbl, long index, long npages, |
4f3dd8a0 MN |
46 | unsigned long uaddr, enum dma_data_direction direction, |
47 | struct dma_attrs *attrs) | |
1da177e4 LT |
48 | { |
49 | u64 rc; | |
bc97ce95 | 50 | u64 tce, rpn; |
1da177e4 LT |
51 | |
52 | while (npages--) { | |
bc97ce95 OJ |
53 | rpn = virt_to_abs(uaddr) >> TCE_SHIFT; |
54 | tce = (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; | |
1da177e4 LT |
55 | |
56 | if (tbl->it_type == TCE_VB) { | |
57 | /* Virtual Bus */ | |
bc97ce95 | 58 | tce |= TCE_VALID|TCE_ALLIO; |
1da177e4 | 59 | if (direction != DMA_TO_DEVICE) |
bc97ce95 | 60 | tce |= TCE_VB_WRITE; |
1da177e4 LT |
61 | } else { |
62 | /* PCI Bus */ | |
bc97ce95 | 63 | tce |= TCE_PCI_READ; /* Read allowed */ |
1da177e4 | 64 | if (direction != DMA_TO_DEVICE) |
bc97ce95 | 65 | tce |= TCE_PCI_WRITE; |
1da177e4 LT |
66 | } |
67 | ||
bc97ce95 | 68 | rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, tce); |
1da177e4 | 69 | if (rc) |
fe333321 | 70 | panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%llx\n", |
1da177e4 LT |
71 | rc); |
72 | index++; | |
3c726f8d | 73 | uaddr += TCE_PAGE_SIZE; |
1da177e4 | 74 | } |
6490c490 | 75 | return 0; |
1da177e4 LT |
76 | } |
77 | ||
78 | static void tce_free_iSeries(struct iommu_table *tbl, long index, long npages) | |
79 | { | |
80 | u64 rc; | |
81 | ||
82 | while (npages--) { | |
83 | rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, 0); | |
84 | if (rc) | |
fe333321 | 85 | panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%llx\n", |
1da177e4 LT |
86 | rc); |
87 | index++; | |
88 | } | |
89 | } | |
90 | ||
ca652c93 ME |
91 | /* |
92 | * Structure passed to HvCallXm_getTceTableParms | |
93 | */ | |
94 | struct iommu_table_cb { | |
95 | unsigned long itc_busno; /* Bus number for this tce table */ | |
96 | unsigned long itc_start; /* Will be NULL for secondary */ | |
97 | unsigned long itc_totalsize; /* Size (in pages) of whole table */ | |
98 | unsigned long itc_offset; /* Index into real tce table of the | |
99 | start of our section */ | |
100 | unsigned long itc_size; /* Size (in pages) of our section */ | |
101 | unsigned long itc_index; /* Index of this tce table */ | |
102 | unsigned short itc_maxtables; /* Max num of tables for partition */ | |
103 | unsigned char itc_virtbus; /* Flag to indicate virtual bus */ | |
104 | unsigned char itc_slotno; /* IOA Tce Slot Index */ | |
105 | unsigned char itc_rsvd[4]; | |
106 | }; | |
107 | ||
1da177e4 LT |
108 | /* |
109 | * Call Hv with the architected data structure to get TCE table info. | |
110 | * info. Put the returned data into the Linux representation of the | |
111 | * TCE table data. | |
112 | * The Hardware Tce table comes in three flavors. | |
113 | * 1. TCE table shared between Buses. | |
114 | * 2. TCE table per Bus. | |
115 | * 3. TCE Table per IOA. | |
116 | */ | |
3c726f8d BH |
117 | void iommu_table_getparms_iSeries(unsigned long busno, |
118 | unsigned char slotno, | |
119 | unsigned char virtbus, | |
120 | struct iommu_table* tbl) | |
1da177e4 LT |
121 | { |
122 | struct iommu_table_cb *parms; | |
123 | ||
f8485350 | 124 | parms = kzalloc(sizeof(*parms), GFP_KERNEL); |
1da177e4 LT |
125 | if (parms == NULL) |
126 | panic("PCI_DMA: TCE Table Allocation failed."); | |
127 | ||
3c726f8d BH |
128 | parms->itc_busno = busno; |
129 | parms->itc_slotno = slotno; | |
130 | parms->itc_virtbus = virtbus; | |
1da177e4 | 131 | |
426c1a11 | 132 | HvCallXm_getTceTableParms(iseries_hv_addr(parms)); |
1da177e4 LT |
133 | |
134 | if (parms->itc_size == 0) | |
135 | panic("PCI_DMA: parms->size is zero, parms is 0x%p", parms); | |
136 | ||
137 | /* itc_size is in pages worth of table, it_size is in # of entries */ | |
5d2efba6 | 138 | tbl->it_size = (parms->itc_size * TCE_PAGE_SIZE) / TCE_ENTRY_SIZE; |
1da177e4 | 139 | tbl->it_busno = parms->itc_busno; |
5d2efba6 | 140 | tbl->it_offset = parms->itc_offset; |
1da177e4 LT |
141 | tbl->it_index = parms->itc_index; |
142 | tbl->it_blocksize = 1; | |
3c726f8d | 143 | tbl->it_type = virtbus ? TCE_VB : TCE_PCI; |
1da177e4 LT |
144 | |
145 | kfree(parms); | |
146 | } | |
147 | ||
148 | ||
3c726f8d BH |
149 | #ifdef CONFIG_PCI |
150 | /* | |
151 | * This function compares the known tables to find an iommu_table | |
152 | * that has already been built for hardware TCEs. | |
153 | */ | |
154 | static struct iommu_table *iommu_table_find(struct iommu_table * tbl) | |
155 | { | |
96ff6afa SR |
156 | struct device_node *node; |
157 | ||
158 | for (node = NULL; (node = of_find_all_nodes(node)); ) { | |
159 | struct pci_dn *pdn = PCI_DN(node); | |
160 | struct iommu_table *it; | |
3c726f8d | 161 | |
96ff6afa SR |
162 | if (pdn == NULL) |
163 | continue; | |
164 | it = pdn->iommu_table; | |
3c726f8d BH |
165 | if ((it != NULL) && |
166 | (it->it_type == TCE_PCI) && | |
167 | (it->it_offset == tbl->it_offset) && | |
168 | (it->it_index == tbl->it_index) && | |
af449c33 JL |
169 | (it->it_size == tbl->it_size)) { |
170 | of_node_put(node); | |
3c726f8d | 171 | return it; |
af449c33 | 172 | } |
3c726f8d BH |
173 | } |
174 | return NULL; | |
175 | } | |
176 | ||
177 | ||
397717c5 | 178 | static void pci_dma_dev_setup_iseries(struct pci_dev *pdev) |
1da177e4 LT |
179 | { |
180 | struct iommu_table *tbl; | |
d7b41b1f | 181 | struct device_node *dn = pci_device_to_OF_node(pdev); |
252e75a5 | 182 | struct pci_dn *pdn = PCI_DN(dn); |
e2eb6392 | 183 | const u32 *lsn = of_get_property(dn, "linux,logical-slot-number", NULL); |
403fac4f SR |
184 | |
185 | BUG_ON(lsn == NULL); | |
1da177e4 | 186 | |
7aa241fd | 187 | tbl = kzalloc(sizeof(struct iommu_table), GFP_KERNEL); |
1da177e4 | 188 | |
403fac4f | 189 | iommu_table_getparms_iSeries(pdn->busno, *lsn, 0, tbl); |
1da177e4 LT |
190 | |
191 | /* Look for existing tce table */ | |
252e75a5 SR |
192 | pdn->iommu_table = iommu_table_find(tbl); |
193 | if (pdn->iommu_table == NULL) | |
ca1588e7 | 194 | pdn->iommu_table = iommu_init_table(tbl, -1); |
1da177e4 LT |
195 | else |
196 | kfree(tbl); | |
738ef42e | 197 | set_iommu_table_base(&pdev->dev, pdn->iommu_table); |
1da177e4 | 198 | } |
397717c5 SR |
199 | #else |
200 | #define pci_dma_dev_setup_iseries NULL | |
145d01e4 | 201 | #endif |
1da177e4 | 202 | |
dd9b67ab SR |
203 | static struct iommu_table veth_iommu_table; |
204 | static struct iommu_table vio_iommu_table; | |
1670b2b2 SR |
205 | |
206 | void *iseries_hv_alloc(size_t size, dma_addr_t *dma_handle, gfp_t flag) | |
207 | { | |
fb3475e9 | 208 | return iommu_alloc_coherent(NULL, &vio_iommu_table, size, dma_handle, |
284901a9 | 209 | DMA_BIT_MASK(32), flag, -1); |
1670b2b2 SR |
210 | } |
211 | EXPORT_SYMBOL_GPL(iseries_hv_alloc); | |
212 | ||
213 | void iseries_hv_free(size_t size, void *vaddr, dma_addr_t dma_handle) | |
214 | { | |
215 | iommu_free_coherent(&vio_iommu_table, size, vaddr, dma_handle); | |
216 | } | |
217 | EXPORT_SYMBOL_GPL(iseries_hv_free); | |
218 | ||
219 | dma_addr_t iseries_hv_map(void *vaddr, size_t size, | |
220 | enum dma_data_direction direction) | |
221 | { | |
f9226d57 MN |
222 | return iommu_map_page(NULL, &vio_iommu_table, virt_to_page(vaddr), |
223 | (unsigned long)vaddr % PAGE_SIZE, size, | |
284901a9 | 224 | DMA_BIT_MASK(32), direction, NULL); |
1670b2b2 SR |
225 | } |
226 | ||
227 | void iseries_hv_unmap(dma_addr_t dma_handle, size_t size, | |
228 | enum dma_data_direction direction) | |
229 | { | |
f9226d57 | 230 | iommu_unmap_page(&vio_iommu_table, dma_handle, size, direction, NULL); |
1670b2b2 SR |
231 | } |
232 | ||
dd9b67ab SR |
233 | void __init iommu_vio_init(void) |
234 | { | |
235 | iommu_table_getparms_iSeries(255, 0, 0xff, &veth_iommu_table); | |
236 | veth_iommu_table.it_size /= 2; | |
237 | vio_iommu_table = veth_iommu_table; | |
238 | vio_iommu_table.it_offset += veth_iommu_table.it_size; | |
239 | ||
240 | if (!iommu_init_table(&veth_iommu_table, -1)) | |
241 | printk("Virtual Bus VETH TCE table failed.\n"); | |
242 | if (!iommu_init_table(&vio_iommu_table, -1)) | |
243 | printk("Virtual Bus VIO TCE table failed.\n"); | |
244 | } | |
245 | ||
246 | struct iommu_table *vio_build_iommu_table_iseries(struct vio_dev *dev) | |
247 | { | |
248 | if (strcmp(dev->type, "network") == 0) | |
249 | return &veth_iommu_table; | |
250 | return &vio_iommu_table; | |
251 | } | |
252 | ||
1da177e4 LT |
253 | void iommu_init_early_iSeries(void) |
254 | { | |
255 | ppc_md.tce_build = tce_build_iSeries; | |
256 | ppc_md.tce_free = tce_free_iSeries; | |
257 | ||
397717c5 | 258 | ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_iseries; |
98747770 | 259 | set_pci_dma_ops(&dma_iommu_ops); |
1da177e4 | 260 | } |