blob: 5647853053bdc39929376fc19f8113965e71d842 [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
Joe Perchesc767a542012-05-21 19:50:07 -070025#define pr_fmt(fmt) "Calgary: " fmt
26
Jon Masone4650582006-06-26 13:58:14 +020027#include <linux/kernel.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/slab.h>
31#include <linux/mm.h>
32#include <linux/spinlock.h>
33#include <linux/string.h>
Chandru95b68de2008-07-25 01:47:55 -070034#include <linux/crash_dump.h>
Jon Masone4650582006-06-26 13:58:14 +020035#include <linux/dma-mapping.h>
Christoph Hellwigfec777c2018-03-19 11:38:15 +010036#include <linux/dma-direct.h>
Akinobu Mitaa66022c2009-12-15 16:48:28 -080037#include <linux/bitmap.h>
Jon Masone4650582006-06-26 13:58:14 +020038#include <linux/pci_ids.h>
39#include <linux/pci.h>
40#include <linux/delay.h>
Jens Axboe8b87d9f2007-07-24 12:38:15 +020041#include <linux/scatterlist.h>
FUJITA Tomonori1b39b072008-02-04 22:28:10 -080042#include <linux/iommu-helper.h>
Alexis Bruemmer1956a962008-07-25 19:44:51 -070043
FUJITA Tomonori46a7fa22008-07-11 10:23:42 +090044#include <asm/iommu.h>
Jon Masone4650582006-06-26 13:58:14 +020045#include <asm/calgary.h>
46#include <asm/tce.h>
47#include <asm/pci-direct.h>
Jon Masone4650582006-06-26 13:58:14 +020048#include <asm/dma.h>
Laurent Vivierb34e90b2006-12-07 02:14:06 +010049#include <asm/rio.h>
Akinobu Mitaae5830a2008-04-19 23:55:19 +090050#include <asm/bios_ebda.h>
FUJITA Tomonorid7b9f7b2009-11-10 19:46:13 +090051#include <asm/x86_init.h>
Konrad Rzeszutek Wilkd2aa232f2010-08-26 13:58:02 -040052#include <asm/iommu_table.h>
Jon Masone4650582006-06-26 13:58:14 +020053
Christoph Hellwig8bd17c62017-05-22 10:36:43 +020054#define CALGARY_MAPPING_ERROR 0
55
Muli Ben-Yehudabff65472006-12-07 02:14:07 +010056#ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT
57int use_calgary __read_mostly = 1;
58#else
59int use_calgary __read_mostly = 0;
60#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
61
Jon Masone4650582006-06-26 13:58:14 +020062#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +020063#define PCI_DEVICE_ID_IBM_CALIOC2 0x0308
Jon Masone4650582006-06-26 13:58:14 +020064
Jon Masone4650582006-06-26 13:58:14 +020065/* register offsets inside the host bridge space */
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +020066#define CALGARY_CONFIG_REG 0x0108
67#define PHB_CSR_OFFSET 0x0110 /* Channel Status */
Jon Masone4650582006-06-26 13:58:14 +020068#define PHB_PLSSR_OFFSET 0x0120
69#define PHB_CONFIG_RW_OFFSET 0x0160
70#define PHB_IOBASE_BAR_LOW 0x0170
71#define PHB_IOBASE_BAR_HIGH 0x0180
72#define PHB_MEM_1_LOW 0x0190
73#define PHB_MEM_1_HIGH 0x01A0
74#define PHB_IO_ADDR_SIZE 0x01B0
75#define PHB_MEM_1_SIZE 0x01C0
76#define PHB_MEM_ST_OFFSET 0x01D0
77#define PHB_AER_OFFSET 0x0200
78#define PHB_CONFIG_0_HIGH 0x0220
79#define PHB_CONFIG_0_LOW 0x0230
80#define PHB_CONFIG_0_END 0x0240
81#define PHB_MEM_2_LOW 0x02B0
82#define PHB_MEM_2_HIGH 0x02C0
83#define PHB_MEM_2_SIZE_HIGH 0x02D0
84#define PHB_MEM_2_SIZE_LOW 0x02E0
85#define PHB_DOSHOLE_OFFSET 0x08E0
86
Muli Ben-Yehudac3860102007-07-21 17:10:53 +020087/* CalIOC2 specific */
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +020088#define PHB_SAVIOR_L2 0x0DB0
89#define PHB_PAGE_MIG_CTRL 0x0DA8
90#define PHB_PAGE_MIG_DEBUG 0x0DA0
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +020091#define PHB_ROOT_COMPLEX_STATUS 0x0CB0
Muli Ben-Yehudac3860102007-07-21 17:10:53 +020092
Jon Masone4650582006-06-26 13:58:14 +020093/* PHB_CONFIG_RW */
94#define PHB_TCE_ENABLE 0x20000000
95#define PHB_SLOT_DISABLE 0x1C000000
96#define PHB_DAC_DISABLE 0x01000000
97#define PHB_MEM2_ENABLE 0x00400000
98#define PHB_MCSR_ENABLE 0x00100000
99/* TAR (Table Address Register) */
100#define TAR_SW_BITS 0x0000ffffffff800fUL
101#define TAR_VALID 0x0000000000000008UL
102/* CSR (Channel/DMA Status Register) */
103#define CSR_AGENT_MASK 0xffe0ffff
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200104/* CCR (Calgary Configuration Register) */
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200105#define CCR_2SEC_TIMEOUT 0x000000000000000EUL
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200106/* PMCR/PMDR (Page Migration Control/Debug Registers */
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200107#define PMR_SOFTSTOP 0x80000000
108#define PMR_SOFTSTOPFAULT 0x40000000
109#define PMR_HARDSTOP 0x20000000
Jon Masone4650582006-06-26 13:58:14 +0200110
Darrick J. Wong499a00e2010-06-24 14:26:47 -0700111/*
112 * The maximum PHB bus number.
113 * x3950M2 (rare): 8 chassis, 48 PHBs per chassis = 384
114 * x3950M2: 4 chassis, 48 PHBs per chassis = 192
115 * x3950 (PCIE): 8 chassis, 32 PHBs per chassis = 256
116 * x3950 (PCIX): 8 chassis, 16 PHBs per chassis = 128
117 */
Darrick J. Wongd5960432010-06-30 17:45:19 -0700118#define MAX_PHB_BUS_NUM 256
Darrick J. Wong499a00e2010-06-24 14:26:47 -0700119
120#define PHBS_PER_CALGARY 4
Jon Masone4650582006-06-26 13:58:14 +0200121
122/* register offsets in Calgary's internal register space */
123static const unsigned long tar_offsets[] = {
124 0x0580 /* TAR0 */,
125 0x0588 /* TAR1 */,
126 0x0590 /* TAR2 */,
127 0x0598 /* TAR3 */
128};
129
130static const unsigned long split_queue_offsets[] = {
131 0x4870 /* SPLIT QUEUE 0 */,
132 0x5870 /* SPLIT QUEUE 1 */,
133 0x6870 /* SPLIT QUEUE 2 */,
134 0x7870 /* SPLIT QUEUE 3 */
135};
136
137static const unsigned long phb_offsets[] = {
138 0x8000 /* PHB0 */,
139 0x9000 /* PHB1 */,
140 0xA000 /* PHB2 */,
141 0xB000 /* PHB3 */
142};
143
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100144/* PHB debug registers */
145
146static const unsigned long phb_debug_offsets[] = {
147 0x4000 /* PHB 0 DEBUG */,
148 0x5000 /* PHB 1 DEBUG */,
149 0x6000 /* PHB 2 DEBUG */,
150 0x7000 /* PHB 3 DEBUG */
151};
152
153/*
154 * STUFF register for each debug PHB,
155 * byte 1 = start bus number, byte 2 = end bus number
156 */
157
158#define PHB_DEBUG_STUFF_OFFSET 0x0020
159
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100160#define EMERGENCY_PAGES 32 /* = 128KB */
161
Jon Masone4650582006-06-26 13:58:14 +0200162unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
163static int translate_empty_slots __read_mostly = 0;
164static int calgary_detected __read_mostly = 0;
165
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100166static struct rio_table_hdr *rio_table_hdr __initdata;
167static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +0100168static struct rio_detail *rio_devs[MAX_NUMNODES * 4] __initdata;
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100169
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200170struct calgary_bus_info {
171 void *tce_space;
Muli Ben-Yehuda0577f142006-09-26 10:52:31 +0200172 unsigned char translation_disabled;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200173 signed char phbid;
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100174 void __iomem *bbar;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200175};
176
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200177static void calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
178static void calgary_tce_cache_blast(struct iommu_table *tbl);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200179static void calgary_dump_error_regs(struct iommu_table *tbl);
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200180static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200181static void calioc2_tce_cache_blast(struct iommu_table *tbl);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200182static void calioc2_dump_error_regs(struct iommu_table *tbl);
Chandru95b68de2008-07-25 01:47:55 -0700183static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl);
184static void get_tce_space_from_tar(void);
Jon Masone4650582006-06-26 13:58:14 +0200185
Julia Lawalld6b56b02015-11-28 16:58:15 +0100186static const struct cal_chipset_ops calgary_chip_ops = {
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200187 .handle_quirks = calgary_handle_quirks,
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200188 .tce_cache_blast = calgary_tce_cache_blast,
189 .dump_error_regs = calgary_dump_error_regs
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200190};
191
Julia Lawalld6b56b02015-11-28 16:58:15 +0100192static const struct cal_chipset_ops calioc2_chip_ops = {
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200193 .handle_quirks = calioc2_handle_quirks,
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200194 .tce_cache_blast = calioc2_tce_cache_blast,
195 .dump_error_regs = calioc2_dump_error_regs
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200196};
197
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200198static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, };
Jon Masone4650582006-06-26 13:58:14 +0200199
Muli Ben-Yehudad588ba82007-10-17 18:04:35 +0200200static inline int translation_enabled(struct iommu_table *tbl)
201{
202 /* only PHBs with translation enabled have an IOMMU table */
203 return (tbl != NULL);
204}
205
Jon Masone4650582006-06-26 13:58:14 +0200206static void iommu_range_reserve(struct iommu_table *tbl,
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200207 unsigned long start_addr, unsigned int npages)
Jon Masone4650582006-06-26 13:58:14 +0200208{
209 unsigned long index;
210 unsigned long end;
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200211 unsigned long flags;
Jon Masone4650582006-06-26 13:58:14 +0200212
213 index = start_addr >> PAGE_SHIFT;
214
215 /* bail out if we're asked to reserve a region we don't cover */
216 if (index >= tbl->it_size)
217 return;
218
219 end = index + npages;
220 if (end > tbl->it_size) /* don't go off the table */
221 end = tbl->it_size;
222
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200223 spin_lock_irqsave(&tbl->it_lock, flags);
224
Akinobu Mitaa66022c2009-12-15 16:48:28 -0800225 bitmap_set(tbl->it_map, index, npages);
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200226
227 spin_unlock_irqrestore(&tbl->it_lock, flags);
Jon Masone4650582006-06-26 13:58:14 +0200228}
229
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800230static unsigned long iommu_range_alloc(struct device *dev,
231 struct iommu_table *tbl,
232 unsigned int npages)
Jon Masone4650582006-06-26 13:58:14 +0200233{
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200234 unsigned long flags;
Jon Masone4650582006-06-26 13:58:14 +0200235 unsigned long offset;
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800236 unsigned long boundary_size;
237
238 boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
239 PAGE_SIZE) >> PAGE_SHIFT;
Jon Masone4650582006-06-26 13:58:14 +0200240
241 BUG_ON(npages == 0);
242
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200243 spin_lock_irqsave(&tbl->it_lock, flags);
244
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800245 offset = iommu_area_alloc(tbl->it_map, tbl->it_size, tbl->it_hint,
246 npages, 0, boundary_size, 0);
Jon Masone4650582006-06-26 13:58:14 +0200247 if (offset == ~0UL) {
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200248 tbl->chip_ops->tce_cache_blast(tbl);
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800249
250 offset = iommu_area_alloc(tbl->it_map, tbl->it_size, 0,
251 npages, 0, boundary_size, 0);
Jon Masone4650582006-06-26 13:58:14 +0200252 if (offset == ~0UL) {
Joe Perchesc767a542012-05-21 19:50:07 -0700253 pr_warn("IOMMU full\n");
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200254 spin_unlock_irqrestore(&tbl->it_lock, flags);
Jon Masone4650582006-06-26 13:58:14 +0200255 if (panic_on_overflow)
256 panic("Calgary: fix the allocator.\n");
257 else
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200258 return CALGARY_MAPPING_ERROR;
Jon Masone4650582006-06-26 13:58:14 +0200259 }
260 }
261
Jon Masone4650582006-06-26 13:58:14 +0200262 tbl->it_hint = offset + npages;
263 BUG_ON(tbl->it_hint > tbl->it_size);
264
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200265 spin_unlock_irqrestore(&tbl->it_lock, flags);
266
Jon Masone4650582006-06-26 13:58:14 +0200267 return offset;
268}
269
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800270static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
271 void *vaddr, unsigned int npages, int direction)
Jon Masone4650582006-06-26 13:58:14 +0200272{
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200273 unsigned long entry;
FUJITA Tomonori1f7564c2009-11-15 21:19:54 +0900274 dma_addr_t ret;
Jon Masone4650582006-06-26 13:58:14 +0200275
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800276 entry = iommu_range_alloc(dev, tbl, npages);
Jon Masone4650582006-06-26 13:58:14 +0200277
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200278 if (unlikely(entry == CALGARY_MAPPING_ERROR)) {
Joe Perchesc767a542012-05-21 19:50:07 -0700279 pr_warn("failed to allocate %u pages in iommu %p\n",
280 npages, tbl);
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200281 return CALGARY_MAPPING_ERROR;
FUJITA Tomonori1f7564c2009-11-15 21:19:54 +0900282 }
Jon Masone4650582006-06-26 13:58:14 +0200283
284 /* set the return dma address */
285 ret = (entry << PAGE_SHIFT) | ((unsigned long)vaddr & ~PAGE_MASK);
286
287 /* put the TCEs in the HW table */
288 tce_build(tbl, entry, npages, (unsigned long)vaddr & PAGE_MASK,
289 direction);
Jon Masone4650582006-06-26 13:58:14 +0200290 return ret;
Jon Masone4650582006-06-26 13:58:14 +0200291}
292
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200293static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
Jon Masone4650582006-06-26 13:58:14 +0200294 unsigned int npages)
295{
296 unsigned long entry;
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100297 unsigned long badend;
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200298 unsigned long flags;
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100299
300 /* were we called with bad_dma_address? */
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200301 badend = CALGARY_MAPPING_ERROR + (EMERGENCY_PAGES * PAGE_SIZE);
Nikola Pajkovsky68dee8e2016-11-15 09:47:49 +0100302 if (unlikely(dma_addr < badend)) {
Arjan van de Venbde78a72008-07-08 09:51:56 -0700303 WARN(1, KERN_ERR "Calgary: driver tried unmapping bad DMA "
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100304 "address 0x%Lx\n", dma_addr);
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100305 return;
306 }
Jon Masone4650582006-06-26 13:58:14 +0200307
308 entry = dma_addr >> PAGE_SHIFT;
309
310 BUG_ON(entry + npages > tbl->it_size);
311
312 tce_free(tbl, entry, npages);
313
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200314 spin_lock_irqsave(&tbl->it_lock, flags);
315
Akinobu Mitaa66022c2009-12-15 16:48:28 -0800316 bitmap_clear(tbl->it_map, entry, npages);
Muli Ben-Yehuda820a1492007-07-21 17:11:04 +0200317
318 spin_unlock_irqrestore(&tbl->it_lock, flags);
Jon Masone4650582006-06-26 13:58:14 +0200319}
320
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200321static inline struct iommu_table *find_iommu_table(struct device *dev)
322{
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200323 struct pci_dev *pdev;
324 struct pci_bus *pbus;
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200325 struct iommu_table *tbl;
326
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200327 pdev = to_pci_dev(dev);
328
Darrick J. Wong45287522009-12-02 15:05:56 -0800329 /* search up the device tree for an iommu */
Murillo Fernandes Bernardesf055a062007-08-10 22:31:00 +0200330 pbus = pdev->bus;
Darrick J. Wong45287522009-12-02 15:05:56 -0800331 do {
332 tbl = pci_iommu(pbus);
333 if (tbl && tbl->it_busno == pbus->number)
334 break;
335 tbl = NULL;
Murillo Fernandes Bernardesf055a062007-08-10 22:31:00 +0200336 pbus = pbus->parent;
Darrick J. Wong45287522009-12-02 15:05:56 -0800337 } while (pbus);
Muli Ben-Yehuda7354b072007-07-21 17:11:03 +0200338
Murillo Fernandes Bernardesf055a062007-08-10 22:31:00 +0200339 BUG_ON(tbl && (tbl->it_busno != pbus->number));
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200340
341 return tbl;
342}
343
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900344static void calgary_unmap_sg(struct device *dev, struct scatterlist *sglist,
345 int nelems,enum dma_data_direction dir,
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700346 unsigned long attrs)
Jon Masone4650582006-06-26 13:58:14 +0200347{
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200348 struct iommu_table *tbl = find_iommu_table(dev);
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200349 struct scatterlist *s;
350 int i;
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200351
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +0200352 if (!translation_enabled(tbl))
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200353 return;
354
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200355 for_each_sg(sglist, s, nelems, i) {
Jon Masone4650582006-06-26 13:58:14 +0200356 unsigned int npages;
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200357 dma_addr_t dma = s->dma_address;
358 unsigned int dmalen = s->dma_length;
Jon Masone4650582006-06-26 13:58:14 +0200359
360 if (dmalen == 0)
361 break;
362
Joerg Roedel036b4c52008-10-15 22:02:12 -0700363 npages = iommu_num_pages(dma, dmalen, PAGE_SIZE);
Muli Ben-Yehuda3cc39bd2007-07-21 17:11:06 +0200364 iommu_free(tbl, dma, npages);
Jon Masone4650582006-06-26 13:58:14 +0200365 }
366}
367
Yinghai Lu0b11e1c2007-07-21 17:11:05 +0200368static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900369 int nelems, enum dma_data_direction dir,
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700370 unsigned long attrs)
Jon Masone4650582006-06-26 13:58:14 +0200371{
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200372 struct iommu_table *tbl = find_iommu_table(dev);
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200373 struct scatterlist *s;
Jon Masone4650582006-06-26 13:58:14 +0200374 unsigned long vaddr;
375 unsigned int npages;
376 unsigned long entry;
377 int i;
378
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200379 for_each_sg(sg, s, nelems, i) {
Jens Axboe58b053e2007-10-22 20:02:46 +0200380 BUG_ON(!sg_page(s));
Jon Masone4650582006-06-26 13:58:14 +0200381
Jens Axboe58b053e2007-10-22 20:02:46 +0200382 vaddr = (unsigned long) sg_virt(s);
Joerg Roedel036b4c52008-10-15 22:02:12 -0700383 npages = iommu_num_pages(vaddr, s->length, PAGE_SIZE);
Jon Masone4650582006-06-26 13:58:14 +0200384
FUJITA Tomonori1b39b072008-02-04 22:28:10 -0800385 entry = iommu_range_alloc(dev, tbl, npages);
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200386 if (entry == CALGARY_MAPPING_ERROR) {
Jon Masone4650582006-06-26 13:58:14 +0200387 /* makes sure unmap knows to stop */
388 s->dma_length = 0;
389 goto error;
390 }
391
392 s->dma_address = (entry << PAGE_SHIFT) | s->offset;
393
394 /* insert into HW table */
FUJITA Tomonori160c1d82009-01-05 23:59:02 +0900395 tce_build(tbl, entry, npages, vaddr & PAGE_MASK, dir);
Jon Masone4650582006-06-26 13:58:14 +0200396
397 s->dma_length = s->length;
398 }
399
Jon Masone4650582006-06-26 13:58:14 +0200400 return nelems;
401error:
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700402 calgary_unmap_sg(dev, sg, nelems, dir, 0);
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200403 for_each_sg(sg, s, nelems, i) {
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200404 sg->dma_address = CALGARY_MAPPING_ERROR;
Jens Axboe8b87d9f2007-07-24 12:38:15 +0200405 sg->dma_length = 0;
Jon Masone4650582006-06-26 13:58:14 +0200406 }
Jon Masone4650582006-06-26 13:58:14 +0200407 return 0;
408}
409
FUJITA Tomonori39916052009-01-05 23:47:24 +0900410static dma_addr_t calgary_map_page(struct device *dev, struct page *page,
411 unsigned long offset, size_t size,
412 enum dma_data_direction dir,
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700413 unsigned long attrs)
Jon Masone4650582006-06-26 13:58:14 +0200414{
FUJITA Tomonori39916052009-01-05 23:47:24 +0900415 void *vaddr = page_address(page) + offset;
Jon Masone4650582006-06-26 13:58:14 +0200416 unsigned long uaddr;
417 unsigned int npages;
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200418 struct iommu_table *tbl = find_iommu_table(dev);
Jon Masone4650582006-06-26 13:58:14 +0200419
420 uaddr = (unsigned long)vaddr;
Joerg Roedel036b4c52008-10-15 22:02:12 -0700421 npages = iommu_num_pages(uaddr, size, PAGE_SIZE);
Jon Masone4650582006-06-26 13:58:14 +0200422
FUJITA Tomonori39916052009-01-05 23:47:24 +0900423 return iommu_alloc(dev, tbl, vaddr, npages, dir);
Jon Masone4650582006-06-26 13:58:14 +0200424}
425
FUJITA Tomonori39916052009-01-05 23:47:24 +0900426static void calgary_unmap_page(struct device *dev, dma_addr_t dma_addr,
427 size_t size, enum dma_data_direction dir,
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700428 unsigned long attrs)
Jon Masone4650582006-06-26 13:58:14 +0200429{
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200430 struct iommu_table *tbl = find_iommu_table(dev);
Jon Masone4650582006-06-26 13:58:14 +0200431 unsigned int npages;
432
FUJITA Tomonori39916052009-01-05 23:47:24 +0900433 npages = iommu_num_pages(dma_addr, size, PAGE_SIZE);
434 iommu_free(tbl, dma_addr, npages);
435}
436
Yinghai Lu0b11e1c2007-07-21 17:11:05 +0200437static void* calgary_alloc_coherent(struct device *dev, size_t size,
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700438 dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs)
Jon Masone4650582006-06-26 13:58:14 +0200439{
440 void *ret = NULL;
441 dma_addr_t mapping;
442 unsigned int npages, order;
Muli Ben-Yehuda35b6dfa2007-07-21 17:10:51 +0200443 struct iommu_table *tbl = find_iommu_table(dev);
Jon Masone4650582006-06-26 13:58:14 +0200444
445 size = PAGE_ALIGN(size); /* size rounded up to full pages */
446 npages = size >> PAGE_SHIFT;
447 order = get_order(size);
448
FUJITA Tomonorif10ac8a2008-09-11 23:08:47 +0900449 flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
450
Jon Masone4650582006-06-26 13:58:14 +0200451 /* alloc enough pages (and possibly more) */
452 ret = (void *)__get_free_pages(flag, order);
453 if (!ret)
454 goto error;
455 memset(ret, 0, size);
456
Alexis Bruemmer1956a962008-07-25 19:44:51 -0700457 /* set up tces to cover the allocated range */
458 mapping = iommu_alloc(dev, tbl, ret, npages, DMA_BIDIRECTIONAL);
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200459 if (mapping == CALGARY_MAPPING_ERROR)
Alexis Bruemmer1956a962008-07-25 19:44:51 -0700460 goto free;
461 *dma_handle = mapping;
Jon Masone4650582006-06-26 13:58:14 +0200462 return ret;
Jon Masone4650582006-06-26 13:58:14 +0200463free:
464 free_pages((unsigned long)ret, get_order(size));
465 ret = NULL;
466error:
467 return ret;
468}
469
Joerg Roedele4ad68b2008-08-19 16:32:41 +0200470static void calgary_free_coherent(struct device *dev, size_t size,
Andrzej Pietrasiewiczbaa676f2012-03-27 14:28:18 +0200471 void *vaddr, dma_addr_t dma_handle,
Krzysztof Kozlowski00085f12016-08-03 13:46:00 -0700472 unsigned long attrs)
Joerg Roedele4ad68b2008-08-19 16:32:41 +0200473{
474 unsigned int npages;
475 struct iommu_table *tbl = find_iommu_table(dev);
476
477 size = PAGE_ALIGN(size);
478 npages = size >> PAGE_SHIFT;
479
480 iommu_free(tbl, dma_handle, npages);
481 free_pages((unsigned long)vaddr, get_order(size));
482}
483
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200484static int calgary_mapping_error(struct device *dev, dma_addr_t dma_addr)
485{
486 return dma_addr == CALGARY_MAPPING_ERROR;
487}
488
Bart Van Assche52997092017-01-20 13:04:01 -0800489static const struct dma_map_ops calgary_dma_ops = {
Andrzej Pietrasiewiczbaa676f2012-03-27 14:28:18 +0200490 .alloc = calgary_alloc_coherent,
491 .free = calgary_free_coherent,
Jon Masone4650582006-06-26 13:58:14 +0200492 .map_sg = calgary_map_sg,
493 .unmap_sg = calgary_unmap_sg,
FUJITA Tomonori39916052009-01-05 23:47:24 +0900494 .map_page = calgary_map_page,
495 .unmap_page = calgary_unmap_page,
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200496 .mapping_error = calgary_mapping_error,
Christoph Hellwigfec777c2018-03-19 11:38:15 +0100497 .dma_supported = dma_direct_supported,
Jon Masone4650582006-06-26 13:58:14 +0200498};
499
Laurent Vivierb34e90b2006-12-07 02:14:06 +0100500static inline void __iomem * busno_to_bbar(unsigned char num)
501{
502 return bus_info[num].bbar;
503}
504
Jon Masone4650582006-06-26 13:58:14 +0200505static inline int busno_to_phbid(unsigned char num)
506{
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200507 return bus_info[num].phbid;
Jon Masone4650582006-06-26 13:58:14 +0200508}
509
510static inline unsigned long split_queue_offset(unsigned char num)
511{
512 size_t idx = busno_to_phbid(num);
513
514 return split_queue_offsets[idx];
515}
516
517static inline unsigned long tar_offset(unsigned char num)
518{
519 size_t idx = busno_to_phbid(num);
520
521 return tar_offsets[idx];
522}
523
524static inline unsigned long phb_offset(unsigned char num)
525{
526 size_t idx = busno_to_phbid(num);
527
528 return phb_offsets[idx];
529}
530
531static inline void __iomem* calgary_reg(void __iomem *bar, unsigned long offset)
532{
533 unsigned long target = ((unsigned long)bar) | offset;
534 return (void __iomem*)target;
535}
536
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200537static inline int is_calioc2(unsigned short device)
538{
539 return (device == PCI_DEVICE_ID_IBM_CALIOC2);
540}
541
542static inline int is_calgary(unsigned short device)
543{
544 return (device == PCI_DEVICE_ID_IBM_CALGARY);
545}
546
547static inline int is_cal_pci_dev(unsigned short device)
548{
549 return (is_calgary(device) || is_calioc2(device));
550}
551
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200552static void calgary_tce_cache_blast(struct iommu_table *tbl)
Jon Masone4650582006-06-26 13:58:14 +0200553{
554 u64 val;
555 u32 aer;
556 int i = 0;
557 void __iomem *bbar = tbl->bbar;
558 void __iomem *target;
559
560 /* disable arbitration on the bus */
561 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);
562 aer = readl(target);
563 writel(0, target);
564
565 /* read plssr to ensure it got there */
566 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET);
567 val = readl(target);
568
569 /* poll split queues until all DMA activity is done */
570 target = calgary_reg(bbar, split_queue_offset(tbl->it_busno));
571 do {
572 val = readq(target);
573 i++;
574 } while ((val & 0xff) != 0xff && i < 100);
575 if (i == 100)
Joe Perchesc767a542012-05-21 19:50:07 -0700576 pr_warn("PCI bus not quiesced, continuing anyway\n");
Jon Masone4650582006-06-26 13:58:14 +0200577
578 /* invalidate TCE cache */
579 target = calgary_reg(bbar, tar_offset(tbl->it_busno));
580 writeq(tbl->tar_val, target);
581
582 /* enable arbitration */
583 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);
584 writel(aer, target);
585 (void)readl(target); /* flush */
586}
587
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200588static void calioc2_tce_cache_blast(struct iommu_table *tbl)
589{
590 void __iomem *bbar = tbl->bbar;
591 void __iomem *target;
592 u64 val64;
593 u32 val;
594 int i = 0;
595 int count = 1;
596 unsigned char bus = tbl->it_busno;
597
598begin:
599 printk(KERN_DEBUG "Calgary: CalIOC2 bus 0x%x entering tce cache blast "
600 "sequence - count %d\n", bus, count);
601
602 /* 1. using the Page Migration Control reg set SoftStop */
603 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
604 val = be32_to_cpu(readl(target));
605 printk(KERN_DEBUG "1a. read 0x%x [LE] from %p\n", val, target);
606 val |= PMR_SOFTSTOP;
607 printk(KERN_DEBUG "1b. writing 0x%x [LE] to %p\n", val, target);
608 writel(cpu_to_be32(val), target);
609
610 /* 2. poll split queues until all DMA activity is done */
611 printk(KERN_DEBUG "2a. starting to poll split queues\n");
612 target = calgary_reg(bbar, split_queue_offset(bus));
613 do {
614 val64 = readq(target);
615 i++;
616 } while ((val64 & 0xff) != 0xff && i < 100);
617 if (i == 100)
Joe Perchesc767a542012-05-21 19:50:07 -0700618 pr_warn("CalIOC2: PCI bus not quiesced, continuing anyway\n");
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200619
620 /* 3. poll Page Migration DEBUG for SoftStopFault */
621 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG);
622 val = be32_to_cpu(readl(target));
623 printk(KERN_DEBUG "3. read 0x%x [LE] from %p\n", val, target);
624
625 /* 4. if SoftStopFault - goto (1) */
626 if (val & PMR_SOFTSTOPFAULT) {
627 if (++count < 100)
628 goto begin;
629 else {
Joe Perchesc767a542012-05-21 19:50:07 -0700630 pr_warn("CalIOC2: too many SoftStopFaults, aborting TCE cache flush sequence!\n");
Muli Ben-Yehuda00be3fa2007-07-21 17:10:54 +0200631 return; /* pray for the best */
632 }
633 }
634
635 /* 5. Slam into HardStop by reading PHB_PAGE_MIG_CTRL */
636 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
637 printk(KERN_DEBUG "5a. slamming into HardStop by reading %p\n", target);
638 val = be32_to_cpu(readl(target));
639 printk(KERN_DEBUG "5b. read 0x%x [LE] from %p\n", val, target);
640 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG);
641 val = be32_to_cpu(readl(target));
642 printk(KERN_DEBUG "5c. read 0x%x [LE] from %p (debug)\n", val, target);
643
644 /* 6. invalidate TCE cache */
645 printk(KERN_DEBUG "6. invalidating TCE cache\n");
646 target = calgary_reg(bbar, tar_offset(bus));
647 writeq(tbl->tar_val, target);
648
649 /* 7. Re-read PMCR */
650 printk(KERN_DEBUG "7a. Re-reading PMCR\n");
651 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
652 val = be32_to_cpu(readl(target));
653 printk(KERN_DEBUG "7b. read 0x%x [LE] from %p\n", val, target);
654
655 /* 8. Remove HardStop */
656 printk(KERN_DEBUG "8a. removing HardStop from PMCR\n");
657 target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);
658 val = 0;
659 printk(KERN_DEBUG "8b. writing 0x%x [LE] to %p\n", val, target);
660 writel(cpu_to_be32(val), target);
661 val = be32_to_cpu(readl(target));
662 printk(KERN_DEBUG "8c. read 0x%x [LE] from %p\n", val, target);
663}
664
Jon Masone4650582006-06-26 13:58:14 +0200665static void __init calgary_reserve_mem_region(struct pci_dev *dev, u64 start,
666 u64 limit)
667{
668 unsigned int numpages;
669
670 limit = limit | 0xfffff;
671 limit++;
672
673 numpages = ((limit - start) >> PAGE_SHIFT);
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300674 iommu_range_reserve(pci_iommu(dev->bus), start, numpages);
Jon Masone4650582006-06-26 13:58:14 +0200675}
676
677static void __init calgary_reserve_peripheral_mem_1(struct pci_dev *dev)
678{
679 void __iomem *target;
680 u64 low, high, sizelow;
681 u64 start, limit;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300682 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200683 unsigned char busnum = dev->bus->number;
684 void __iomem *bbar = tbl->bbar;
685
686 /* peripheral MEM_1 region */
687 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_LOW);
688 low = be32_to_cpu(readl(target));
689 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_HIGH);
690 high = be32_to_cpu(readl(target));
691 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_SIZE);
692 sizelow = be32_to_cpu(readl(target));
693
694 start = (high << 32) | low;
695 limit = sizelow;
696
697 calgary_reserve_mem_region(dev, start, limit);
698}
699
700static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev)
701{
702 void __iomem *target;
703 u32 val32;
704 u64 low, high, sizelow, sizehigh;
705 u64 start, limit;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300706 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200707 unsigned char busnum = dev->bus->number;
708 void __iomem *bbar = tbl->bbar;
709
710 /* is it enabled? */
711 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
712 val32 = be32_to_cpu(readl(target));
713 if (!(val32 & PHB_MEM2_ENABLE))
714 return;
715
716 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_LOW);
717 low = be32_to_cpu(readl(target));
718 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_HIGH);
719 high = be32_to_cpu(readl(target));
720 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_LOW);
721 sizelow = be32_to_cpu(readl(target));
722 target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_HIGH);
723 sizehigh = be32_to_cpu(readl(target));
724
725 start = (high << 32) | low;
726 limit = (sizehigh << 32) | sizelow;
727
728 calgary_reserve_mem_region(dev, start, limit);
729}
730
731/*
732 * some regions of the IO address space do not get translated, so we
733 * must not give devices IO addresses in those regions. The regions
734 * are the 640KB-1MB region and the two PCI peripheral memory holes.
735 * Reserve all of them in the IOMMU bitmap to avoid giving them out
736 * later.
737 */
738static void __init calgary_reserve_regions(struct pci_dev *dev)
739{
740 unsigned int npages;
Jon Masone4650582006-06-26 13:58:14 +0200741 u64 start;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300742 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200743
Muli Ben-Yehuda310adfd2007-02-13 13:26:24 +0100744 /* reserve EMERGENCY_PAGES from bad_dma_address and up */
Christoph Hellwig8bd17c62017-05-22 10:36:43 +0200745 iommu_range_reserve(tbl, CALGARY_MAPPING_ERROR, EMERGENCY_PAGES);
Jon Masone4650582006-06-26 13:58:14 +0200746
747 /* avoid the BIOS/VGA first 640KB-1MB region */
Muli Ben-Yehudae8f20412007-07-21 17:11:01 +0200748 /* for CalIOC2 - avoid the entire first MB */
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200749 if (is_calgary(dev->device)) {
750 start = (640 * 1024);
751 npages = ((1024 - 640) * 1024) >> PAGE_SHIFT;
752 } else { /* calioc2 */
753 start = 0;
Muli Ben-Yehudae8f20412007-07-21 17:11:01 +0200754 npages = (1 * 1024 * 1024) >> PAGE_SHIFT;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200755 }
Jon Masone4650582006-06-26 13:58:14 +0200756 iommu_range_reserve(tbl, start, npages);
757
758 /* reserve the two PCI peripheral memory regions in IO space */
759 calgary_reserve_peripheral_mem_1(dev);
760 calgary_reserve_peripheral_mem_2(dev);
761}
762
763static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar)
764{
765 u64 val64;
766 u64 table_phys;
767 void __iomem *target;
768 int ret;
769 struct iommu_table *tbl;
770
771 /* build TCE tables for each PHB */
772 ret = build_tce_table(dev, bbar);
773 if (ret)
774 return ret;
775
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300776 tbl = pci_iommu(dev->bus);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200777 tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space;
Chandru95b68de2008-07-25 01:47:55 -0700778
779 if (is_kdump_kernel())
780 calgary_init_bitmap_from_tce_table(tbl);
781 else
782 tce_free(tbl, 0, tbl->it_size);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +0200783
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200784 if (is_calgary(dev->device))
785 tbl->chip_ops = &calgary_chip_ops;
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200786 else if (is_calioc2(dev->device))
787 tbl->chip_ops = &calioc2_chip_ops;
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200788 else
789 BUG();
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +0200790
Jon Masone4650582006-06-26 13:58:14 +0200791 calgary_reserve_regions(dev);
792
793 /* set TARs for each PHB */
794 target = calgary_reg(bbar, tar_offset(dev->bus->number));
795 val64 = be64_to_cpu(readq(target));
796
797 /* zero out all TAR bits under sw control */
798 val64 &= ~TAR_SW_BITS;
Jon Masone4650582006-06-26 13:58:14 +0200799 table_phys = (u64)__pa(tbl->it_base);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200800
Jon Masone4650582006-06-26 13:58:14 +0200801 val64 |= table_phys;
802
803 BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M);
804 val64 |= (u64) specified_table_size;
805
806 tbl->tar_val = cpu_to_be64(val64);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200807
Jon Masone4650582006-06-26 13:58:14 +0200808 writeq(tbl->tar_val, target);
809 readq(target); /* flush */
810
811 return 0;
812}
813
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200814static void __init calgary_free_bus(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +0200815{
816 u64 val64;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300817 struct iommu_table *tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +0200818 void __iomem *target;
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200819 unsigned int bitmapsz;
Jon Masone4650582006-06-26 13:58:14 +0200820
821 target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number));
822 val64 = be64_to_cpu(readq(target));
823 val64 &= ~TAR_SW_BITS;
824 writeq(cpu_to_be64(val64), target);
825 readq(target); /* flush */
826
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200827 bitmapsz = tbl->it_size / BITS_PER_BYTE;
828 free_pages((unsigned long)tbl->it_map, get_order(bitmapsz));
829 tbl->it_map = NULL;
830
Jon Masone4650582006-06-26 13:58:14 +0200831 kfree(tbl);
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +0300832
833 set_pci_iommu(dev->bus, NULL);
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +0200834
835 /* Can't free bootmem allocated memory after system is up :-( */
836 bus_info[dev->bus->number].tce_space = NULL;
Jon Masone4650582006-06-26 13:58:14 +0200837}
838
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200839static void calgary_dump_error_regs(struct iommu_table *tbl)
840{
841 void __iomem *bbar = tbl->bbar;
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200842 void __iomem *target;
Muli Ben-Yehudaddbd41b2007-07-21 17:10:57 +0200843 u32 csr, plssr;
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200844
845 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);
Muli Ben-Yehudaddbd41b2007-07-21 17:10:57 +0200846 csr = be32_to_cpu(readl(target));
847
848 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET);
849 plssr = be32_to_cpu(readl(target));
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200850
851 /* If no error, the agent ID in the CSR is not valid */
Joe Perchesc767a542012-05-21 19:50:07 -0700852 pr_emerg("DMA error on Calgary PHB 0x%x, 0x%08x@CSR 0x%08x@PLSSR\n",
853 tbl->it_busno, csr, plssr);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200854}
855
856static void calioc2_dump_error_regs(struct iommu_table *tbl)
857{
858 void __iomem *bbar = tbl->bbar;
859 u32 csr, csmr, plssr, mck, rcstat;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200860 void __iomem *target;
861 unsigned long phboff = phb_offset(tbl->it_busno);
862 unsigned long erroff;
863 u32 errregs[7];
864 int i;
865
866 /* dump CSR */
867 target = calgary_reg(bbar, phboff | PHB_CSR_OFFSET);
868 csr = be32_to_cpu(readl(target));
869 /* dump PLSSR */
870 target = calgary_reg(bbar, phboff | PHB_PLSSR_OFFSET);
871 plssr = be32_to_cpu(readl(target));
872 /* dump CSMR */
873 target = calgary_reg(bbar, phboff | 0x290);
874 csmr = be32_to_cpu(readl(target));
875 /* dump mck */
876 target = calgary_reg(bbar, phboff | 0x800);
877 mck = be32_to_cpu(readl(target));
878
Joe Perchesc767a542012-05-21 19:50:07 -0700879 pr_emerg("DMA error on CalIOC2 PHB 0x%x\n", tbl->it_busno);
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200880
Joe Perchesc767a542012-05-21 19:50:07 -0700881 pr_emerg("0x%08x@CSR 0x%08x@PLSSR 0x%08x@CSMR 0x%08x@MCK\n",
882 csr, plssr, csmr, mck);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200883
884 /* dump rest of error regs */
Joe Perchesc767a542012-05-21 19:50:07 -0700885 pr_emerg("");
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200886 for (i = 0; i < ARRAY_SIZE(errregs); i++) {
Muli Ben-Yehuda7354b072007-07-21 17:11:03 +0200887 /* err regs are at 0x810 - 0x870 */
888 erroff = (0x810 + (i * 0x10));
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200889 target = calgary_reg(bbar, phboff | erroff);
890 errregs[i] = be32_to_cpu(readl(target));
Joe Perchesc767a542012-05-21 19:50:07 -0700891 pr_cont("0x%08x@0x%lx ", errregs[i], erroff);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200892 }
Joe Perchesc767a542012-05-21 19:50:07 -0700893 pr_cont("\n");
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200894
895 /* root complex status */
896 target = calgary_reg(bbar, phboff | PHB_ROOT_COMPLEX_STATUS);
897 rcstat = be32_to_cpu(readl(target));
898 printk(KERN_EMERG "Calgary: 0x%08x@0x%x\n", rcstat,
899 PHB_ROOT_COMPLEX_STATUS);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200900}
901
Kees Cook31426922017-10-19 16:37:43 -0700902static void calgary_watchdog(struct timer_list *t)
Jon Masone4650582006-06-26 13:58:14 +0200903{
Kees Cook31426922017-10-19 16:37:43 -0700904 struct iommu_table *tbl = from_timer(tbl, t, watchdog_timer);
Jon Masone4650582006-06-26 13:58:14 +0200905 void __iomem *bbar = tbl->bbar;
906 u32 val32;
907 void __iomem *target;
908
909 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);
910 val32 = be32_to_cpu(readl(target));
911
912 /* If no error, the agent ID in the CSR is not valid */
913 if (val32 & CSR_AGENT_MASK) {
Muli Ben-Yehuda8cb32dc2007-07-21 17:10:55 +0200914 tbl->chip_ops->dump_error_regs(tbl);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200915
916 /* reset error */
Jon Masone4650582006-06-26 13:58:14 +0200917 writel(0, target);
918
919 /* Disable bus that caused the error */
920 target = calgary_reg(bbar, phb_offset(tbl->it_busno) |
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +0200921 PHB_CONFIG_RW_OFFSET);
Jon Masone4650582006-06-26 13:58:14 +0200922 val32 = be32_to_cpu(readl(target));
923 val32 |= PHB_SLOT_DISABLE;
924 writel(cpu_to_be32(val32), target);
925 readl(target); /* flush */
926 } else {
927 /* Reset the timer */
928 mod_timer(&tbl->watchdog_timer, jiffies + 2 * HZ);
929 }
930}
931
Muli Ben-Yehudaa2b663f2007-07-21 17:10:47 +0200932static void __init calgary_set_split_completion_timeout(void __iomem *bbar,
933 unsigned char busnum, unsigned long timeout)
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200934{
935 u64 val64;
936 void __iomem *target;
Muli Ben-Yehuda58db8542006-12-07 02:14:06 +0100937 unsigned int phb_shift = ~0; /* silence gcc */
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200938 u64 mask;
939
940 switch (busno_to_phbid(busnum)) {
941 case 0: phb_shift = (63 - 19);
942 break;
943 case 1: phb_shift = (63 - 23);
944 break;
945 case 2: phb_shift = (63 - 27);
946 break;
947 case 3: phb_shift = (63 - 35);
948 break;
949 default:
950 BUG_ON(busno_to_phbid(busnum));
951 }
952
953 target = calgary_reg(bbar, CALGARY_CONFIG_REG);
954 val64 = be64_to_cpu(readq(target));
955
956 /* zero out this PHB's timer bits */
957 mask = ~(0xFUL << phb_shift);
958 val64 &= mask;
Muli Ben-Yehudaa2b663f2007-07-21 17:10:47 +0200959 val64 |= (timeout << phb_shift);
Muli Ben-Yehudacb01fc72006-10-22 00:41:15 +0200960 writeq(cpu_to_be64(val64), target);
961 readq(target); /* flush */
962}
963
Sam Ravnborg31f3dff2008-02-01 17:49:42 +0100964static void __init calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev)
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200965{
966 unsigned char busnum = dev->bus->number;
967 void __iomem *bbar = tbl->bbar;
968 void __iomem *target;
969 u32 val;
970
Muli Ben-Yehuda8bcf7702007-07-21 17:11:00 +0200971 /*
972 * CalIOC2 designers recommend setting bit 8 in 0xnDB0 to 1
973 */
974 target = calgary_reg(bbar, phb_offset(busnum) | PHB_SAVIOR_L2);
975 val = cpu_to_be32(readl(target));
976 val |= 0x00800000;
977 writel(cpu_to_be32(val), target);
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200978}
979
Sam Ravnborg31f3dff2008-02-01 17:49:42 +0100980static void __init calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev)
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +0200981{
982 unsigned char busnum = dev->bus->number;
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +0200983
984 /*
985 * Give split completion a longer timeout on bus 1 for aic94xx
986 * http://bugzilla.kernel.org/show_bug.cgi?id=7180
987 */
Muli Ben-Yehudac3860102007-07-21 17:10:53 +0200988 if (is_calgary(dev->device) && (busnum == 1))
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +0200989 calgary_set_split_completion_timeout(tbl->bbar, busnum,
990 CCR_2SEC_TIMEOUT);
991}
992
Jon Masone4650582006-06-26 13:58:14 +0200993static void __init calgary_enable_translation(struct pci_dev *dev)
994{
995 u32 val32;
996 unsigned char busnum;
997 void __iomem *target;
998 void __iomem *bbar;
999 struct iommu_table *tbl;
1000
1001 busnum = dev->bus->number;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001002 tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +02001003 bbar = tbl->bbar;
1004
1005 /* enable TCE in PHB Config Register */
1006 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
1007 val32 = be32_to_cpu(readl(target));
1008 val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE;
1009
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001010 printk(KERN_INFO "Calgary: enabling translation on %s PHB %#x\n",
1011 (dev->device == PCI_DEVICE_ID_IBM_CALGARY) ?
1012 "Calgary" : "CalIOC2", busnum);
Jon Masone4650582006-06-26 13:58:14 +02001013 printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this "
1014 "bus.\n");
1015
1016 writel(cpu_to_be32(val32), target);
1017 readl(target); /* flush */
1018
Kees Cook31426922017-10-19 16:37:43 -07001019 timer_setup(&tbl->watchdog_timer, calgary_watchdog, 0);
Jon Masone4650582006-06-26 13:58:14 +02001020 mod_timer(&tbl->watchdog_timer, jiffies);
1021}
1022
1023static void __init calgary_disable_translation(struct pci_dev *dev)
1024{
1025 u32 val32;
1026 unsigned char busnum;
1027 void __iomem *target;
1028 void __iomem *bbar;
1029 struct iommu_table *tbl;
1030
1031 busnum = dev->bus->number;
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001032 tbl = pci_iommu(dev->bus);
Jon Masone4650582006-06-26 13:58:14 +02001033 bbar = tbl->bbar;
1034
1035 /* disable TCE in PHB Config Register */
1036 target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);
1037 val32 = be32_to_cpu(readl(target));
1038 val32 &= ~(PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE);
1039
Jon Mason70d666d2006-10-05 18:47:21 +02001040 printk(KERN_INFO "Calgary: disabling translation on PHB %#x!\n", busnum);
Jon Masone4650582006-06-26 13:58:14 +02001041 writel(cpu_to_be32(val32), target);
1042 readl(target); /* flush */
1043
1044 del_timer_sync(&tbl->watchdog_timer);
1045}
1046
Muli Ben-Yehudaa4fc5202006-09-26 10:52:31 +02001047static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
Jon Masone4650582006-06-26 13:58:14 +02001048{
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001049 pci_dev_get(dev);
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001050 set_pci_iommu(dev->bus, NULL);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001051
1052 /* is the device behind a bridge? */
1053 if (dev->bus->parent)
1054 dev->bus->parent->self = dev;
1055 else
1056 dev->bus->self = dev;
Jon Masone4650582006-06-26 13:58:14 +02001057}
1058
1059static int __init calgary_init_one(struct pci_dev *dev)
1060{
Jon Masone4650582006-06-26 13:58:14 +02001061 void __iomem *bbar;
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +02001062 struct iommu_table *tbl;
Jon Masone4650582006-06-26 13:58:14 +02001063 int ret;
1064
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001065 bbar = busno_to_bbar(dev->bus->number);
Jon Masone4650582006-06-26 13:58:14 +02001066 ret = calgary_setup_tar(dev, bbar);
1067 if (ret)
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001068 goto done;
Jon Masone4650582006-06-26 13:58:14 +02001069
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001070 pci_dev_get(dev);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001071
1072 if (dev->bus->parent) {
1073 if (dev->bus->parent->self)
1074 printk(KERN_WARNING "Calgary: IEEEE, dev %p has "
1075 "bus->parent->self!\n", dev);
1076 dev->bus->parent->self = dev;
1077 } else
1078 dev->bus->self = dev;
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +02001079
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001080 tbl = pci_iommu(dev->bus);
Muli Ben-Yehudaff297b82007-07-21 17:10:50 +02001081 tbl->chip_ops->handle_quirks(tbl, dev);
Muli Ben-Yehudab8d2ea12007-07-21 17:10:49 +02001082
Jon Masone4650582006-06-26 13:58:14 +02001083 calgary_enable_translation(dev);
1084
1085 return 0;
1086
Jon Masone4650582006-06-26 13:58:14 +02001087done:
1088 return ret;
1089}
1090
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001091static int __init calgary_locate_bbars(void)
Jon Masone4650582006-06-26 13:58:14 +02001092{
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001093 int ret;
1094 int rioidx, phb, bus;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001095 void __iomem *bbar;
1096 void __iomem *target;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001097 unsigned long offset;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001098 u8 start_bus, end_bus;
1099 u32 val;
1100
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001101 ret = -ENODATA;
1102 for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) {
1103 struct rio_detail *rio = rio_devs[rioidx];
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001104
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001105 if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY))
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001106 continue;
1107
1108 /* map entire 1MB of Calgary config space */
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001109 bbar = ioremap_nocache(rio->BBAR, 1024 * 1024);
1110 if (!bbar)
1111 goto error;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001112
1113 for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001114 offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET;
1115 target = calgary_reg(bbar, offset);
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001116
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001117 val = be32_to_cpu(readl(target));
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001118
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001119 start_bus = (u8)((val & 0x00FF0000) >> 16);
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001120 end_bus = (u8)((val & 0x0000FF00) >> 8);
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001121
1122 if (end_bus) {
1123 for (bus = start_bus; bus <= end_bus; bus++) {
1124 bus_info[bus].bbar = bbar;
1125 bus_info[bus].phbid = phb;
1126 }
1127 } else {
1128 bus_info[start_bus].bbar = bbar;
1129 bus_info[start_bus].phbid = phb;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001130 }
1131 }
1132 }
1133
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001134 return 0;
1135
1136error:
1137 /* scan bus_info and iounmap any bbars we previously ioremap'd */
1138 for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++)
1139 if (bus_info[bus].bbar)
1140 iounmap(bus_info[bus].bbar);
1141
1142 return ret;
1143}
1144
1145static int __init calgary_init(void)
1146{
1147 int ret;
1148 struct pci_dev *dev = NULL;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001149 struct calgary_bus_info *info;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001150
1151 ret = calgary_locate_bbars();
1152 if (ret)
1153 return ret;
Jon Masone4650582006-06-26 13:58:14 +02001154
Chandru95b68de2008-07-25 01:47:55 -07001155 /* Purely for kdump kernel case */
1156 if (is_kdump_kernel())
1157 get_tce_space_from_tar();
1158
Jon Masondedc9932006-10-05 18:47:21 +02001159 do {
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001160 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
Jon Masone4650582006-06-26 13:58:14 +02001161 if (!dev)
1162 break;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001163 if (!is_cal_pci_dev(dev->device))
1164 continue;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001165
1166 info = &bus_info[dev->bus->number];
1167 if (info->translation_disabled) {
Jon Masone4650582006-06-26 13:58:14 +02001168 calgary_init_one_nontraslated(dev);
1169 continue;
1170 }
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001171
1172 if (!info->tce_space && !translate_empty_slots)
Jon Masone4650582006-06-26 13:58:14 +02001173 continue;
Muli Ben-Yehuda12de2572007-07-21 17:11:02 +02001174
Jon Masone4650582006-06-26 13:58:14 +02001175 ret = calgary_init_one(dev);
1176 if (ret)
1177 goto error;
Jon Masondedc9932006-10-05 18:47:21 +02001178 } while (1);
Jon Masone4650582006-06-26 13:58:14 +02001179
Alexis Bruemmer1956a962008-07-25 19:44:51 -07001180 dev = NULL;
1181 for_each_pci_dev(dev) {
1182 struct iommu_table *tbl;
1183
1184 tbl = find_iommu_table(&dev->dev);
1185
1186 if (translation_enabled(tbl))
Bart Van Assche56579332017-01-20 13:04:02 -08001187 dev->dev.dma_ops = &calgary_dma_ops;
Alexis Bruemmer1956a962008-07-25 19:44:51 -07001188 }
1189
Jon Masone4650582006-06-26 13:58:14 +02001190 return ret;
1191
1192error:
Jon Masondedc9932006-10-05 18:47:21 +02001193 do {
Greg Kroah-Hartmana2b5d872008-02-13 09:32:03 -08001194 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
Muli Ben-Yehuda9f2dc462006-09-26 10:52:31 +02001195 if (!dev)
1196 break;
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001197 if (!is_cal_pci_dev(dev->device))
1198 continue;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001199
1200 info = &bus_info[dev->bus->number];
1201 if (info->translation_disabled) {
Jon Masone4650582006-06-26 13:58:14 +02001202 pci_dev_put(dev);
1203 continue;
1204 }
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001205 if (!info->tce_space && !translate_empty_slots)
Jon Masone4650582006-06-26 13:58:14 +02001206 continue;
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001207
Jon Masone4650582006-06-26 13:58:14 +02001208 calgary_disable_translation(dev);
Muli Ben-Yehudab8f4fe62006-09-26 10:52:31 +02001209 calgary_free_bus(dev);
Muli Ben-Yehuda871b1702006-09-26 10:52:31 +02001210 pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */
Bart Van Assche56579332017-01-20 13:04:02 -08001211 dev->dev.dma_ops = NULL;
Jon Masondedc9932006-10-05 18:47:21 +02001212 } while (1);
Jon Masone4650582006-06-26 13:58:14 +02001213
1214 return ret;
1215}
1216
WANG Chao0534af02014-03-10 22:52:00 +08001217static inline int __init determine_tce_table_size(void)
Jon Masone4650582006-06-26 13:58:14 +02001218{
1219 int ret;
1220
1221 if (specified_table_size != TCE_TABLE_SIZE_UNSPECIFIED)
1222 return specified_table_size;
1223
WANG Chao0534af02014-03-10 22:52:00 +08001224 if (is_kdump_kernel() && saved_max_pfn) {
1225 /*
1226 * Table sizes are from 0 to 7 (TCE_TABLE_SIZE_64K to
1227 * TCE_TABLE_SIZE_8M). Table size 0 has 8K entries and each
1228 * larger table size has twice as many entries, so shift the
1229 * max ram address by 13 to divide by 8K and then look at the
1230 * order of the result to choose between 0-7.
1231 */
1232 ret = get_order((saved_max_pfn * PAGE_SIZE) >> 13);
1233 if (ret > TCE_TABLE_SIZE_8M)
1234 ret = TCE_TABLE_SIZE_8M;
1235 } else {
1236 /*
1237 * Use 8M by default (suggested by Muli) if it's not
1238 * kdump kernel and saved_max_pfn isn't set.
1239 */
Jon Masone4650582006-06-26 13:58:14 +02001240 ret = TCE_TABLE_SIZE_8M;
WANG Chao0534af02014-03-10 22:52:00 +08001241 }
Jon Masone4650582006-06-26 13:58:14 +02001242
1243 return ret;
1244}
1245
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001246static int __init build_detail_arrays(void)
1247{
1248 unsigned long ptr;
David Howells85d57792008-08-18 11:58:17 +02001249 unsigned numnodes, i;
1250 int scal_detail_size, rio_detail_size;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001251
David Howells85d57792008-08-18 11:58:17 +02001252 numnodes = rio_table_hdr->num_scal_dev;
1253 if (numnodes > MAX_NUMNODES){
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001254 printk(KERN_WARNING
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001255 "Calgary: MAX_NUMNODES too low! Defined as %d, "
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001256 "but system has %d nodes.\n",
David Howells85d57792008-08-18 11:58:17 +02001257 MAX_NUMNODES, numnodes);
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001258 return -ENODEV;
1259 }
1260
1261 switch (rio_table_hdr->version){
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001262 case 2:
1263 scal_detail_size = 11;
1264 rio_detail_size = 13;
1265 break;
1266 case 3:
1267 scal_detail_size = 12;
1268 rio_detail_size = 15;
1269 break;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001270 default:
1271 printk(KERN_WARNING
1272 "Calgary: Invalid Rio Grande Table Version: %d\n",
1273 rio_table_hdr->version);
1274 return -EPROTO;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001275 }
1276
1277 ptr = ((unsigned long)rio_table_hdr) + 3;
David Howells85d57792008-08-18 11:58:17 +02001278 for (i = 0; i < numnodes; i++, ptr += scal_detail_size)
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001279 scal_devs[i] = (struct scal_detail *)ptr;
1280
1281 for (i = 0; i < rio_table_hdr->num_rio_dev;
1282 i++, ptr += rio_detail_size)
1283 rio_devs[i] = (struct rio_detail *)ptr;
1284
1285 return 0;
1286}
1287
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001288static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev)
1289{
1290 int dev;
1291 u32 val;
1292
1293 if (pci_dev == PCI_DEVICE_ID_IBM_CALIOC2) {
1294 /*
Lucas De Marchi0d2eb442011-03-17 16:24:16 -03001295 * FIXME: properly scan for devices across the
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001296 * PCI-to-PCI bridge on every CalIOC2 port.
1297 */
1298 return 1;
1299 }
1300
1301 for (dev = 1; dev < 8; dev++) {
1302 val = read_pci_config(bus, dev, 0, 0);
1303 if (val != 0xffffffff)
1304 break;
1305 }
1306 return (val != 0xffffffff);
1307}
1308
Chandru95b68de2008-07-25 01:47:55 -07001309/*
1310 * calgary_init_bitmap_from_tce_table():
Lucas De Marchi0d2eb442011-03-17 16:24:16 -03001311 * Function for kdump case. In the second/kdump kernel initialize
Chandru95b68de2008-07-25 01:47:55 -07001312 * the bitmap based on the tce table entries obtained from first kernel
1313 */
1314static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl)
1315{
1316 u64 *tp;
1317 unsigned int index;
1318 tp = ((u64 *)tbl->it_base);
1319 for (index = 0 ; index < tbl->it_size; index++) {
1320 if (*tp != 0x0)
1321 set_bit(index, tbl->it_map);
1322 tp++;
1323 }
1324}
1325
1326/*
1327 * get_tce_space_from_tar():
1328 * Function for kdump case. Get the tce tables from first kernel
Daniel Mack3ad2f3fb2010-02-03 08:01:28 +08001329 * by reading the contents of the base address register of calgary iommu
Chandru95b68de2008-07-25 01:47:55 -07001330 */
Marcin Slusarzf7106662008-08-17 17:50:52 +02001331static void __init get_tce_space_from_tar(void)
Chandru95b68de2008-07-25 01:47:55 -07001332{
1333 int bus;
1334 void __iomem *target;
1335 unsigned long tce_space;
1336
1337 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
1338 struct calgary_bus_info *info = &bus_info[bus];
1339 unsigned short pci_device;
1340 u32 val;
1341
1342 val = read_pci_config(bus, 0, 0, 0);
1343 pci_device = (val & 0xFFFF0000) >> 16;
1344
1345 if (!is_cal_pci_dev(pci_device))
1346 continue;
1347 if (info->translation_disabled)
1348 continue;
1349
1350 if (calgary_bus_has_devices(bus, pci_device) ||
1351 translate_empty_slots) {
1352 target = calgary_reg(bus_info[bus].bbar,
1353 tar_offset(bus));
1354 tce_space = be64_to_cpu(readq(target));
1355 tce_space = tce_space & TAR_SW_BITS;
1356
1357 tce_space = tce_space & (~specified_table_size);
1358 info->tce_space = (u64 *)__va(tce_space);
1359 }
1360 }
1361 return;
1362}
1363
FUJITA Tomonorif4131c62009-11-14 21:26:50 +09001364static int __init calgary_iommu_init(void)
1365{
1366 int ret;
1367
1368 /* ok, we're trying to use Calgary - let's roll */
1369 printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n");
1370
1371 ret = calgary_init();
1372 if (ret) {
1373 printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "
1374 "falling back to no_iommu\n", ret);
1375 return ret;
1376 }
1377
FUJITA Tomonorif4131c62009-11-14 21:26:50 +09001378 return 0;
1379}
FUJITA Tomonorid7b9f7b2009-11-10 19:46:13 +09001380
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001381int __init detect_calgary(void)
Jon Masone4650582006-06-26 13:58:14 +02001382{
Jon Masond2105b12006-07-29 21:42:43 +02001383 int bus;
Jon Masone4650582006-06-26 13:58:14 +02001384 void *tbl;
Jon Masond2105b12006-07-29 21:42:43 +02001385 int calgary_found = 0;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001386 unsigned long ptr;
Ingo Molnar136f1e72006-12-20 11:53:32 +01001387 unsigned int offset, prev_offset;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001388 int ret;
Jon Masone4650582006-06-26 13:58:14 +02001389
1390 /*
1391 * if the user specified iommu=off or iommu=soft or we found
1392 * another HW IOMMU already, bail out.
1393 */
FUJITA Tomonori75f1cdf2009-11-10 19:46:20 +09001394 if (no_iommu || iommu_detected)
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001395 return -ENODEV;
Jon Masone4650582006-06-26 13:58:14 +02001396
Muli Ben-Yehudabff65472006-12-07 02:14:07 +01001397 if (!use_calgary)
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001398 return -ENODEV;
Muli Ben-Yehudabff65472006-12-07 02:14:07 +01001399
Andi Kleen0637a702006-09-26 10:52:41 +02001400 if (!early_pci_allowed())
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001401 return -ENODEV;
Andi Kleen0637a702006-09-26 10:52:41 +02001402
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001403 printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n");
1404
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001405 ptr = (unsigned long)phys_to_virt(get_bios_ebda());
1406
1407 rio_table_hdr = NULL;
Ingo Molnar136f1e72006-12-20 11:53:32 +01001408 prev_offset = 0;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001409 offset = 0x180;
Ingo Molnar136f1e72006-12-20 11:53:32 +01001410 /*
1411 * The next offset is stored in the 1st word.
1412 * Only parse up until the offset increases:
1413 */
1414 while (offset > prev_offset) {
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001415 /* The block id is stored in the 2nd word */
1416 if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
1417 /* set the pointer past the offset & block id */
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001418 rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4);
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001419 break;
1420 }
Ingo Molnar136f1e72006-12-20 11:53:32 +01001421 prev_offset = offset;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001422 offset = *((unsigned short *)(ptr + offset));
1423 }
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001424 if (!rio_table_hdr) {
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001425 printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table "
1426 "in EBDA - bailing!\n");
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001427 return -ENODEV;
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001428 }
1429
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001430 ret = build_detail_arrays();
1431 if (ret) {
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001432 printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n", ret);
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001433 return -ENOMEM;
Muli Ben-Yehudaeae93752006-12-07 02:14:06 +01001434 }
Laurent Vivierb34e90b2006-12-07 02:14:06 +01001435
WANG Chao0534af02014-03-10 22:52:00 +08001436 specified_table_size = determine_tce_table_size();
Jon Masone4650582006-06-26 13:58:14 +02001437
Jon Masond2105b12006-07-29 21:42:43 +02001438 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001439 struct calgary_bus_info *info = &bus_info[bus];
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001440 unsigned short pci_device;
1441 u32 val;
Jon Masond2105b12006-07-29 21:42:43 +02001442
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001443 val = read_pci_config(bus, 0, 0, 0);
1444 pci_device = (val & 0xFFFF0000) >> 16;
1445
1446 if (!is_cal_pci_dev(pci_device))
Jon Masone4650582006-06-26 13:58:14 +02001447 continue;
Jon Masond2105b12006-07-29 21:42:43 +02001448
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001449 if (info->translation_disabled)
Jon Masone4650582006-06-26 13:58:14 +02001450 continue;
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001451
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001452 if (calgary_bus_has_devices(bus, pci_device) ||
1453 translate_empty_slots) {
Chandru95b68de2008-07-25 01:47:55 -07001454 /*
1455 * If it is kdump kernel, find and use tce tables
1456 * from first kernel, else allocate tce tables here
1457 */
1458 if (!is_kdump_kernel()) {
1459 tbl = alloc_tce_table();
1460 if (!tbl)
1461 goto cleanup;
1462 info->tce_space = tbl;
1463 }
Muli Ben-Yehuda8a244592007-07-21 17:10:52 +02001464 calgary_found = 1;
Jon Masond2105b12006-07-29 21:42:43 +02001465 }
Jon Masone4650582006-06-26 13:58:14 +02001466 }
1467
Muli Ben-Yehudab92cc552007-01-11 01:52:44 +01001468 printk(KERN_DEBUG "Calgary: finished detection, Calgary %s\n",
1469 calgary_found ? "found" : "not found");
1470
Jon Masond2105b12006-07-29 21:42:43 +02001471 if (calgary_found) {
Jon Masone4650582006-06-26 13:58:14 +02001472 iommu_detected = 1;
1473 calgary_detected = 1;
Muli Ben-Yehudade684652006-09-26 10:52:33 +02001474 printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected.\n");
FUJITA Tomonori7e055752009-04-14 12:12:29 +09001475 printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d\n",
1476 specified_table_size);
Alexis Bruemmer1956a962008-07-25 19:44:51 -07001477
FUJITA Tomonorid7b9f7b2009-11-10 19:46:13 +09001478 x86_init.iommu.iommu_init = calgary_iommu_init;
Jon Masone4650582006-06-26 13:58:14 +02001479 }
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001480 return calgary_found;
Jon Masone4650582006-06-26 13:58:14 +02001481
1482cleanup:
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001483 for (--bus; bus >= 0; --bus) {
1484 struct calgary_bus_info *info = &bus_info[bus];
1485
1486 if (info->tce_space)
1487 free_tce_table(info->tce_space);
1488 }
Konrad Rzeszutek Wilk480125b2010-08-26 13:57:57 -04001489 return -ENOMEM;
Jon Masone4650582006-06-26 13:58:14 +02001490}
1491
Jon Masone4650582006-06-26 13:58:14 +02001492static int __init calgary_parse_options(char *p)
1493{
1494 unsigned int bridge;
Shuah Khan74bc4912012-05-20 17:24:28 -06001495 unsigned long val;
Jon Masone4650582006-06-26 13:58:14 +02001496 size_t len;
Shuah Khan74bc4912012-05-20 17:24:28 -06001497 ssize_t ret;
Jon Masone4650582006-06-26 13:58:14 +02001498
1499 while (*p) {
1500 if (!strncmp(p, "64k", 3))
1501 specified_table_size = TCE_TABLE_SIZE_64K;
1502 else if (!strncmp(p, "128k", 4))
1503 specified_table_size = TCE_TABLE_SIZE_128K;
1504 else if (!strncmp(p, "256k", 4))
1505 specified_table_size = TCE_TABLE_SIZE_256K;
1506 else if (!strncmp(p, "512k", 4))
1507 specified_table_size = TCE_TABLE_SIZE_512K;
1508 else if (!strncmp(p, "1M", 2))
1509 specified_table_size = TCE_TABLE_SIZE_1M;
1510 else if (!strncmp(p, "2M", 2))
1511 specified_table_size = TCE_TABLE_SIZE_2M;
1512 else if (!strncmp(p, "4M", 2))
1513 specified_table_size = TCE_TABLE_SIZE_4M;
1514 else if (!strncmp(p, "8M", 2))
1515 specified_table_size = TCE_TABLE_SIZE_8M;
1516
1517 len = strlen("translate_empty_slots");
1518 if (!strncmp(p, "translate_empty_slots", len))
1519 translate_empty_slots = 1;
1520
1521 len = strlen("disable");
1522 if (!strncmp(p, "disable", len)) {
1523 p += len;
1524 if (*p == '=')
1525 ++p;
1526 if (*p == '\0')
1527 break;
Shuah Khan74bc4912012-05-20 17:24:28 -06001528 ret = kstrtoul(p, 0, &val);
1529 if (ret)
Jon Masone4650582006-06-26 13:58:14 +02001530 break;
1531
Shuah Khan74bc4912012-05-20 17:24:28 -06001532 bridge = val;
Jon Masond2105b12006-07-29 21:42:43 +02001533 if (bridge < MAX_PHB_BUS_NUM) {
Jon Masone4650582006-06-26 13:58:14 +02001534 printk(KERN_INFO "Calgary: disabling "
Jon Mason70d666d2006-10-05 18:47:21 +02001535 "translation for PHB %#x\n", bridge);
Muli Ben-Yehudaf38db652006-09-26 10:52:31 +02001536 bus_info[bridge].translation_disabled = 1;
Jon Masone4650582006-06-26 13:58:14 +02001537 }
1538 }
1539
1540 p = strpbrk(p, ",");
1541 if (!p)
1542 break;
1543
1544 p++; /* skip ',' */
1545 }
1546 return 1;
1547}
1548__setup("calgary=", calgary_parse_options);
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001549
1550static void __init calgary_fixup_one_tce_space(struct pci_dev *dev)
1551{
1552 struct iommu_table *tbl;
1553 unsigned int npages;
1554 int i;
1555
Muli Ben-Yehuda08f1c192007-07-22 00:23:39 +03001556 tbl = pci_iommu(dev->bus);
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001557
1558 for (i = 0; i < 4; i++) {
1559 struct resource *r = &dev->resource[PCI_BRIDGE_RESOURCES + i];
1560
1561 /* Don't give out TCEs that map MEM resources */
1562 if (!(r->flags & IORESOURCE_MEM))
1563 continue;
1564
1565 /* 0-based? we reserve the whole 1st MB anyway */
1566 if (!r->start)
1567 continue;
1568
1569 /* cover the whole region */
Joe Perches28f65c112011-06-09 09:13:32 -07001570 npages = resource_size(r) >> PAGE_SHIFT;
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001571 npages++;
1572
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001573 iommu_range_reserve(tbl, r->start, npages);
1574 }
1575}
1576
1577static int __init calgary_fixup_tce_spaces(void)
1578{
1579 struct pci_dev *dev = NULL;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001580 struct calgary_bus_info *info;
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001581
1582 if (no_iommu || swiotlb || !calgary_detected)
1583 return -ENODEV;
1584
Muli Ben-Yehuda12de2572007-07-21 17:11:02 +02001585 printk(KERN_DEBUG "Calgary: fixing up tce spaces\n");
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001586
1587 do {
1588 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
1589 if (!dev)
1590 break;
1591 if (!is_cal_pci_dev(dev->device))
1592 continue;
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001593
1594 info = &bus_info[dev->bus->number];
1595 if (info->translation_disabled)
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001596 continue;
1597
Muli Ben-Yehudabc3c6052007-10-17 18:04:39 +02001598 if (!info->tce_space)
Muli Ben-Yehuda07877cf2007-07-21 17:10:58 +02001599 continue;
1600
1601 calgary_fixup_one_tce_space(dev);
1602
1603 } while (1);
1604
1605 return 0;
1606}
1607
1608/*
1609 * We need to be call after pcibios_assign_resources (fs_initcall level)
1610 * and before device_initcall.
1611 */
1612rootfs_initcall(calgary_fixup_tce_spaces);
Konrad Rzeszutek Wilkd2aa232f2010-08-26 13:58:02 -04001613
1614IOMMU_INIT_POST(detect_calgary);