blob: 07b587c5a2d24ebab473962debdf4c9c6461b16d [file] [log] [blame]
Jon Masone4650582006-06-26 13:58:14 +02001/*
2 * Derived from arch/powerpc/kernel/iommu.c
3 *
Muli Ben-Yehuda98822342007-07-21 17:10:48 +02004 * Copyright IBM Corporation, 2006-2007
Jon Masond8d2bed2006-10-05 18:47:21 +02005 * Copyright (C) 2006 Jon Mason <jdmason@kudzu.us>
Jon Masone4650582006-06-26 13:58:14 +02006 *
Jon Masond8d2bed2006-10-05 18:47:21 +02007 * Author: Jon Mason <jdmason@kudzu.us>
Muli Ben-Yehudaaa0a9f32006-07-10 17:06:15 +02008 * Author: Muli Ben-Yehuda <muli@il.ibm.com>
9
Jon Masone4650582006-06-26 13:58:14 +020010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
Jon Masone4650582006-06-26 13:58:14 +020025#include <linux/kernel.h>
26#include <linux/init.h>
27#include <linux/types.h>
28#include <linux/slab.h>
29#include <linux/mm.h>
30#include <linux/spinlock.h>
31#include <linux/string.h>
Chandru95b68de2008-07-25 01:47:55 -070032#include <linux/crash_dump.h>
Jon Masone4650582006-06-26 13:58:14 +020033#include <linux/dma-mapping.h>
Akinobu Mitaa66022c2009-12-15 16:48:28 -080034#include <linux/bitmap.h>
Jon Masone4650582006-06-26 13:58:14 +020035#include <linux/pci_ids.h>
36#include <linux/pci.h>
37#include <linux/delay.h>
Jens Axboe8b87d9f2007-07-24 12:38:15 +020038#include <linux/scatterlist.h>
FUJITA Tomonori1b39b072008-02-04 22:28:10 -080039#include <linux/iommu-helper.h>
Alexis Bruemmer1956a962008-07-25 19:44:51 -070040
FUJITA Tomonori46a7fa22008-07-11 10:23:42 +090041#include <asm/iommu.h>
Jon Masone4650582006-06-26 13:58:14 +020042#include <asm/calgary.h>
43#include <asm/tce.h>
44#include <asm/pci-direct.h>
45#include <asm/system.h>
46#include <asm/dma.h>
Laurent Vivierb34e90b2006-12-07 02:14:06 +010047#include <asm/rio.h>
Akinobu Mitaae5830a2008-04-19 23:55:19 +090048#include <asm/bios_ebda.h>
FUJITA Tomonorid7b9f7b2009-11-10 19:46:13 +090049#include <asm/x86_init.h>
Konrad Rzeszutek Wilkd2aa2322010-08-26 13:58:02 -040050#include <asm/iommu_table.h>
Jon Masone4650582006-06-26 13:58:14 +020051
Muli Ben-Yehudabff65472006-12-07 02:14:07 +010052#ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT
53int use_calgary __read_mostly = 1;
54#else
55int use_calgary __read_mostly = 0;
56#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
57
Jon Masone4650582006-06-26 13:58:14 +020058#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +020059#define PCI_DEVICE_ID_IBM_CALIOC2 0x0308
Jon Masone4650582006-06-26 13:58:14 +020060
Jon Masone4650582006-06-26 13:58:14 +020061/* register offsets inside the host bridge space */
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +020062#define CALGARY_CONFIG_REG 0x0108
63#define PHB_CSR_OFFSET 0x0110 /* Channel Status */
Jon Masone4650582006-06-26 13:58:14 +020064#define PHB_PLSSR_OFFSET 0x0120
65#define PHB_CONFIG_RW_OFFSET 0x0160
66#define PHB_IOBASE_BAR_LOW 0x0170
67#define PHB_IOBASE_BAR_HIGH 0x0180
68#define PHB_MEM_1_LOW 0x0190
69#define PHB_MEM_1_HIGH 0x01A0
70#define PHB_IO_ADDR_SIZE 0x01B0
71#define PHB_MEM_1_SIZE 0x01C0
72#define PHB_MEM_ST_OFFSET 0x01D0
73#define PHB_AER_OFFSET 0x0200
74#define PHB_CONFIG_0_HIGH 0x0220
75#define PHB_CONFIG_0_LOW 0x0230
76#define PHB_CONFIG_0_END 0x0240
77#define PHB_MEM_2_LOW 0x02B0
78#define PHB_MEM_2_HIGH 0x02C0
79#define PHB_MEM_2_SIZE_HIGH 0x02D0
80#define PHB_MEM_2_SIZE_LOW 0x02E0
81#define PHB_DOSHOLE_OFFSET 0x08E0
82
Muli Ben-Yehudac3860102007-07-21 17:10:53 +020083/* CalIOC2 specific */
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +020084#define PHB_SAVIOR_L2 0x0DB0
85#define PHB_PAGE_MIG_CTRL 0x0DA8
86#define PHB_PAGE_MIG_DEBUG 0x0DA0
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +020087#define PHB_ROOT_COMPLEX_STATUS 0x0CB0
Muli Ben-Yehudac3860102007-07-21 17:10:53 +020088
Jon Masone4650582006-06-26 13:58:14 +020089/* PHB_CONFIG_RW */
90#define PHB_TCE_ENABLE 0x20000000
91#define PHB_SLOT_DISABLE 0x1C000000
92#define PHB_DAC_DISABLE 0x01000000
93#define PHB_MEM2_ENABLE 0x00400000
94#define PHB_MCSR_ENABLE 0x00100000
95/* TAR (Table Address Register) */
96#define TAR_SW_BITS 0x0000ffffffff800fUL
97#define TAR_VALID 0x0000000000000008UL
98/* CSR (Channel/DMA Status Register) */
99#define CSR_AGENT_MASK 0xffe0ffff
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200100/* CCR (Calgary Configuration Register) */
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200101#define CCR_2SEC_TIMEOUT 0x000000000000000EUL
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200102/* PMCR/PMDR (Page Migration Control/Debug Registers */
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200103#define PMR_SOFTSTOP 0x80000000
104#define PMR_SOFTSTOPFAULT 0x40000000
105#define PMR_HARDSTOP 0x20000000
Jon Masone4650582006-06-26 13:58:14 +0200106
Darrick J. Wong499a00e2010-06-24 14:26:47 -0700107/*
108 * The maximum PHB bus number.
109 * x3950M2 (rare): 8 chassis, 48 PHBs per chassis = 384
110 * x3950M2: 4 chassis, 48 PHBs per chassis = 192
111 * x3950 (PCIE): 8 chassis, 32 PHBs per chassis = 256
112 * x3950 (PCIX): 8 chassis, 16 PHBs per chassis = 128
113 */
Darrick J. Wongd5960432010-06-30 17:45:19 -0700114#define MAX_PHB_BUS_NUM 256
Darrick J. Wong499a00e2010-06-24 14:26:47 -0700115
116#define PHBS_PER_CALGARY 4
Jon Masone4650582006-06-26 13:58:14 +0200117
118/* register offsets in Calgary's internal register space */
119static const unsigned long tar_offsets[] = {
120 0x0580 /* TAR0 */,
121 0x0588 /* TAR1 */,
122 0x0590 /* TAR2 */,
123 0x0598 /* TAR3 */
124};
125
126static const unsigned long split_queue_offsets[] = {
127 0x4870 /* SPLIT QUEUE 0 */,
128 0x5870 /* SPLIT QUEUE 1 */,
129 0x6870 /* SPLIT QUEUE 2 */,
130 0x7870 /* SPLIT QUEUE 3 */
131};
132
133static const unsigned long phb_offsets[] = {
134 0x8000 /* PHB0 */,
135 0x9000 /* PHB1 */,
136 0xA000 /* PHB2 */,
137 0xB000 /* PHB3 */
138};
139
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100140/* PHB debug registers */
141
142static const unsigned long phb_debug_offsets[] = {
143 0x4000 /* PHB 0 DEBUG */,
144 0x5000 /* PHB 1 DEBUG */,
145 0x6000 /* PHB 2 DEBUG */,
146 0x7000 /* PHB 3 DEBUG */
147};
148
149/*
150 * STUFF register for each debug PHB,
151 * byte 1 = start bus number, byte 2 = end bus number
152 */
153
154#define PHB_DEBUG_STUFF_OFFSET 0x0020
155
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100156#define EMERGENCY_PAGES 32 /* = 128KB */
157
Jon Masone4650582006-06-26 13:58:14 +0200158unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
159static int translate_empty_slots __read_mostly = 0;
160static int calgary_detected __read_mostly = 0;
161
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100162static struct rio_table_hdr *rio_table_hdr __initdata;
163static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +0100164static struct rio_detail *rio_devs[MAX_NUMNODES * 4] __initdata;
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100165
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200166struct calgary_bus_info {
167 void *tce_space;
Muli Ben-Yehuda0577f142006-09-26 10:52:31 +0200168 unsigned char translation_disabled;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200169 signed char phbid;
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100170 void __iomem *bbar;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200171};
172
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200173static void calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
174static void calgary_tce_cache_blast(struct iommu_table *tbl);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200175static void calgary_dump_error_regs(struct iommu_table *tbl);
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200176static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200177static void calioc2_tce_cache_blast(struct iommu_table *tbl);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200178static void calioc2_dump_error_regs(struct iommu_table *tbl);
Chandru95b68de2008-07-25 01:47:55 -0700179static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl);
180static void get_tce_space_from_tar(void);
Jon Masone4650582006-06-26 13:58:14 +0200181
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200182static struct cal_chipset_ops calgary_chip_ops = {
183 .handle_quirks = calgary_handle_quirks,
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200184 .tce_cache_blast = calgary_tce_cache_blast,
185 .dump_error_regs = calgary_dump_error_regs
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200186};
187
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200188static struct cal_chipset_ops calioc2_chip_ops = {
189 .handle_quirks = calioc2_handle_quirks,
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200190 .tce_cache_blast = calioc2_tce_cache_blast,
191 .dump_error_regs = calioc2_dump_error_regs
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200192};
193
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200194static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, };
Jon Masone4650582006-06-26 13:58:14 +0200195
Muli Ben-Yehudad588ba82007-10-17 18:04:35 +0200196static inline int translation_enabled(struct iommu_table *tbl)
197{
198 /* only PHBs with translation enabled have an IOMMU table */
199 return (tbl != NULL);
200}
201
Jon Masone4650582006-06-26 13:58:14 +0200202static void iommu_range_reserve(struct iommu_table *tbl,
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200203 unsigned long start_addr, unsigned int npages)
Jon Masone4650582006-06-26 13:58:14 +0200204{
205 unsigned long index;
206 unsigned long end;
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200207 unsigned long flags;
Jon Masone4650582006-06-26 13:58:14 +0200208
209 index = start_addr >> PAGE_SHIFT;
210
211 /* bail out if we're asked to reserve a region we don't cover */
212 if (index >= tbl->it_size)
213 return;
214
215 end = index + npages;
216 if (end > tbl->it_size) /* don't go off the table */
217 end = tbl->it_size;
218
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200219 spin_lock_irqsave(&tbl->it_lock, flags);
220
Akinobu Mitaa66022c2009-12-15 16:48:28 -0800221 bitmap_set(tbl->it_map, index, npages);
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200222
223 spin_unlock_irqrestore(&tbl->it_lock, flags);
Jon Masone4650582006-06-26 13:58:14 +0200224}
225
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800226static unsigned long iommu_range_alloc(struct device *dev,
227 struct iommu_table *tbl,
228 unsigned int npages)
Jon Masone4650582006-06-26 13:58:14 +0200229{
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200230 unsigned long flags;
Jon Masone4650582006-06-26 13:58:14 +0200231 unsigned long offset;
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800232 unsigned long boundary_size;
233
234 boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
235 PAGE_SIZE) >> PAGE_SHIFT;
Jon Masone4650582006-06-26 13:58:14 +0200236
237 BUG_ON(npages == 0);
238
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200239 spin_lock_irqsave(&tbl->it_lock, flags);
240
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800241 offset = iommu_area_alloc(tbl->it_map, tbl->it_size, tbl->it_hint,
242 npages, 0, boundary_size, 0);
Jon Masone4650582006-06-26 13:58:14 +0200243 if (offset == ~0UL) {
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200244 tbl->chip_ops->tce_cache_blast(tbl);
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800245
246 offset = iommu_area_alloc(tbl->it_map, tbl->it_size, 0,
247 npages, 0, boundary_size, 0);
Jon Masone4650582006-06-26 13:58:14 +0200248 if (offset == ~0UL) {
249 printk(KERN_WARNING "Calgary: IOMMU full.\n");
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200250 spin_unlock_irqrestore(&tbl->it_lock, flags);
Jon Masone4650582006-06-26 13:58:14 +0200251 if (panic_on_overflow)
252 panic("Calgary: fix the allocator.\n");
253 else
FUJITA Tomonori8fd524b2009-11-15 21:19:53 +0900254 return DMA_ERROR_CODE;
Jon Masone4650582006-06-26 13:58:14 +0200255 }
256 }
257
Jon Masone4650582006-06-26 13:58:14 +0200258 tbl->it_hint = offset + npages;
259 BUG_ON(tbl->it_hint > tbl->it_size);
260
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200261 spin_unlock_irqrestore(&tbl->it_lock, flags);
262
Jon Masone4650582006-06-26 13:58:14 +0200263 return offset;
264}
265
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800266static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
267 void *vaddr, unsigned int npages, int direction)
Jon Masone4650582006-06-26 13:58:14 +0200268{
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200269 unsigned long entry;
FUJITA Tomonori1f7564c2009-11-15 21:19:54 +0900270 dma_addr_t ret;
Jon Masone4650582006-06-26 13:58:14 +0200271
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800272 entry = iommu_range_alloc(dev, tbl, npages);
Jon Masone4650582006-06-26 13:58:14 +0200273
FUJITA Tomonori1f7564c2009-11-15 21:19:54 +0900274 if (unlikely(entry == DMA_ERROR_CODE)) {
275 printk(KERN_WARNING "Calgary: failed to allocate %u pages in "
276 "iommu %p\n", npages, tbl);
277 return DMA_ERROR_CODE;
278 }
Jon Masone4650582006-06-26 13:58:14 +0200279
280 /* set the return dma address */
281 ret = (entry << PAGE_SHIFT) | ((unsigned long)vaddr & ~PAGE_MASK);
282
283 /* put the TCEs in the HW table */
284 tce_build(tbl, entry, npages, (unsigned long)vaddr & PAGE_MASK,
285 direction);
Jon Masone4650582006-06-26 13:58:14 +0200286 return ret;
Jon Masone4650582006-06-26 13:58:14 +0200287}
288
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200289static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
Jon Masone4650582006-06-26 13:58:14 +0200290 unsigned int npages)
291{
292 unsigned long entry;
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100293 unsigned long badend;
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200294 unsigned long flags;
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100295
296 /* were we called with bad_dma_address? */
FUJITA Tomonori8fd524b2009-11-15 21:19:53 +0900297 badend = DMA_ERROR_CODE + (EMERGENCY_PAGES * PAGE_SIZE);
298 if (unlikely((dma_addr >= DMA_ERROR_CODE) && (dma_addr < badend))) {
Arjan van de Venbde78a72008-07-08 09:51:56 -0700299 WARN(1, KERN_ERR "Calgary: driver tried unmapping bad DMA "
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100300 "address 0x%Lx\n", dma_addr);
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100301 return;
302 }
Jon Masone4650582006-06-26 13:58:14 +0200303
304 entry = dma_addr >> PAGE_SHIFT;
305
306 BUG_ON(entry + npages > tbl->it_size);
307
308 tce_free(tbl, entry, npages);
309
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200310 spin_lock_irqsave(&tbl->it_lock, flags);
311
Akinobu Mitaa66022c2009-12-15 16:48:28 -0800312 bitmap_clear(tbl->it_map, entry, npages);
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200313
314 spin_unlock_irqrestore(&tbl->it_lock, flags);
Jon Masone4650582006-06-26 13:58:14 +0200315}
316
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200317static inline struct iommu_table *find_iommu_table(struct device *dev)
318{
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200319 struct pci_dev *pdev;
320 struct pci_bus *pbus;
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200321 struct iommu_table *tbl;
322
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200323 pdev = to_pci_dev(dev);
324
Darrick J. Wong45287522009-12-02 15:05:56 -0800325 /* search up the device tree for an iommu */
Murillo Fernandes Bernardesf055a062007-08-10 22:31:00 +0200326 pbus = pdev->bus;
Darrick J. Wong45287522009-12-02 15:05:56 -0800327 do {
328 tbl = pci_iommu(pbus);
329 if (tbl && tbl->it_busno == pbus->number)
330 break;
331 tbl = NULL;
Murillo Fernandes Bernardesf055a062007-08-10 22:31:00 +0200332 pbus = pbus->parent;
Darrick J. Wong45287522009-12-02 15:05:56 -0800333 } while (pbus);
Muli Ben-Yehuda7354b072007-07-21 17:11:03 +0200334
Murillo Fernandes Bernardesf055a062007-08-10 22:31:00 +0200335 BUG_ON(tbl && (tbl->it_busno != pbus->number));
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200336
337 return tbl;
338}
339
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900340static void calgary_unmap_sg(struct device *dev, struct scatterlist *sglist,
341 int nelems,enum dma_data_direction dir,
342 struct dma_attrs *attrs)
Jon Masone4650582006-06-26 13:58:14 +0200343{
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200344 struct iommu_table *tbl = find_iommu_table(dev);
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200345 struct scatterlist *s;
346 int i;
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200347
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +0200348 if (!translation_enabled(tbl))
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200349 return;
350
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200351 for_each_sg(sglist, s, nelems, i) {
Jon Masone4650582006-06-26 13:58:14 +0200352 unsigned int npages;
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200353 dma_addr_t dma = s->dma_address;
354 unsigned int dmalen = s->dma_length;
Jon Masone4650582006-06-26 13:58:14 +0200355
356 if (dmalen == 0)
357 break;
358
Joerg Roedel036b4c52008-10-15 22:02:12 -0700359 npages = iommu_num_pages(dma, dmalen, PAGE_SIZE);
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200360 iommu_free(tbl, dma, npages);
Jon Masone4650582006-06-26 13:58:14 +0200361 }
362}
363
Yinghai Lu0b11e1c2007-07-21 17:11:05 +0200364static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900365 int nelems, enum dma_data_direction dir,
366 struct dma_attrs *attrs)
Jon Masone4650582006-06-26 13:58:14 +0200367{
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200368 struct iommu_table *tbl = find_iommu_table(dev);
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200369 struct scatterlist *s;
Jon Masone4650582006-06-26 13:58:14 +0200370 unsigned long vaddr;
371 unsigned int npages;
372 unsigned long entry;
373 int i;
374
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200375 for_each_sg(sg, s, nelems, i) {
Jens Axboe58b053e2007-10-22 20:02:46 +0200376 BUG_ON(!sg_page(s));
Jon Masone4650582006-06-26 13:58:14 +0200377
Jens Axboe58b053e2007-10-22 20:02:46 +0200378 vaddr = (unsigned long) sg_virt(s);
Joerg Roedel036b4c52008-10-15 22:02:12 -0700379 npages = iommu_num_pages(vaddr, s->length, PAGE_SIZE);
Jon Masone4650582006-06-26 13:58:14 +0200380
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800381 entry = iommu_range_alloc(dev, tbl, npages);
FUJITA Tomonori8fd524b2009-11-15 21:19:53 +0900382 if (entry == DMA_ERROR_CODE) {
Jon Masone4650582006-06-26 13:58:14 +0200383 /* makes sure unmap knows to stop */
384 s->dma_length = 0;
385 goto error;
386 }
387
388 s->dma_address = (entry << PAGE_SHIFT) | s->offset;
389
390 /* insert into HW table */
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900391 tce_build(tbl, entry, npages, vaddr & PAGE_MASK, dir);
Jon Masone4650582006-06-26 13:58:14 +0200392
393 s->dma_length = s->length;
394 }
395
Jon Masone4650582006-06-26 13:58:14 +0200396 return nelems;
397error:
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900398 calgary_unmap_sg(dev, sg, nelems, dir, NULL);
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200399 for_each_sg(sg, s, nelems, i) {
FUJITA Tomonori8fd524b2009-11-15 21:19:53 +0900400 sg->dma_address = DMA_ERROR_CODE;
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200401 sg->dma_length = 0;
Jon Masone4650582006-06-26 13:58:14 +0200402 }
Jon Masone4650582006-06-26 13:58:14 +0200403 return 0;
404}
405
FUJITA Tomonori39916052009-01-05 23:47:24 +0900406static dma_addr_t calgary_map_page(struct device *dev, struct page *page,
407 unsigned long offset, size_t size,
408 enum dma_data_direction dir,
409 struct dma_attrs *attrs)
Jon Masone4650582006-06-26 13:58:14 +0200410{
FUJITA Tomonori39916052009-01-05 23:47:24 +0900411 void *vaddr = page_address(page) + offset;
Jon Masone4650582006-06-26 13:58:14 +0200412 unsigned long uaddr;
413 unsigned int npages;
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200414 struct iommu_table *tbl = find_iommu_table(dev);
Jon Masone4650582006-06-26 13:58:14 +0200415
416 uaddr = (unsigned long)vaddr;
Joerg Roedel036b4c52008-10-15 22:02:12 -0700417 npages = iommu_num_pages(uaddr, size, PAGE_SIZE);
Jon Masone4650582006-06-26 13:58:14 +0200418
FUJITA Tomonori39916052009-01-05 23:47:24 +0900419 return iommu_alloc(dev, tbl, vaddr, npages, dir);
Jon Masone4650582006-06-26 13:58:14 +0200420}
421
FUJITA Tomonori39916052009-01-05 23:47:24 +0900422static void calgary_unmap_page(struct device *dev, dma_addr_t dma_addr,
423 size_t size, enum dma_data_direction dir,
424 struct dma_attrs *attrs)
Jon Masone4650582006-06-26 13:58:14 +0200425{
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200426 struct iommu_table *tbl = find_iommu_table(dev);
Jon Masone4650582006-06-26 13:58:14 +0200427 unsigned int npages;
428
FUJITA Tomonori39916052009-01-05 23:47:24 +0900429 npages = iommu_num_pages(dma_addr, size, PAGE_SIZE);
430 iommu_free(tbl, dma_addr, npages);
431}
432
Yinghai Lu0b11e1c2007-07-21 17:11:05 +0200433static void* calgary_alloc_coherent(struct device *dev, size_t size,
Andrzej Pietrasiewiczbaa676f2012-03-27 14:28:18 +0200434 dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs)
Jon Masone4650582006-06-26 13:58:14 +0200435{
436 void *ret = NULL;
437 dma_addr_t mapping;
438 unsigned int npages, order;
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200439 struct iommu_table *tbl = find_iommu_table(dev);
Jon Masone4650582006-06-26 13:58:14 +0200440
441 size = PAGE_ALIGN(size); /* size rounded up to full pages */
442 npages = size >> PAGE_SHIFT;
443 order = get_order(size);
444
FUJITA Tomonorif10ac8a2008-09-11 23:08:47 +0900445 flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
446
Jon Masone4650582006-06-26 13:58:14 +0200447 /* alloc enough pages (and possibly more) */
448 ret = (void *)__get_free_pages(flag, order);
449 if (!ret)
450 goto error;
451 memset(ret, 0, size);
452
Alexis Bruemmer1956a962008-07-25 19:44:51 -0700453 /* set up tces to cover the allocated range */
454 mapping = iommu_alloc(dev, tbl, ret, npages, DMA_BIDIRECTIONAL);
FUJITA Tomonori8fd524b2009-11-15 21:19:53 +0900455 if (mapping == DMA_ERROR_CODE)
Alexis Bruemmer1956a962008-07-25 19:44:51 -0700456 goto free;
457 *dma_handle = mapping;
Jon Masone4650582006-06-26 13:58:14 +0200458 return ret;
Jon Masone4650582006-06-26 13:58:14 +0200459free:
460 free_pages((unsigned long)ret, get_order(size));
461 ret = NULL;
462error:
463 return ret;
464}
465
Joerg Roedele4ad68b2008-08-19 16:32:41 +0200466static void calgary_free_coherent(struct device *dev, size_t size,
Andrzej Pietrasiewiczbaa676f2012-03-27 14:28:18 +0200467 void *vaddr, dma_addr_t dma_handle,
468 struct dma_attrs *attrs)
Joerg Roedele4ad68b2008-08-19 16:32:41 +0200469{
470 unsigned int npages;
471 struct iommu_table *tbl = find_iommu_table(dev);
472
473 size = PAGE_ALIGN(size);
474 npages = size >> PAGE_SHIFT;
475
476 iommu_free(tbl, dma_handle, npages);
477 free_pages((unsigned long)vaddr, get_order(size));
478}
479
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900480static struct dma_map_ops calgary_dma_ops = {
Andrzej Pietrasiewiczbaa676f2012-03-27 14:28:18 +0200481 .alloc = calgary_alloc_coherent,
482 .free = calgary_free_coherent,
Jon Masone4650582006-06-26 13:58:14 +0200483 .map_sg = calgary_map_sg,
484 .unmap_sg = calgary_unmap_sg,
FUJITA Tomonori39916052009-01-05 23:47:24 +0900485 .map_page = calgary_map_page,
486 .unmap_page = calgary_unmap_page,
Jon Masone4650582006-06-26 13:58:14 +0200487};
488
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100489static inline void __iomem * busno_to_bbar(unsigned char num)
490{
491 return bus_info[num].bbar;
492}
493
Jon Masone4650582006-06-26 13:58:14 +0200494static inline int busno_to_phbid(unsigned char num)
495{
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200496 return bus_info[num].phbid;
Jon Masone4650582006-06-26 13:58:14 +0200497}
498
499static inline unsigned long split_queue_offset(unsigned char num)
500{
501 size_t idx = busno_to_phbid(num);
502
503 return split_queue_offsets[idx];
504}
505
506static inline unsigned long tar_offset(unsigned char num)
507{
508 size_t idx = busno_to_phbid(num);
509
510 return tar_offsets[idx];
511}
512
513static inline unsigned long phb_offset(unsigned char num)
514{
515 size_t idx = busno_to_phbid(num);
516
517 return phb_offsets[idx];
518}
519
520static inline void __iomem* calgary_reg(void __iomem *bar, unsigned long offset)
521{
522 unsigned long target = ((unsigned long)bar) | offset;
523 return (void __iomem*)target;
524}
525
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200526static inline int is_calioc2(unsigned short device)
527{
528 return (device == PCI_DEVICE_ID_IBM_CALIOC2);
529}
530
531static inline int is_calgary(unsigned short device)
532{
533 return (device == PCI_DEVICE_ID_IBM_CALGARY);
534}
535
536static inline int is_cal_pci_dev(unsigned short device)
537{
538 return (is_calgary(device) || is_calioc2(device));
539}
540
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200541static void calgary_tce_cache_blast(struct iommu_table *tbl)
Jon Masone4650582006-06-26 13:58:14 +0200542{
543 u64 val;
544 u32 aer;
545 int i = 0;
546 void __iomem *bbar = tbl->bbar;
547 void __iomem *target;
548
549 /* disable arbitration on the bus */
550 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);
551 aer = readl(target);
552 writel(0, target);
553
554 /* read plssr to ensure it got there */
555 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET);
556 val = readl(target);
557
558 /* poll split queues until all DMA activity is done */
559 target = calgary_reg(bbar, split_queue_offset(tbl->it_busno));
560 do {
561 val = readq(target);
562 i++;
563 } while ((val & 0xff) != 0xff && i < 100);
564 if (i == 100)
565 printk(KERN_WARNING "Calgary: PCI bus not quiesced, "
566 "continuing anyway\n");
567
568 /* invalidate TCE cache */
569 target = calgary_reg(bbar, tar_offset(tbl->it_busno));
570 writeq(tbl->tar_val, target);
571
572 /* enable arbitration */
573 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);
574 writel(aer, target);
575 (void)readl(target); /* flush */
576}
577
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200578static void calioc2_tce_cache_blast(struct iommu_table *tbl)
579{
580 void __iomem *bbar = tbl->bbar;
581 void __iomem *target;
582 u64 val64;
583 u32 val;
584 int i = 0;
585 int count = 1;
586 unsigned char bus = tbl->it_busno;
587
588begin:
589 printk(KERN_DEBUG "Calgary: CalIOC2 bus 0x%x entering tce cache blast "
590 "sequence - count %d\n", bus, count);
591
592 /* 1. using the Page Migration Control reg set SoftStop */
593 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
594 val = be32_to_cpu(readl(target));
595 printk(KERN_DEBUG "1a. read 0x%x [LE] from %p\n", val, target);
596 val |= PMR_SOFTSTOP;
597 printk(KERN_DEBUG "1b. writing 0x%x [LE] to %p\n", val, target);
598 writel(cpu_to_be32(val), target);
599
600 /* 2. poll split queues until all DMA activity is done */
601 printk(KERN_DEBUG "2a. starting to poll split queues\n");
602 target = calgary_reg(bbar, split_queue_offset(bus));
603 do {
604 val64 = readq(target);
605 i++;
606 } while ((val64 & 0xff) != 0xff && i < 100);
607 if (i == 100)
608 printk(KERN_WARNING "CalIOC2: PCI bus not quiesced, "
609 "continuing anyway\n");
610
611 /* 3. poll Page Migration DEBUG for SoftStopFault */
612 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG);
613 val = be32_to_cpu(readl(target));
614 printk(KERN_DEBUG "3. read 0x%x [LE] from %p\n", val, target);
615
616 /* 4. if SoftStopFault - goto (1) */
617 if (val & PMR_SOFTSTOPFAULT) {
618 if (++count < 100)
619 goto begin;
620 else {
621 printk(KERN_WARNING "CalIOC2: too many SoftStopFaults, "
622 "aborting TCE cache flush sequence!\n");
623 return; /* pray for the best */
624 }
625 }
626
627 /* 5. Slam into HardStop by reading PHB_PAGE_MIG_CTRL */
628 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
629 printk(KERN_DEBUG "5a. slamming into HardStop by reading %p\n", target);
630 val = be32_to_cpu(readl(target));
631 printk(KERN_DEBUG "5b. read 0x%x [LE] from %p\n", val, target);
632 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG);
633 val = be32_to_cpu(readl(target));
634 printk(KERN_DEBUG "5c. read 0x%x [LE] from %p (debug)\n", val, target);
635
636 /* 6. invalidate TCE cache */
637 printk(KERN_DEBUG "6. invalidating TCE cache\n");
638 target = calgary_reg(bbar, tar_offset(bus));
639 writeq(tbl->tar_val, target);
640
641 /* 7. Re-read PMCR */
642 printk(KERN_DEBUG "7a. Re-reading PMCR\n");
643 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
644 val = be32_to_cpu(readl(target));
645 printk(KERN_DEBUG "7b. read 0x%x [LE] from %p\n", val, target);
646
647 /* 8. Remove HardStop */
648 printk(KERN_DEBUG "8a. removing HardStop from PMCR\n");
649 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
650 val = 0;
651 printk(KERN_DEBUG "8b. writing 0x%x [LE] to %p\n", val, target);
652 writel(cpu_to_be32(val), target);
653 val = be32_to_cpu(readl(target));
654 printk(KERN_DEBUG "8c. read 0x%x [LE] from %p\n", val, target);
655}
656
Jon Masone4650582006-06-26 13:58:14 +0200657static void __init calgary_reserve_mem_region(struct pci_dev *dev, u64 start,
658 u64 limit)
659{
660 unsigned int numpages;
661
662 limit = limit | 0xfffff;
663 limit++;
664
665 numpages = ((limit - start) >> PAGE_SHIFT);
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300666 iommu_range_reserve(pci_iommu(dev->bus), start, numpages);
Jon Masone4650582006-06-26 13:58:14 +0200667}
668
669static void __init calgary_reserve_peripheral_mem_1(struct pci_dev *dev)
670{
671 void __iomem *target;
672 u64 low, high, sizelow;
673 u64 start, limit;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300674 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200675 unsigned char busnum = dev->bus->number;
676 void __iomem *bbar = tbl->bbar;
677
678 /* peripheral MEM_1 region */
679 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_LOW);
680 low = be32_to_cpu(readl(target));
681 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_HIGH);
682 high = be32_to_cpu(readl(target));
683 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_SIZE);
684 sizelow = be32_to_cpu(readl(target));
685
686 start = (high << 32) | low;
687 limit = sizelow;
688
689 calgary_reserve_mem_region(dev, start, limit);
690}
691
692static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev)
693{
694 void __iomem *target;
695 u32 val32;
696 u64 low, high, sizelow, sizehigh;
697 u64 start, limit;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300698 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200699 unsigned char busnum = dev->bus->number;
700 void __iomem *bbar = tbl->bbar;
701
702 /* is it enabled? */
703 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
704 val32 = be32_to_cpu(readl(target));
705 if (!(val32 & PHB_MEM2_ENABLE))
706 return;
707
708 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_LOW);
709 low = be32_to_cpu(readl(target));
710 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_HIGH);
711 high = be32_to_cpu(readl(target));
712 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_LOW);
713 sizelow = be32_to_cpu(readl(target));
714 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_HIGH);
715 sizehigh = be32_to_cpu(readl(target));
716
717 start = (high << 32) | low;
718 limit = (sizehigh << 32) | sizelow;
719
720 calgary_reserve_mem_region(dev, start, limit);
721}
722
723/*
724 * some regions of the IO address space do not get translated, so we
725 * must not give devices IO addresses in those regions. The regions
726 * are the 640KB-1MB region and the two PCI peripheral memory holes.
727 * Reserve all of them in the IOMMU bitmap to avoid giving them out
728 * later.
729 */
730static void __init calgary_reserve_regions(struct pci_dev *dev)
731{
732 unsigned int npages;
Jon Masone4650582006-06-26 13:58:14 +0200733 u64 start;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300734 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200735
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100736 /* reserve EMERGENCY_PAGES from bad_dma_address and up */
FUJITA Tomonori8fd524b2009-11-15 21:19:53 +0900737 iommu_range_reserve(tbl, DMA_ERROR_CODE, EMERGENCY_PAGES);
Jon Masone4650582006-06-26 13:58:14 +0200738
739 /* avoid the BIOS/VGA first 640KB-1MB region */
Muli Ben-Yehudae8f20412007-07-21 17:11:01 +0200740 /* for CalIOC2 - avoid the entire first MB */
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200741 if (is_calgary(dev->device)) {
742 start = (640 * 1024);
743 npages = ((1024 - 640) * 1024) >> PAGE_SHIFT;
744 } else { /* calioc2 */
745 start = 0;
Muli Ben-Yehudae8f20412007-07-21 17:11:01 +0200746 npages = (1 * 1024 * 1024) >> PAGE_SHIFT;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200747 }
Jon Masone4650582006-06-26 13:58:14 +0200748 iommu_range_reserve(tbl, start, npages);
749
750 /* reserve the two PCI peripheral memory regions in IO space */
751 calgary_reserve_peripheral_mem_1(dev);
752 calgary_reserve_peripheral_mem_2(dev);
753}
754
755static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar)
756{
757 u64 val64;
758 u64 table_phys;
759 void __iomem *target;
760 int ret;
761 struct iommu_table *tbl;
762
763 /* build TCE tables for each PHB */
764 ret = build_tce_table(dev, bbar);
765 if (ret)
766 return ret;
767
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300768 tbl = pci_iommu(dev->bus);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200769 tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space;
Chandru95b68de2008-07-25 01:47:55 -0700770
771 if (is_kdump_kernel())
772 calgary_init_bitmap_from_tce_table(tbl);
773 else
774 tce_free(tbl, 0, tbl->it_size);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200775
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200776 if (is_calgary(dev->device))
777 tbl->chip_ops = &calgary_chip_ops;
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200778 else if (is_calioc2(dev->device))
779 tbl->chip_ops = &calioc2_chip_ops;
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200780 else
781 BUG();
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200782
Jon Masone4650582006-06-26 13:58:14 +0200783 calgary_reserve_regions(dev);
784
785 /* set TARs for each PHB */
786 target = calgary_reg(bbar, tar_offset(dev->bus->number));
787 val64 = be64_to_cpu(readq(target));
788
789 /* zero out all TAR bits under sw control */
790 val64 &= ~TAR_SW_BITS;
Jon Masone4650582006-06-26 13:58:14 +0200791 table_phys = (u64)__pa(tbl->it_base);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200792
Jon Masone4650582006-06-26 13:58:14 +0200793 val64 |= table_phys;
794
795 BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M);
796 val64 |= (u64) specified_table_size;
797
798 tbl->tar_val = cpu_to_be64(val64);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200799
Jon Masone4650582006-06-26 13:58:14 +0200800 writeq(tbl->tar_val, target);
801 readq(target); /* flush */
802
803 return 0;
804}
805
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200806static void __init calgary_free_bus(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +0200807{
808 u64 val64;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300809 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200810 void __iomem *target;
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200811 unsigned int bitmapsz;
Jon Masone4650582006-06-26 13:58:14 +0200812
813 target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number));
814 val64 = be64_to_cpu(readq(target));
815 val64 &= ~TAR_SW_BITS;
816 writeq(cpu_to_be64(val64), target);
817 readq(target); /* flush */
818
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200819 bitmapsz = tbl->it_size / BITS_PER_BYTE;
820 free_pages((unsigned long)tbl->it_map, get_order(bitmapsz));
821 tbl->it_map = NULL;
822
Jon Masone4650582006-06-26 13:58:14 +0200823 kfree(tbl);
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300824
825 set_pci_iommu(dev->bus, NULL);
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200826
827 /* Can't free bootmem allocated memory after system is up :-( */
828 bus_info[dev->bus->number].tce_space = NULL;
Jon Masone4650582006-06-26 13:58:14 +0200829}
830
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200831static void calgary_dump_error_regs(struct iommu_table *tbl)
832{
833 void __iomem *bbar = tbl->bbar;
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200834 void __iomem *target;
Muli Ben-Yehudaddbd41b2007-07-21 17:10:57 +0200835 u32 csr, plssr;
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200836
837 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);
Muli Ben-Yehudaddbd41b2007-07-21 17:10:57 +0200838 csr = be32_to_cpu(readl(target));
839
840 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET);
841 plssr = be32_to_cpu(readl(target));
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200842
843 /* If no error, the agent ID in the CSR is not valid */
844 printk(KERN_EMERG "Calgary: DMA error on Calgary PHB 0x%x, "
Muli Ben-Yehudaddbd41b2007-07-21 17:10:57 +0200845 "0x%08x@CSR 0x%08x@PLSSR\n", tbl->it_busno, csr, plssr);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200846}
847
848static void calioc2_dump_error_regs(struct iommu_table *tbl)
849{
850 void __iomem *bbar = tbl->bbar;
851 u32 csr, csmr, plssr, mck, rcstat;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200852 void __iomem *target;
853 unsigned long phboff = phb_offset(tbl->it_busno);
854 unsigned long erroff;
855 u32 errregs[7];
856 int i;
857
858 /* dump CSR */
859 target = calgary_reg(bbar, phboff | PHB_CSR_OFFSET);
860 csr = be32_to_cpu(readl(target));
861 /* dump PLSSR */
862 target = calgary_reg(bbar, phboff | PHB_PLSSR_OFFSET);
863 plssr = be32_to_cpu(readl(target));
864 /* dump CSMR */
865 target = calgary_reg(bbar, phboff | 0x290);
866 csmr = be32_to_cpu(readl(target));
867 /* dump mck */
868 target = calgary_reg(bbar, phboff | 0x800);
869 mck = be32_to_cpu(readl(target));
870
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200871 printk(KERN_EMERG "Calgary: DMA error on CalIOC2 PHB 0x%x\n",
872 tbl->it_busno);
873
874 printk(KERN_EMERG "Calgary: 0x%08x@CSR 0x%08x@PLSSR 0x%08x@CSMR 0x%08x@MCK\n",
875 csr, plssr, csmr, mck);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200876
877 /* dump rest of error regs */
878 printk(KERN_EMERG "Calgary: ");
879 for (i = 0; i < ARRAY_SIZE(errregs); i++) {
Muli Ben-Yehuda7354b072007-07-21 17:11:03 +0200880 /* err regs are at 0x810 - 0x870 */
881 erroff = (0x810 + (i * 0x10));
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200882 target = calgary_reg(bbar, phboff | erroff);
883 errregs[i] = be32_to_cpu(readl(target));
884 printk("0x%08x@0x%lx ", errregs[i], erroff);
885 }
886 printk("\n");
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200887
888 /* root complex status */
889 target = calgary_reg(bbar, phboff | PHB_ROOT_COMPLEX_STATUS);
890 rcstat = be32_to_cpu(readl(target));
891 printk(KERN_EMERG "Calgary: 0x%08x@0x%x\n", rcstat,
892 PHB_ROOT_COMPLEX_STATUS);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200893}
894
Jon Masone4650582006-06-26 13:58:14 +0200895static void calgary_watchdog(unsigned long data)
896{
897 struct pci_dev *dev = (struct pci_dev *)data;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300898 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200899 void __iomem *bbar = tbl->bbar;
900 u32 val32;
901 void __iomem *target;
902
903 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);
904 val32 = be32_to_cpu(readl(target));
905
906 /* If no error, the agent ID in the CSR is not valid */
907 if (val32 & CSR_AGENT_MASK) {
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200908 tbl->chip_ops->dump_error_regs(tbl);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200909
910 /* reset error */
Jon Masone4650582006-06-26 13:58:14 +0200911 writel(0, target);
912
913 /* Disable bus that caused the error */
914 target = calgary_reg(bbar, phb_offset(tbl->it_busno) |
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200915 PHB_CONFIG_RW_OFFSET);
Jon Masone4650582006-06-26 13:58:14 +0200916 val32 = be32_to_cpu(readl(target));
917 val32 |= PHB_SLOT_DISABLE;
918 writel(cpu_to_be32(val32), target);
919 readl(target); /* flush */
920 } else {
921 /* Reset the timer */
922 mod_timer(&tbl->watchdog_timer, jiffies + 2 * HZ);
923 }
924}
925
Muli Ben-Yehudaa2b663f2007-07-21 17:10:47 +0200926static void __init calgary_set_split_completion_timeout(void __iomem *bbar,
927 unsigned char busnum, unsigned long timeout)
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200928{
929 u64 val64;
930 void __iomem *target;
Muli Ben-Yehuda58db8542006-12-07 02:14:06 +0100931 unsigned int phb_shift = ~0; /* silence gcc */
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200932 u64 mask;
933
934 switch (busno_to_phbid(busnum)) {
935 case 0: phb_shift = (63 - 19);
936 break;
937 case 1: phb_shift = (63 - 23);
938 break;
939 case 2: phb_shift = (63 - 27);
940 break;
941 case 3: phb_shift = (63 - 35);
942 break;
943 default:
944 BUG_ON(busno_to_phbid(busnum));
945 }
946
947 target = calgary_reg(bbar, CALGARY_CONFIG_REG);
948 val64 = be64_to_cpu(readq(target));
949
950 /* zero out this PHB's timer bits */
951 mask = ~(0xFUL << phb_shift);
952 val64 &= mask;
Muli Ben-Yehudaa2b663f2007-07-21 17:10:47 +0200953 val64 |= (timeout << phb_shift);
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200954 writeq(cpu_to_be64(val64), target);
955 readq(target); /* flush */
956}
957
Sam Ravnborg31f3dff2008-02-01 17:49:42 +0100958static void __init calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev)
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200959{
960 unsigned char busnum = dev->bus->number;
961 void __iomem *bbar = tbl->bbar;
962 void __iomem *target;
963 u32 val;
964
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200965 /*
966 * CalIOC2 designers recommend setting bit 8 in 0xnDB0 to 1
967 */
968 target = calgary_reg(bbar, phb_offset(busnum) | PHB_SAVIOR_L2);
969 val = cpu_to_be32(readl(target));
970 val |= 0x00800000;
971 writel(cpu_to_be32(val), target);
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200972}
973
Sam Ravnborg31f3dff2008-02-01 17:49:42 +0100974static void __init calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev)
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +0200975{
976 unsigned char busnum = dev->bus->number;
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +0200977
978 /*
979 * Give split completion a longer timeout on bus 1 for aic94xx
980 * http://bugzilla.kernel.org/show_bug.cgi?id=7180
981 */
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200982 if (is_calgary(dev->device) && (busnum == 1))
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +0200983 calgary_set_split_completion_timeout(tbl->bbar, busnum,
984 CCR_2SEC_TIMEOUT);
985}
986
Jon Masone4650582006-06-26 13:58:14 +0200987static void __init calgary_enable_translation(struct pci_dev *dev)
988{
989 u32 val32;
990 unsigned char busnum;
991 void __iomem *target;
992 void __iomem *bbar;
993 struct iommu_table *tbl;
994
995 busnum = dev->bus->number;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300996 tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200997 bbar = tbl->bbar;
998
999 /* enable TCE in PHB Config Register */
1000 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
1001 val32 = be32_to_cpu(readl(target));
1002 val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE;
1003
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001004 printk(KERN_INFO "Calgary: enabling translation on %s PHB %#x\n",
1005 (dev->device == PCI_DEVICE_ID_IBM_CALGARY) ?
1006 "Calgary" : "CalIOC2", busnum);
Jon Masone4650582006-06-26 13:58:14 +02001007 printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this "
1008 "bus.\n");
1009
1010 writel(cpu_to_be32(val32), target);
1011 readl(target); /* flush */
1012
1013 init_timer(&tbl->watchdog_timer);
1014 tbl->watchdog_timer.function = &calgary_watchdog;
1015 tbl->watchdog_timer.data = (unsigned long)dev;
1016 mod_timer(&tbl->watchdog_timer, jiffies);
1017}
1018
1019static void __init calgary_disable_translation(struct pci_dev *dev)
1020{
1021 u32 val32;
1022 unsigned char busnum;
1023 void __iomem *target;
1024 void __iomem *bbar;
1025 struct iommu_table *tbl;
1026
1027 busnum = dev->bus->number;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001028 tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +02001029 bbar = tbl->bbar;
1030
1031 /* disable TCE in PHB Config Register */
1032 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
1033 val32 = be32_to_cpu(readl(target));
1034 val32 &= ~(PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE);
1035
Jon Mason70d666d2006-10-05 18:47:21 +02001036 printk(KERN_INFO "Calgary: disabling translation on PHB %#x!\n", busnum);
Jon Masone4650582006-06-26 13:58:14 +02001037 writel(cpu_to_be32(val32), target);
1038 readl(target); /* flush */
1039
1040 del_timer_sync(&tbl->watchdog_timer);
1041}
1042
Muli Ben-Yehudaa4fc5202006-09-26 10:52:31 +02001043static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +02001044{
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001045 pci_dev_get(dev);
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001046 set_pci_iommu(dev->bus, NULL);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001047
1048 /* is the device behind a bridge? */
1049 if (dev->bus->parent)
1050 dev->bus->parent->self = dev;
1051 else
1052 dev->bus->self = dev;
Jon Masone4650582006-06-26 13:58:14 +02001053}
1054
1055static int __init calgary_init_one(struct pci_dev *dev)
1056{
Jon Masone4650582006-06-26 13:58:14 +02001057 void __iomem *bbar;
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +02001058 struct iommu_table *tbl;
Jon Masone4650582006-06-26 13:58:14 +02001059 int ret;
1060
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001061 bbar = busno_to_bbar(dev->bus->number);
Jon Masone4650582006-06-26 13:58:14 +02001062 ret = calgary_setup_tar(dev, bbar);
1063 if (ret)
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001064 goto done;
Jon Masone4650582006-06-26 13:58:14 +02001065
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001066 pci_dev_get(dev);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001067
1068 if (dev->bus->parent) {
1069 if (dev->bus->parent->self)
1070 printk(KERN_WARNING "Calgary: IEEEE, dev %p has "
1071 "bus->parent->self!\n", dev);
1072 dev->bus->parent->self = dev;
1073 } else
1074 dev->bus->self = dev;
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +02001075
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001076 tbl = pci_iommu(dev->bus);
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +02001077 tbl->chip_ops->handle_quirks(tbl, dev);
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +02001078
Jon Masone4650582006-06-26 13:58:14 +02001079 calgary_enable_translation(dev);
1080
1081 return 0;
1082
Jon Masone4650582006-06-26 13:58:14 +02001083done:
1084 return ret;
1085}
1086
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001087static int __init calgary_locate_bbars(void)
Jon Masone4650582006-06-26 13:58:14 +02001088{
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001089 int ret;
1090 int rioidx, phb, bus;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001091 void __iomem *bbar;
1092 void __iomem *target;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001093 unsigned long offset;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001094 u8 start_bus, end_bus;
1095 u32 val;
1096
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001097 ret = -ENODATA;
1098 for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) {
1099 struct rio_detail *rio = rio_devs[rioidx];
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001100
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001101 if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY))
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001102 continue;
1103
1104 /* map entire 1MB of Calgary config space */
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001105 bbar = ioremap_nocache(rio->BBAR, 1024 * 1024);
1106 if (!bbar)
1107 goto error;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001108
1109 for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001110 offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET;
1111 target = calgary_reg(bbar, offset);
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001112
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001113 val = be32_to_cpu(readl(target));
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001114
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001115 start_bus = (u8)((val & 0x00FF0000) >> 16);
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001116 end_bus = (u8)((val & 0x0000FF00) >> 8);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001117
1118 if (end_bus) {
1119 for (bus = start_bus; bus <= end_bus; bus++) {
1120 bus_info[bus].bbar = bbar;
1121 bus_info[bus].phbid = phb;
1122 }
1123 } else {
1124 bus_info[start_bus].bbar = bbar;
1125 bus_info[start_bus].phbid = phb;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001126 }
1127 }
1128 }
1129
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001130 return 0;
1131
1132error:
1133 /* scan bus_info and iounmap any bbars we previously ioremap'd */
1134 for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++)
1135 if (bus_info[bus].bbar)
1136 iounmap(bus_info[bus].bbar);
1137
1138 return ret;
1139}
1140
1141static int __init calgary_init(void)
1142{
1143 int ret;
1144 struct pci_dev *dev = NULL;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001145 struct calgary_bus_info *info;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001146
1147 ret = calgary_locate_bbars();
1148 if (ret)
1149 return ret;
Jon Masone4650582006-06-26 13:58:14 +02001150
Chandru95b68de2008-07-25 01:47:55 -07001151 /* Purely for kdump kernel case */
1152 if (is_kdump_kernel())
1153 get_tce_space_from_tar();
1154
Jon Masondedc9932006-10-05 18:47:21 +02001155 do {
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001156 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
Jon Masone4650582006-06-26 13:58:14 +02001157 if (!dev)
1158 break;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001159 if (!is_cal_pci_dev(dev->device))
1160 continue;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001161
1162 info = &bus_info[dev->bus->number];
1163 if (info->translation_disabled) {
Jon Masone4650582006-06-26 13:58:14 +02001164 calgary_init_one_nontraslated(dev);
1165 continue;
1166 }
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001167
1168 if (!info->tce_space && !translate_empty_slots)
Jon Masone4650582006-06-26 13:58:14 +02001169 continue;
Muli Ben-Yehuda12de2572007-07-21 17:11:02 +02001170
Jon Masone4650582006-06-26 13:58:14 +02001171 ret = calgary_init_one(dev);
1172 if (ret)
1173 goto error;
Jon Masondedc9932006-10-05 18:47:21 +02001174 } while (1);
Jon Masone4650582006-06-26 13:58:14 +02001175
Alexis Bruemmer1956a962008-07-25 19:44:51 -07001176 dev = NULL;
1177 for_each_pci_dev(dev) {
1178 struct iommu_table *tbl;
1179
1180 tbl = find_iommu_table(&dev->dev);
1181
1182 if (translation_enabled(tbl))
1183 dev->dev.archdata.dma_ops = &calgary_dma_ops;
1184 }
1185
Jon Masone4650582006-06-26 13:58:14 +02001186 return ret;
1187
1188error:
Jon Masondedc9932006-10-05 18:47:21 +02001189 do {
Greg Kroah-Hartmana2b5d872008-02-13 09:32:03 -08001190 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
Muli Ben-Yehuda9f2dc462006-09-26 10:52:31 +02001191 if (!dev)
1192 break;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001193 if (!is_cal_pci_dev(dev->device))
1194 continue;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001195
1196 info = &bus_info[dev->bus->number];
1197 if (info->translation_disabled) {
Jon Masone4650582006-06-26 13:58:14 +02001198 pci_dev_put(dev);
1199 continue;
1200 }
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001201 if (!info->tce_space && !translate_empty_slots)
Jon Masone4650582006-06-26 13:58:14 +02001202 continue;
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001203
Jon Masone4650582006-06-26 13:58:14 +02001204 calgary_disable_translation(dev);
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +02001205 calgary_free_bus(dev);
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001206 pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */
Alexis Bruemmer1956a962008-07-25 19:44:51 -07001207 dev->dev.archdata.dma_ops = NULL;
Jon Masondedc9932006-10-05 18:47:21 +02001208 } while (1);
Jon Masone4650582006-06-26 13:58:14 +02001209
1210 return ret;
1211}
1212
1213static inline int __init determine_tce_table_size(u64 ram)
1214{
1215 int ret;
1216
1217 if (specified_table_size != TCE_TABLE_SIZE_UNSPECIFIED)
1218 return specified_table_size;
1219
1220 /*
1221 * Table sizes are from 0 to 7 (TCE_TABLE_SIZE_64K to
1222 * TCE_TABLE_SIZE_8M). Table size 0 has 8K entries and each
1223 * larger table size has twice as many entries, so shift the
1224 * max ram address by 13 to divide by 8K and then look at the
1225 * order of the result to choose between 0-7.
1226 */
1227 ret = get_order(ram >> 13);
1228 if (ret > TCE_TABLE_SIZE_8M)
1229 ret = TCE_TABLE_SIZE_8M;
1230
1231 return ret;
1232}
1233
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001234static int __init build_detail_arrays(void)
1235{
1236 unsigned long ptr;
David Howells85d57792008-08-18 11:58:17 +02001237 unsigned numnodes, i;
1238 int scal_detail_size, rio_detail_size;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001239
David Howells85d57792008-08-18 11:58:17 +02001240 numnodes = rio_table_hdr->num_scal_dev;
1241 if (numnodes > MAX_NUMNODES){
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001242 printk(KERN_WARNING
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001243 "Calgary: MAX_NUMNODES too low! Defined as %d, "
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001244 "but system has %d nodes.\n",
David Howells85d57792008-08-18 11:58:17 +02001245 MAX_NUMNODES, numnodes);
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001246 return -ENODEV;
1247 }
1248
1249 switch (rio_table_hdr->version){
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001250 case 2:
1251 scal_detail_size = 11;
1252 rio_detail_size = 13;
1253 break;
1254 case 3:
1255 scal_detail_size = 12;
1256 rio_detail_size = 15;
1257 break;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001258 default:
1259 printk(KERN_WARNING
1260 "Calgary: Invalid Rio Grande Table Version: %d\n",
1261 rio_table_hdr->version);
1262 return -EPROTO;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001263 }
1264
1265 ptr = ((unsigned long)rio_table_hdr) + 3;
David Howells85d57792008-08-18 11:58:17 +02001266 for (i = 0; i < numnodes; i++, ptr += scal_detail_size)
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001267 scal_devs[i] = (struct scal_detail *)ptr;
1268
1269 for (i = 0; i < rio_table_hdr->num_rio_dev;
1270 i++, ptr += rio_detail_size)
1271 rio_devs[i] = (struct rio_detail *)ptr;
1272
1273 return 0;
1274}
1275
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001276static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev)
1277{
1278 int dev;
1279 u32 val;
1280
1281 if (pci_dev == PCI_DEVICE_ID_IBM_CALIOC2) {
1282 /*
Lucas De Marchi0d2eb442011-03-17 16:24:16 -03001283 * FIXME: properly scan for devices across the
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001284 * PCI-to-PCI bridge on every CalIOC2 port.
1285 */
1286 return 1;
1287 }
1288
1289 for (dev = 1; dev < 8; dev++) {
1290 val = read_pci_config(bus, dev, 0, 0);
1291 if (val != 0xffffffff)
1292 break;
1293 }
1294 return (val != 0xffffffff);
1295}
1296
Chandru95b68de2008-07-25 01:47:55 -07001297/*
1298 * calgary_init_bitmap_from_tce_table():
Lucas De Marchi0d2eb442011-03-17 16:24:16 -03001299 * Function for kdump case. In the second/kdump kernel initialize
Chandru95b68de2008-07-25 01:47:55 -07001300 * the bitmap based on the tce table entries obtained from first kernel
1301 */
1302static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl)
1303{
1304 u64 *tp;
1305 unsigned int index;
1306 tp = ((u64 *)tbl->it_base);
1307 for (index = 0 ; index < tbl->it_size; index++) {
1308 if (*tp != 0x0)
1309 set_bit(index, tbl->it_map);
1310 tp++;
1311 }
1312}
1313
1314/*
1315 * get_tce_space_from_tar():
1316 * Function for kdump case. Get the tce tables from first kernel
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08001317 * by reading the contents of the base address register of calgary iommu
Chandru95b68de2008-07-25 01:47:55 -07001318 */
Marcin Slusarzf7106662008-08-17 17:50:52 +02001319static void __init get_tce_space_from_tar(void)
Chandru95b68de2008-07-25 01:47:55 -07001320{
1321 int bus;
1322 void __iomem *target;
1323 unsigned long tce_space;
1324
1325 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
1326 struct calgary_bus_info *info = &bus_info[bus];
1327 unsigned short pci_device;
1328 u32 val;
1329
1330 val = read_pci_config(bus, 0, 0, 0);
1331 pci_device = (val & 0xFFFF0000) >> 16;
1332
1333 if (!is_cal_pci_dev(pci_device))
1334 continue;
1335 if (info->translation_disabled)
1336 continue;
1337
1338 if (calgary_bus_has_devices(bus, pci_device) ||
1339 translate_empty_slots) {
1340 target = calgary_reg(bus_info[bus].bbar,
1341 tar_offset(bus));
1342 tce_space = be64_to_cpu(readq(target));
1343 tce_space = tce_space & TAR_SW_BITS;
1344
1345 tce_space = tce_space & (~specified_table_size);
1346 info->tce_space = (u64 *)__va(tce_space);
1347 }
1348 }
1349 return;
1350}
1351
FUJITA Tomonorif4131c62009-11-14 21:26:50 +09001352static int __init calgary_iommu_init(void)
1353{
1354 int ret;
1355
1356 /* ok, we're trying to use Calgary - let's roll */
1357 printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n");
1358
1359 ret = calgary_init();
1360 if (ret) {
1361 printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "
1362 "falling back to no_iommu\n", ret);
1363 return ret;
1364 }
1365
FUJITA Tomonorif4131c62009-11-14 21:26:50 +09001366 return 0;
1367}
FUJITA Tomonorid7b9f7b2009-11-10 19:46:13 +09001368
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001369int __init detect_calgary(void)
Jon Masone4650582006-06-26 13:58:14 +02001370{
Jon Masond2105b12006-07-29 21:42:43 +02001371 int bus;
Jon Masone4650582006-06-26 13:58:14 +02001372 void *tbl;
Jon Masond2105b12006-07-29 21:42:43 +02001373 int calgary_found = 0;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001374 unsigned long ptr;
Ingo Molnar136f1e72006-12-20 11:53:32 +01001375 unsigned int offset, prev_offset;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001376 int ret;
Jon Masone4650582006-06-26 13:58:14 +02001377
1378 /*
1379 * if the user specified iommu=off or iommu=soft or we found
1380 * another HW IOMMU already, bail out.
1381 */
FUJITA Tomonori75f1cdf2009-11-10 19:46:20 +09001382 if (no_iommu || iommu_detected)
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001383 return -ENODEV;
Jon Masone4650582006-06-26 13:58:14 +02001384
Muli Ben-Yehudabff65472006-12-07 02:14:07 +01001385 if (!use_calgary)
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001386 return -ENODEV;
Muli Ben-Yehudabff65472006-12-07 02:14:07 +01001387
Andi Kleen0637a702006-09-26 10:52:41 +02001388 if (!early_pci_allowed())
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001389 return -ENODEV;
Andi Kleen0637a702006-09-26 10:52:41 +02001390
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001391 printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n");
1392
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001393 ptr = (unsigned long)phys_to_virt(get_bios_ebda());
1394
1395 rio_table_hdr = NULL;
Ingo Molnar136f1e72006-12-20 11:53:32 +01001396 prev_offset = 0;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001397 offset = 0x180;
Ingo Molnar136f1e72006-12-20 11:53:32 +01001398 /*
1399 * The next offset is stored in the 1st word.
1400 * Only parse up until the offset increases:
1401 */
1402 while (offset > prev_offset) {
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001403 /* The block id is stored in the 2nd word */
1404 if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
1405 /* set the pointer past the offset & block id */
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001406 rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4);
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001407 break;
1408 }
Ingo Molnar136f1e72006-12-20 11:53:32 +01001409 prev_offset = offset;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001410 offset = *((unsigned short *)(ptr + offset));
1411 }
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001412 if (!rio_table_hdr) {
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001413 printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table "
1414 "in EBDA - bailing!\n");
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001415 return -ENODEV;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001416 }
1417
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001418 ret = build_detail_arrays();
1419 if (ret) {
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001420 printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n", ret);
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001421 return -ENOMEM;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001422 }
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001423
Chandru95b68de2008-07-25 01:47:55 -07001424 specified_table_size = determine_tce_table_size((is_kdump_kernel() ?
1425 saved_max_pfn : max_pfn) * PAGE_SIZE);
Jon Masone4650582006-06-26 13:58:14 +02001426
Jon Masond2105b12006-07-29 21:42:43 +02001427 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001428 struct calgary_bus_info *info = &bus_info[bus];
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001429 unsigned short pci_device;
1430 u32 val;
Jon Masond2105b12006-07-29 21:42:43 +02001431
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001432 val = read_pci_config(bus, 0, 0, 0);
1433 pci_device = (val & 0xFFFF0000) >> 16;
1434
1435 if (!is_cal_pci_dev(pci_device))
Jon Masone4650582006-06-26 13:58:14 +02001436 continue;
Jon Masond2105b12006-07-29 21:42:43 +02001437
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001438 if (info->translation_disabled)
Jon Masone4650582006-06-26 13:58:14 +02001439 continue;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001440
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001441 if (calgary_bus_has_devices(bus, pci_device) ||
1442 translate_empty_slots) {
Chandru95b68de2008-07-25 01:47:55 -07001443 /*
1444 * If it is kdump kernel, find and use tce tables
1445 * from first kernel, else allocate tce tables here
1446 */
1447 if (!is_kdump_kernel()) {
1448 tbl = alloc_tce_table();
1449 if (!tbl)
1450 goto cleanup;
1451 info->tce_space = tbl;
1452 }
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001453 calgary_found = 1;
Jon Masond2105b12006-07-29 21:42:43 +02001454 }
Jon Masone4650582006-06-26 13:58:14 +02001455 }
1456
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001457 printk(KERN_DEBUG "Calgary: finished detection, Calgary %s\n",
1458 calgary_found ? "found" : "not found");
1459
Jon Masond2105b12006-07-29 21:42:43 +02001460 if (calgary_found) {
Jon Masone4650582006-06-26 13:58:14 +02001461 iommu_detected = 1;
1462 calgary_detected = 1;
Muli Ben-Yehudade684652006-09-26 10:52:33 +02001463 printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected.\n");
FUJITA Tomonori7e055752009-04-14 12:12:29 +09001464 printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d\n",
1465 specified_table_size);
Alexis Bruemmer1956a962008-07-25 19:44:51 -07001466
FUJITA Tomonorid7b9f7b2009-11-10 19:46:13 +09001467 x86_init.iommu.iommu_init = calgary_iommu_init;
Jon Masone4650582006-06-26 13:58:14 +02001468 }
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001469 return calgary_found;
Jon Masone4650582006-06-26 13:58:14 +02001470
1471cleanup:
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001472 for (--bus; bus >= 0; --bus) {
1473 struct calgary_bus_info *info = &bus_info[bus];
1474
1475 if (info->tce_space)
1476 free_tce_table(info->tce_space);
1477 }
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001478 return -ENOMEM;
Jon Masone4650582006-06-26 13:58:14 +02001479}
1480
Jon Masone4650582006-06-26 13:58:14 +02001481static int __init calgary_parse_options(char *p)
1482{
1483 unsigned int bridge;
1484 size_t len;
1485 char* endp;
1486
1487 while (*p) {
1488 if (!strncmp(p, "64k", 3))
1489 specified_table_size = TCE_TABLE_SIZE_64K;
1490 else if (!strncmp(p, "128k", 4))
1491 specified_table_size = TCE_TABLE_SIZE_128K;
1492 else if (!strncmp(p, "256k", 4))
1493 specified_table_size = TCE_TABLE_SIZE_256K;
1494 else if (!strncmp(p, "512k", 4))
1495 specified_table_size = TCE_TABLE_SIZE_512K;
1496 else if (!strncmp(p, "1M", 2))
1497 specified_table_size = TCE_TABLE_SIZE_1M;
1498 else if (!strncmp(p, "2M", 2))
1499 specified_table_size = TCE_TABLE_SIZE_2M;
1500 else if (!strncmp(p, "4M", 2))
1501 specified_table_size = TCE_TABLE_SIZE_4M;
1502 else if (!strncmp(p, "8M", 2))
1503 specified_table_size = TCE_TABLE_SIZE_8M;
1504
1505 len = strlen("translate_empty_slots");
1506 if (!strncmp(p, "translate_empty_slots", len))
1507 translate_empty_slots = 1;
1508
1509 len = strlen("disable");
1510 if (!strncmp(p, "disable", len)) {
1511 p += len;
1512 if (*p == '=')
1513 ++p;
1514 if (*p == '\0')
1515 break;
Julia Lawalleff79ae2008-11-25 14:13:03 +01001516 bridge = simple_strtoul(p, &endp, 0);
Jon Masone4650582006-06-26 13:58:14 +02001517 if (p == endp)
1518 break;
1519
Jon Masond2105b12006-07-29 21:42:43 +02001520 if (bridge < MAX_PHB_BUS_NUM) {
Jon Masone4650582006-06-26 13:58:14 +02001521 printk(KERN_INFO "Calgary: disabling "
Jon Mason70d666d2006-10-05 18:47:21 +02001522 "translation for PHB %#x\n", bridge);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001523 bus_info[bridge].translation_disabled = 1;
Jon Masone4650582006-06-26 13:58:14 +02001524 }
1525 }
1526
1527 p = strpbrk(p, ",");
1528 if (!p)
1529 break;
1530
1531 p++; /* skip ',' */
1532 }
1533 return 1;
1534}
1535__setup("calgary=", calgary_parse_options);
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001536
1537static void __init calgary_fixup_one_tce_space(struct pci_dev *dev)
1538{
1539 struct iommu_table *tbl;
1540 unsigned int npages;
1541 int i;
1542
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001543 tbl = pci_iommu(dev->bus);
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001544
1545 for (i = 0; i < 4; i++) {
1546 struct resource *r = &dev->resource[PCI_BRIDGE_RESOURCES + i];
1547
1548 /* Don't give out TCEs that map MEM resources */
1549 if (!(r->flags & IORESOURCE_MEM))
1550 continue;
1551
1552 /* 0-based? we reserve the whole 1st MB anyway */
1553 if (!r->start)
1554 continue;
1555
1556 /* cover the whole region */
Joe Perches28f65c112011-06-09 09:13:32 -07001557 npages = resource_size(r) >> PAGE_SHIFT;
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001558 npages++;
1559
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001560 iommu_range_reserve(tbl, r->start, npages);
1561 }
1562}
1563
1564static int __init calgary_fixup_tce_spaces(void)
1565{
1566 struct pci_dev *dev = NULL;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001567 struct calgary_bus_info *info;
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001568
1569 if (no_iommu || swiotlb || !calgary_detected)
1570 return -ENODEV;
1571
Muli Ben-Yehuda12de2572007-07-21 17:11:02 +02001572 printk(KERN_DEBUG "Calgary: fixing up tce spaces\n");
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001573
1574 do {
1575 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
1576 if (!dev)
1577 break;
1578 if (!is_cal_pci_dev(dev->device))
1579 continue;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001580
1581 info = &bus_info[dev->bus->number];
1582 if (info->translation_disabled)
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001583 continue;
1584
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001585 if (!info->tce_space)
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001586 continue;
1587
1588 calgary_fixup_one_tce_space(dev);
1589
1590 } while (1);
1591
1592 return 0;
1593}
1594
1595/*
1596 * We need to be call after pcibios_assign_resources (fs_initcall level)
1597 * and before device_initcall.
1598 */
1599rootfs_initcall(calgary_fixup_tce_spaces);
Konrad Rzeszutek Wilkd2aa2322010-08-26 13:58:02 -04001600
1601IOMMU_INIT_POST(detect_calgary);