// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Derived from arch/powerpc/kernel/iommu.c
*
* Copyright IBM Corporation, 2006-2007
* Copyright (C) 2006 Jon Mason <jdmason@kudzu.us>
*
* Author: Jon Mason <jdmason@kudzu.us>
* Author: Muli Ben-Yehuda <muli@il.ibm.com>
*/
#define pr_fmt(fmt) "Calgary: " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/crash_dump.h>
#include <linux/dma-mapping.h>
#include <linux/dma-direct.h>
#include <linux/bitmap.h>
#include <linux/pci_ids.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/iommu-helper.h>
#include <asm/iommu.h>
#include <asm/calgary.h>
#include <asm/tce.h>
#include <asm/pci-direct.h>
#include <asm/dma.h>
#include <asm/rio.h>
#include <asm/bios_ebda.h>
#include <asm/x86_init.h>
#include <asm/iommu_table.h>
#ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT
int use_calgary __read_mostly = 1;
#else
int use_calgary __read_mostly = 0;
#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
#define PCI_DEVICE_ID_IBM_CALIOC2 0x0308
/* register offsets inside the host bridge space */
#define CALGARY_CONFIG_REG 0x0108
#define PHB_CSR_OFFSET 0x0110 /* Channel Status */
#define PHB_PLSSR_OFFSET 0x0120
#define PHB_CONFIG_RW_OFFSET 0x0160
#define PHB_IOBASE_BAR_LOW 0x0170
#define PHB_IOBASE_BAR_HIGH 0x0180
#define PHB_MEM_1_LOW 0x0190
#define PHB_MEM_1_HIGH 0x01A0
#define PHB_IO_ADDR_SIZE 0x01B0
#define PHB_MEM_1_SIZE 0x01C0
#define PHB_MEM_ST_OFFSET 0x01D0
#define PHB_AER_OFFSET 0x0200
#define PHB_CONFIG_0_HIGH 0x0220
#define PHB_CONFIG_0_LOW 0x0230
#define PHB_CONFIG_0_END 0x0240
#define PHB_MEM_2_LOW 0x02B0
#define PHB_MEM_2_HIGH 0x02C0
#define PHB_MEM_2_SIZE_HIGH 0x02D0
#define PHB_MEM_2_SIZE_LOW 0x02E0
#define PHB_DOSHOLE_OFFSET 0x08E0
/* CalIOC2 specific */
#define PHB_SAVIOR_L2 0x0DB0
#define PHB_PAGE_MIG_CTRL 0x0DA8
#define PHB_PAGE_MIG_DEBUG 0x0DA0
#define PHB_ROOT_COMPLEX_STATUS 0x0CB0
/* PHB_CONFIG_RW */
#define PHB_TCE_ENABLE 0x20000000
#define PHB_SLOT_DISABLE 0x1C000000
#define PHB_DAC_DISABLE 0x01000000
#define PHB_MEM2_ENABLE 0x00400000
#define PHB_MCSR_ENABLE 0x00100000
/* TAR (Table Address Register) */
#define TAR_SW_BITS 0x0000ffffffff800fUL
#define TAR_VALID 0x0000000000000008UL
/* CSR (Channel/DMA Status Register) */
#define CSR_AGENT_MASK 0xffe0ffff
/* CCR (Calgary Configuration Register) */
#define CCR_2SEC_TIMEOUT 0x000000000000000EUL
/* PMCR/PMDR (Page Migration Control/Debug Registers */
#define PMR_SOFTSTOP 0x80000000
#define PMR_SOFTSTOPFAULT 0x40000000
#define PMR_HARDSTOP 0x20000000
/*
* The maximum PHB bus number.
* x3950M2 (rare): 8 chassis, 48 PHBs per chassis = 384
* x3950M2: 4 chassis, 48 PHBs per chassis = 192
* x3950 (PCIE): 8 chassis, 32 PHBs per chassis = 256
* x3950 (PCIX): 8 chassis, 16 PHBs per chassis = 128
*/
#define MAX_PHB_BUS_NUM 256
#define PHBS_PER_CALGARY 4
/* register offsets in Calgary's internal register space */
static const unsigned long tar_offsets[] = {
0x0580 /* TAR0 */,
0x0588 /* TAR1 */,
0x0590 /* TAR2 */,
0x0598 /* TAR3 */
};
static const unsigned long split_queue_offsets[] = {
0x4870 /* SPLIT QUEUE 0 */,
0x5870 /* SPLIT QUEUE 1 */,
0x6870 /* SPLIT QUEUE 2 */,
0x7870 /* SPLIT QUEUE 3 */
};
static const unsigned long phb_offsets[] = {
0x8000 /* PHB0 */,
0x9000 /* PHB1 */,
0xA000 /* PHB2 */,
0xB000 /* PHB3 */
};
/* PHB debug registers */
static const unsigned long phb_debug_offsets[] = {
0x4000 /* PHB 0 DEBUG */,
0x5000 /* PHB 1 DEBUG */,
0x6000 /* PHB 2 DEBUG */,
0x7000 /* PHB 3 DEBUG */
};
/*
* STUFF register for each debug PHB,
* byte 1 = start bus number, byte 2 = end bus number
*/
#define PHB_DEBUG_STUFF_OFFSET 0x0020
unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
static int translate_empty_slots __read_mostly = 0;
static int calgary_detected __read_mostly = 0;
static struct rio_table_hdr *rio_table_hdr __initdata;
static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
static struct rio_detail *rio_devs[MAX_NUMNODES * 4] __initdata;
struct calgary_bus_info {
void *tce_space;
unsigned char translation_disabled;
signed char phbid;
void __iomem *bbar;
};
static void calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
static void calgary_tce_cache_blast(struct iommu_table *tbl);
static void calgary_dump_error_regs(struct iommu_table *tbl);
static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev);
static void calioc2_tce_cache_blast(struct iommu_table *tbl);
static void calioc2_dump_error_regs(struct iommu_table *tbl);
static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl);
static void get_tce_space_from_tar(void);
static const struct cal_chipset_ops calgary_chip_ops = {
.handle_quirks = calgary_handle_quirks,
.tce_cache_blast = calgary_tce_cache_blast,
.dump_error_regs = calgary_dump_error_regs
};
static const struct cal_chipset_ops calioc2_chip_ops = {
.handle_quirks = calioc2_handle_quirks,
.tce_cache_blast = calioc2_tce_cache_blast,
.dump_error_regs = calioc2_dump_error_regs
};
static st
|