diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ia64/sn/pci/pcibr/pcibr_dma.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ia64/sn/pci/pcibr/pcibr_dma.c')
-rw-r--r-- | arch/ia64/sn/pci/pcibr/pcibr_dma.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_dma.c b/arch/ia64/sn/pci/pcibr/pcibr_dma.c new file mode 100644 index 00000000000..b1d66ac065c --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/pcibr_dma.c | |||
@@ -0,0 +1,379 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <asm/sn/sn_sal.h> | ||
12 | #include <asm/sn/geo.h> | ||
13 | #include "xtalk/xwidgetdev.h" | ||
14 | #include "xtalk/hubdev.h" | ||
15 | #include "pci/pcibus_provider_defs.h" | ||
16 | #include "pci/pcidev.h" | ||
17 | #include "pci/tiocp.h" | ||
18 | #include "pci/pic.h" | ||
19 | #include "pci/pcibr_provider.h" | ||
20 | #include "pci/tiocp.h" | ||
21 | #include "tio.h" | ||
22 | #include <asm/sn/addrs.h> | ||
23 | |||
24 | extern int sn_ioif_inited; | ||
25 | |||
26 | /* ===================================================================== | ||
27 | * DMA MANAGEMENT | ||
28 | * | ||
29 | * The Bridge ASIC provides three methods of doing DMA: via a "direct map" | ||
30 | * register available in 32-bit PCI space (which selects a contiguous 2G | ||
31 | * address space on some other widget), via "direct" addressing via 64-bit | ||
32 | * PCI space (all destination information comes from the PCI address, | ||
33 | * including transfer attributes), and via a "mapped" region that allows | ||
34 | * a bunch of different small mappings to be established with the PMU. | ||
35 | * | ||
36 | * For efficiency, we most prefer to use the 32bit direct mapping facility, | ||
37 | * since it requires no resource allocations. The advantage of using the | ||
38 | * PMU over the 64-bit direct is that single-cycle PCI addressing can be | ||
39 | * used; the advantage of using 64-bit direct over PMU addressing is that | ||
40 | * we do not have to allocate entries in the PMU. | ||
41 | */ | ||
42 | |||
43 | static uint64_t | ||
44 | pcibr_dmamap_ate32(struct pcidev_info *info, | ||
45 | uint64_t paddr, size_t req_size, uint64_t flags) | ||
46 | { | ||
47 | |||
48 | struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info; | ||
49 | struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> | ||
50 | pdi_pcibus_info; | ||
51 | uint8_t internal_device = (PCI_SLOT(pcidev_info->pdi_host_pcidev_info-> | ||
52 | pdi_linux_pcidev->devfn)) - 1; | ||
53 | int ate_count; | ||
54 | int ate_index; | ||
55 | uint64_t ate_flags = flags | PCI32_ATE_V; | ||
56 | uint64_t ate; | ||
57 | uint64_t pci_addr; | ||
58 | uint64_t xio_addr; | ||
59 | uint64_t offset; | ||
60 | |||
61 | /* PIC in PCI-X mode does not supports 32bit PageMap mode */ | ||
62 | if (IS_PIC_SOFT(pcibus_info) && IS_PCIX(pcibus_info)) { | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* Calculate the number of ATEs needed. */ | ||
67 | if (!(MINIMAL_ATE_FLAG(paddr, req_size))) { | ||
68 | ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */ | ||
69 | +req_size /* max mapping bytes */ | ||
70 | - 1) + 1; /* round UP */ | ||
71 | } else { /* assume requested target is page aligned */ | ||
72 | ate_count = IOPG(req_size /* max mapping bytes */ | ||
73 | - 1) + 1; /* round UP */ | ||
74 | } | ||
75 | |||
76 | /* Get the number of ATEs required. */ | ||
77 | ate_index = pcibr_ate_alloc(pcibus_info, ate_count); | ||
78 | if (ate_index < 0) | ||
79 | return 0; | ||
80 | |||
81 | /* In PCI-X mode, Prefetch not supported */ | ||
82 | if (IS_PCIX(pcibus_info)) | ||
83 | ate_flags &= ~(PCI32_ATE_PREF); | ||
84 | |||
85 | xio_addr = | ||
86 | IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : | ||
87 | PHYS_TO_TIODMA(paddr); | ||
88 | offset = IOPGOFF(xio_addr); | ||
89 | ate = ate_flags | (xio_addr - offset); | ||
90 | |||
91 | /* If PIC, put the targetid in the ATE */ | ||
92 | if (IS_PIC_SOFT(pcibus_info)) { | ||
93 | ate |= (pcibus_info->pbi_hub_xid << PIC_ATE_TARGETID_SHFT); | ||
94 | } | ||
95 | ate_write(pcibus_info, ate_index, ate_count, ate); | ||
96 | |||
97 | /* | ||
98 | * Set up the DMA mapped Address. | ||
99 | */ | ||
100 | pci_addr = PCI32_MAPPED_BASE + offset + IOPGSIZE * ate_index; | ||
101 | |||
102 | /* | ||
103 | * If swap was set in device in pcibr_endian_set() | ||
104 | * we need to turn swapping on. | ||
105 | */ | ||
106 | if (pcibus_info->pbi_devreg[internal_device] & PCIBR_DEV_SWAP_DIR) | ||
107 | ATE_SWAP_ON(pci_addr); | ||
108 | |||
109 | return pci_addr; | ||
110 | } | ||
111 | |||
112 | static uint64_t | ||
113 | pcibr_dmatrans_direct64(struct pcidev_info * info, uint64_t paddr, | ||
114 | uint64_t dma_attributes) | ||
115 | { | ||
116 | struct pcibus_info *pcibus_info = (struct pcibus_info *) | ||
117 | ((info->pdi_host_pcidev_info)->pdi_pcibus_info); | ||
118 | uint64_t pci_addr; | ||
119 | |||
120 | /* Translate to Crosstalk View of Physical Address */ | ||
121 | pci_addr = (IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : | ||
122 | PHYS_TO_TIODMA(paddr)) | dma_attributes; | ||
123 | |||
124 | /* Handle Bus mode */ | ||
125 | if (IS_PCIX(pcibus_info)) | ||
126 | pci_addr &= ~PCI64_ATTR_PREF; | ||
127 | |||
128 | /* Handle Bridge Chipset differences */ | ||
129 | if (IS_PIC_SOFT(pcibus_info)) { | ||
130 | pci_addr |= | ||
131 | ((uint64_t) pcibus_info-> | ||
132 | pbi_hub_xid << PIC_PCI64_ATTR_TARG_SHFT); | ||
133 | } else | ||
134 | pci_addr |= TIOCP_PCI64_CMDTYPE_MEM; | ||
135 | |||
136 | /* If PCI mode, func zero uses VCHAN0, every other func uses VCHAN1 */ | ||
137 | if (!IS_PCIX(pcibus_info) && PCI_FUNC(info->pdi_linux_pcidev->devfn)) | ||
138 | pci_addr |= PCI64_ATTR_VIRTUAL; | ||
139 | |||
140 | return pci_addr; | ||
141 | |||
142 | } | ||
143 | |||
144 | static uint64_t | ||
145 | pcibr_dmatrans_direct32(struct pcidev_info * info, | ||
146 | uint64_t paddr, size_t req_size, uint64_t flags) | ||
147 | { | ||
148 | |||
149 | struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info; | ||
150 | struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> | ||
151 | pdi_pcibus_info; | ||
152 | uint64_t xio_addr; | ||
153 | |||
154 | uint64_t xio_base; | ||
155 | uint64_t offset; | ||
156 | uint64_t endoff; | ||
157 | |||
158 | if (IS_PCIX(pcibus_info)) { | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : | ||
163 | PHYS_TO_TIODMA(paddr); | ||
164 | |||
165 | xio_base = pcibus_info->pbi_dir_xbase; | ||
166 | offset = xio_addr - xio_base; | ||
167 | endoff = req_size + offset; | ||
168 | if ((req_size > (1ULL << 31)) || /* Too Big */ | ||
169 | (xio_addr < xio_base) || /* Out of range for mappings */ | ||
170 | (endoff > (1ULL << 31))) { /* Too Big */ | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | return PCI32_DIRECT_BASE | offset; | ||
175 | |||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Wrapper routine for free'ing DMA maps | ||
180 | * DMA mappings for Direct 64 and 32 do not have any DMA maps. | ||
181 | */ | ||
182 | void | ||
183 | pcibr_dma_unmap(struct pcidev_info *pcidev_info, dma_addr_t dma_handle, | ||
184 | int direction) | ||
185 | { | ||
186 | struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> | ||
187 | pdi_pcibus_info; | ||
188 | |||
189 | if (IS_PCI32_MAPPED(dma_handle)) { | ||
190 | int ate_index; | ||
191 | |||
192 | ate_index = | ||
193 | IOPG((ATE_SWAP_OFF(dma_handle) - PCI32_MAPPED_BASE)); | ||
194 | pcibr_ate_free(pcibus_info, ate_index); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * On SN systems there is a race condition between a PIO read response and | ||
200 | * DMA's. In rare cases, the read response may beat the DMA, causing the | ||
201 | * driver to think that data in memory is complete and meaningful. This code | ||
202 | * eliminates that race. This routine is called by the PIO read routines | ||
203 | * after doing the read. For PIC this routine then forces a fake interrupt | ||
204 | * on another line, which is logically associated with the slot that the PIO | ||
205 | * is addressed to. It then spins while watching the memory location that | ||
206 | * the interrupt is targetted to. When the interrupt response arrives, we | ||
207 | * are sure that the DMA has landed in memory and it is safe for the driver | ||
208 | * to proceed. For TIOCP use the Device(x) Write Request Buffer Flush | ||
209 | * Bridge register since it ensures the data has entered the coherence domain, | ||
210 | * unlike the PIC Device(x) Write Request Buffer Flush register. | ||
211 | */ | ||
212 | |||
213 | void sn_dma_flush(uint64_t addr) | ||
214 | { | ||
215 | nasid_t nasid; | ||
216 | int is_tio; | ||
217 | int wid_num; | ||
218 | int i, j; | ||
219 | int bwin; | ||
220 | uint64_t flags; | ||
221 | struct hubdev_info *hubinfo; | ||
222 | volatile struct sn_flush_device_list *p; | ||
223 | struct sn_flush_nasid_entry *flush_nasid_list; | ||
224 | |||
225 | if (!sn_ioif_inited) | ||
226 | return; | ||
227 | |||
228 | nasid = NASID_GET(addr); | ||
229 | if (-1 == nasid_to_cnodeid(nasid)) | ||
230 | return; | ||
231 | |||
232 | hubinfo = (NODEPDA(nasid_to_cnodeid(nasid)))->pdinfo; | ||
233 | |||
234 | if (!hubinfo) { | ||
235 | BUG(); | ||
236 | } | ||
237 | is_tio = (nasid & 1); | ||
238 | if (is_tio) { | ||
239 | wid_num = TIO_SWIN_WIDGETNUM(addr); | ||
240 | bwin = TIO_BWIN_WINDOWNUM(addr); | ||
241 | } else { | ||
242 | wid_num = SWIN_WIDGETNUM(addr); | ||
243 | bwin = BWIN_WINDOWNUM(addr); | ||
244 | } | ||
245 | |||
246 | flush_nasid_list = &hubinfo->hdi_flush_nasid_list; | ||
247 | if (flush_nasid_list->widget_p == NULL) | ||
248 | return; | ||
249 | if (bwin > 0) { | ||
250 | uint64_t itte = flush_nasid_list->iio_itte[bwin]; | ||
251 | |||
252 | if (is_tio) { | ||
253 | wid_num = (itte >> TIO_ITTE_WIDGET_SHIFT) & | ||
254 | TIO_ITTE_WIDGET_MASK; | ||
255 | } else { | ||
256 | wid_num = (itte >> IIO_ITTE_WIDGET_SHIFT) & | ||
257 | IIO_ITTE_WIDGET_MASK; | ||
258 | } | ||
259 | } | ||
260 | if (flush_nasid_list->widget_p == NULL) | ||
261 | return; | ||
262 | if (flush_nasid_list->widget_p[wid_num] == NULL) | ||
263 | return; | ||
264 | p = &flush_nasid_list->widget_p[wid_num][0]; | ||
265 | |||
266 | /* find a matching BAR */ | ||
267 | for (i = 0; i < DEV_PER_WIDGET; i++) { | ||
268 | for (j = 0; j < PCI_ROM_RESOURCE; j++) { | ||
269 | if (p->sfdl_bar_list[j].start == 0) | ||
270 | break; | ||
271 | if (addr >= p->sfdl_bar_list[j].start | ||
272 | && addr <= p->sfdl_bar_list[j].end) | ||
273 | break; | ||
274 | } | ||
275 | if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0) | ||
276 | break; | ||
277 | p++; | ||
278 | } | ||
279 | |||
280 | /* if no matching BAR, return without doing anything. */ | ||
281 | if (i == DEV_PER_WIDGET) | ||
282 | return; | ||
283 | |||
284 | /* | ||
285 | * For TIOCP use the Device(x) Write Request Buffer Flush Bridge | ||
286 | * register since it ensures the data has entered the coherence | ||
287 | * domain, unlike PIC | ||
288 | */ | ||
289 | if (is_tio) { | ||
290 | uint32_t tio_id = REMOTE_HUB_L(nasid, TIO_NODE_ID); | ||
291 | uint32_t revnum = XWIDGET_PART_REV_NUM(tio_id); | ||
292 | |||
293 | /* TIOCP BRINGUP WAR (PV907516): Don't write buffer flush reg */ | ||
294 | if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { | ||
295 | return; | ||
296 | } else { | ||
297 | pcireg_wrb_flush_get(p->sfdl_pcibus_info, | ||
298 | (p->sfdl_slot - 1)); | ||
299 | } | ||
300 | } else { | ||
301 | spin_lock_irqsave(&((struct sn_flush_device_list *)p)-> | ||
302 | sfdl_flush_lock, flags); | ||
303 | |||
304 | p->sfdl_flush_value = 0; | ||
305 | |||
306 | /* force an interrupt. */ | ||
307 | *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1; | ||
308 | |||
309 | /* wait for the interrupt to come back. */ | ||
310 | while (*(p->sfdl_flush_addr) != 0x10f) ; | ||
311 | |||
312 | /* okay, everything is synched up. */ | ||
313 | spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags); | ||
314 | } | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * Wrapper DMA interface. Called from pci_dma.c routines. | ||
320 | */ | ||
321 | |||
322 | uint64_t | ||
323 | pcibr_dma_map(struct pcidev_info * pcidev_info, unsigned long phys_addr, | ||
324 | size_t size, unsigned int flags) | ||
325 | { | ||
326 | dma_addr_t dma_handle; | ||
327 | struct pci_dev *pcidev = pcidev_info->pdi_linux_pcidev; | ||
328 | |||
329 | if (flags & SN_PCIDMA_CONSISTENT) { | ||
330 | /* sn_pci_alloc_consistent interfaces */ | ||
331 | if (pcidev->dev.coherent_dma_mask == ~0UL) { | ||
332 | dma_handle = | ||
333 | pcibr_dmatrans_direct64(pcidev_info, phys_addr, | ||
334 | PCI64_ATTR_BAR); | ||
335 | } else { | ||
336 | dma_handle = | ||
337 | (dma_addr_t) pcibr_dmamap_ate32(pcidev_info, | ||
338 | phys_addr, size, | ||
339 | PCI32_ATE_BAR); | ||
340 | } | ||
341 | } else { | ||
342 | /* map_sg/map_single interfaces */ | ||
343 | |||
344 | /* SN cannot support DMA addresses smaller than 32 bits. */ | ||
345 | if (pcidev->dma_mask < 0x7fffffff) { | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | if (pcidev->dma_mask == ~0UL) { | ||
350 | /* | ||
351 | * Handle the most common case: 64 bit cards. This | ||
352 | * call should always succeed. | ||
353 | */ | ||
354 | |||
355 | dma_handle = | ||
356 | pcibr_dmatrans_direct64(pcidev_info, phys_addr, | ||
357 | PCI64_ATTR_PREF); | ||
358 | } else { | ||
359 | /* Handle 32-63 bit cards via direct mapping */ | ||
360 | dma_handle = | ||
361 | pcibr_dmatrans_direct32(pcidev_info, phys_addr, | ||
362 | size, 0); | ||
363 | if (!dma_handle) { | ||
364 | /* | ||
365 | * It is a 32 bit card and we cannot do direct mapping, | ||
366 | * so we use an ATE. | ||
367 | */ | ||
368 | |||
369 | dma_handle = | ||
370 | pcibr_dmamap_ate32(pcidev_info, phys_addr, | ||
371 | size, PCI32_ATE_PREF); | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | |||
376 | return dma_handle; | ||
377 | } | ||
378 | |||
379 | EXPORT_SYMBOL(sn_dma_flush); | ||