162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for V3 Semiconductor PCI Local Bus to PCI Bridge 462306a36Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on the code from arch/arm/mach-integrator/pci_v3.c 762306a36Sopenharmony_ci * Copyright (C) 1999 ARM Limited 862306a36Sopenharmony_ci * Copyright (C) 2000-2001 Deep Blue Solutions Ltd 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Contributors to the old driver include: 1162306a36Sopenharmony_ci * Russell King <linux@armlinux.org.uk> 1262306a36Sopenharmony_ci * David A. Rusling <david.rusling@linaro.org> (uHAL, ARM Firmware suite) 1362306a36Sopenharmony_ci * Rob Herring <robh@kernel.org> 1462306a36Sopenharmony_ci * Liviu Dudau <Liviu.Dudau@arm.com> 1562306a36Sopenharmony_ci * Grant Likely <grant.likely@secretlab.ca> 1662306a36Sopenharmony_ci * Arnd Bergmann <arnd@arndb.de> 1762306a36Sopenharmony_ci * Bjorn Helgaas <bhelgaas@google.com> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_pci.h> 2562306a36Sopenharmony_ci#include <linux/pci.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/bitops.h> 2962306a36Sopenharmony_ci#include <linux/irq.h> 3062306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 3162306a36Sopenharmony_ci#include <linux/regmap.h> 3262306a36Sopenharmony_ci#include <linux/clk.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "../pci.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define V3_PCI_VENDOR 0x00000000 3762306a36Sopenharmony_ci#define V3_PCI_DEVICE 0x00000002 3862306a36Sopenharmony_ci#define V3_PCI_CMD 0x00000004 3962306a36Sopenharmony_ci#define V3_PCI_STAT 0x00000006 4062306a36Sopenharmony_ci#define V3_PCI_CC_REV 0x00000008 4162306a36Sopenharmony_ci#define V3_PCI_HDR_CFG 0x0000000C 4262306a36Sopenharmony_ci#define V3_PCI_IO_BASE 0x00000010 4362306a36Sopenharmony_ci#define V3_PCI_BASE0 0x00000014 4462306a36Sopenharmony_ci#define V3_PCI_BASE1 0x00000018 4562306a36Sopenharmony_ci#define V3_PCI_SUB_VENDOR 0x0000002C 4662306a36Sopenharmony_ci#define V3_PCI_SUB_ID 0x0000002E 4762306a36Sopenharmony_ci#define V3_PCI_ROM 0x00000030 4862306a36Sopenharmony_ci#define V3_PCI_BPARAM 0x0000003C 4962306a36Sopenharmony_ci#define V3_PCI_MAP0 0x00000040 5062306a36Sopenharmony_ci#define V3_PCI_MAP1 0x00000044 5162306a36Sopenharmony_ci#define V3_PCI_INT_STAT 0x00000048 5262306a36Sopenharmony_ci#define V3_PCI_INT_CFG 0x0000004C 5362306a36Sopenharmony_ci#define V3_LB_BASE0 0x00000054 5462306a36Sopenharmony_ci#define V3_LB_BASE1 0x00000058 5562306a36Sopenharmony_ci#define V3_LB_MAP0 0x0000005E 5662306a36Sopenharmony_ci#define V3_LB_MAP1 0x00000062 5762306a36Sopenharmony_ci#define V3_LB_BASE2 0x00000064 5862306a36Sopenharmony_ci#define V3_LB_MAP2 0x00000066 5962306a36Sopenharmony_ci#define V3_LB_SIZE 0x00000068 6062306a36Sopenharmony_ci#define V3_LB_IO_BASE 0x0000006E 6162306a36Sopenharmony_ci#define V3_FIFO_CFG 0x00000070 6262306a36Sopenharmony_ci#define V3_FIFO_PRIORITY 0x00000072 6362306a36Sopenharmony_ci#define V3_FIFO_STAT 0x00000074 6462306a36Sopenharmony_ci#define V3_LB_ISTAT 0x00000076 6562306a36Sopenharmony_ci#define V3_LB_IMASK 0x00000077 6662306a36Sopenharmony_ci#define V3_SYSTEM 0x00000078 6762306a36Sopenharmony_ci#define V3_LB_CFG 0x0000007A 6862306a36Sopenharmony_ci#define V3_PCI_CFG 0x0000007C 6962306a36Sopenharmony_ci#define V3_DMA_PCI_ADR0 0x00000080 7062306a36Sopenharmony_ci#define V3_DMA_PCI_ADR1 0x00000090 7162306a36Sopenharmony_ci#define V3_DMA_LOCAL_ADR0 0x00000084 7262306a36Sopenharmony_ci#define V3_DMA_LOCAL_ADR1 0x00000094 7362306a36Sopenharmony_ci#define V3_DMA_LENGTH0 0x00000088 7462306a36Sopenharmony_ci#define V3_DMA_LENGTH1 0x00000098 7562306a36Sopenharmony_ci#define V3_DMA_CSR0 0x0000008B 7662306a36Sopenharmony_ci#define V3_DMA_CSR1 0x0000009B 7762306a36Sopenharmony_ci#define V3_DMA_CTLB_ADR0 0x0000008C 7862306a36Sopenharmony_ci#define V3_DMA_CTLB_ADR1 0x0000009C 7962306a36Sopenharmony_ci#define V3_DMA_DELAY 0x000000E0 8062306a36Sopenharmony_ci#define V3_MAIL_DATA 0x000000C0 8162306a36Sopenharmony_ci#define V3_PCI_MAIL_IEWR 0x000000D0 8262306a36Sopenharmony_ci#define V3_PCI_MAIL_IERD 0x000000D2 8362306a36Sopenharmony_ci#define V3_LB_MAIL_IEWR 0x000000D4 8462306a36Sopenharmony_ci#define V3_LB_MAIL_IERD 0x000000D6 8562306a36Sopenharmony_ci#define V3_MAIL_WR_STAT 0x000000D8 8662306a36Sopenharmony_ci#define V3_MAIL_RD_STAT 0x000000DA 8762306a36Sopenharmony_ci#define V3_QBA_MAP 0x000000DC 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* PCI STATUS bits */ 9062306a36Sopenharmony_ci#define V3_PCI_STAT_PAR_ERR BIT(15) 9162306a36Sopenharmony_ci#define V3_PCI_STAT_SYS_ERR BIT(14) 9262306a36Sopenharmony_ci#define V3_PCI_STAT_M_ABORT_ERR BIT(13) 9362306a36Sopenharmony_ci#define V3_PCI_STAT_T_ABORT_ERR BIT(12) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* LB ISTAT bits */ 9662306a36Sopenharmony_ci#define V3_LB_ISTAT_MAILBOX BIT(7) 9762306a36Sopenharmony_ci#define V3_LB_ISTAT_PCI_RD BIT(6) 9862306a36Sopenharmony_ci#define V3_LB_ISTAT_PCI_WR BIT(5) 9962306a36Sopenharmony_ci#define V3_LB_ISTAT_PCI_INT BIT(4) 10062306a36Sopenharmony_ci#define V3_LB_ISTAT_PCI_PERR BIT(3) 10162306a36Sopenharmony_ci#define V3_LB_ISTAT_I2O_QWR BIT(2) 10262306a36Sopenharmony_ci#define V3_LB_ISTAT_DMA1 BIT(1) 10362306a36Sopenharmony_ci#define V3_LB_ISTAT_DMA0 BIT(0) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* PCI COMMAND bits */ 10662306a36Sopenharmony_ci#define V3_COMMAND_M_FBB_EN BIT(9) 10762306a36Sopenharmony_ci#define V3_COMMAND_M_SERR_EN BIT(8) 10862306a36Sopenharmony_ci#define V3_COMMAND_M_PAR_EN BIT(6) 10962306a36Sopenharmony_ci#define V3_COMMAND_M_MASTER_EN BIT(2) 11062306a36Sopenharmony_ci#define V3_COMMAND_M_MEM_EN BIT(1) 11162306a36Sopenharmony_ci#define V3_COMMAND_M_IO_EN BIT(0) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* SYSTEM bits */ 11462306a36Sopenharmony_ci#define V3_SYSTEM_M_RST_OUT BIT(15) 11562306a36Sopenharmony_ci#define V3_SYSTEM_M_LOCK BIT(14) 11662306a36Sopenharmony_ci#define V3_SYSTEM_UNLOCK 0xa05f 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* PCI CFG bits */ 11962306a36Sopenharmony_ci#define V3_PCI_CFG_M_I2O_EN BIT(15) 12062306a36Sopenharmony_ci#define V3_PCI_CFG_M_IO_REG_DIS BIT(14) 12162306a36Sopenharmony_ci#define V3_PCI_CFG_M_IO_DIS BIT(13) 12262306a36Sopenharmony_ci#define V3_PCI_CFG_M_EN3V BIT(12) 12362306a36Sopenharmony_ci#define V3_PCI_CFG_M_RETRY_EN BIT(10) 12462306a36Sopenharmony_ci#define V3_PCI_CFG_M_AD_LOW1 BIT(9) 12562306a36Sopenharmony_ci#define V3_PCI_CFG_M_AD_LOW0 BIT(8) 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * This is the value applied to C/BE[3:1], with bit 0 always held 0 12862306a36Sopenharmony_ci * during DMA access. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci#define V3_PCI_CFG_M_RTYPE_SHIFT 5 13162306a36Sopenharmony_ci#define V3_PCI_CFG_M_WTYPE_SHIFT 1 13262306a36Sopenharmony_ci#define V3_PCI_CFG_TYPE_DEFAULT 0x3 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* PCI BASE bits (PCI -> Local Bus) */ 13562306a36Sopenharmony_ci#define V3_PCI_BASE_M_ADR_BASE 0xFFF00000U 13662306a36Sopenharmony_ci#define V3_PCI_BASE_M_ADR_BASEL 0x000FFF00U 13762306a36Sopenharmony_ci#define V3_PCI_BASE_M_PREFETCH BIT(3) 13862306a36Sopenharmony_ci#define V3_PCI_BASE_M_TYPE (3 << 1) 13962306a36Sopenharmony_ci#define V3_PCI_BASE_M_IO BIT(0) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* PCI MAP bits (PCI -> Local bus) */ 14262306a36Sopenharmony_ci#define V3_PCI_MAP_M_MAP_ADR 0xFFF00000U 14362306a36Sopenharmony_ci#define V3_PCI_MAP_M_RD_POST_INH BIT(15) 14462306a36Sopenharmony_ci#define V3_PCI_MAP_M_ROM_SIZE (3 << 10) 14562306a36Sopenharmony_ci#define V3_PCI_MAP_M_SWAP (3 << 8) 14662306a36Sopenharmony_ci#define V3_PCI_MAP_M_ADR_SIZE 0x000000F0U 14762306a36Sopenharmony_ci#define V3_PCI_MAP_M_REG_EN BIT(1) 14862306a36Sopenharmony_ci#define V3_PCI_MAP_M_ENABLE BIT(0) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* LB_BASE0,1 bits (Local bus -> PCI) */ 15162306a36Sopenharmony_ci#define V3_LB_BASE_ADR_BASE 0xfff00000U 15262306a36Sopenharmony_ci#define V3_LB_BASE_SWAP (3 << 8) 15362306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE (15 << 4) 15462306a36Sopenharmony_ci#define V3_LB_BASE_PREFETCH BIT(3) 15562306a36Sopenharmony_ci#define V3_LB_BASE_ENABLE BIT(0) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_1MB (0 << 4) 15862306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_2MB (1 << 4) 15962306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_4MB (2 << 4) 16062306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_8MB (3 << 4) 16162306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_16MB (4 << 4) 16262306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_32MB (5 << 4) 16362306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_64MB (6 << 4) 16462306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_128MB (7 << 4) 16562306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_256MB (8 << 4) 16662306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_512MB (9 << 4) 16762306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_1GB (10 << 4) 16862306a36Sopenharmony_ci#define V3_LB_BASE_ADR_SIZE_2GB (11 << 4) 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci#define v3_addr_to_lb_base(a) ((a) & V3_LB_BASE_ADR_BASE) 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* LB_MAP0,1 bits (Local bus -> PCI) */ 17362306a36Sopenharmony_ci#define V3_LB_MAP_MAP_ADR 0xfff0U 17462306a36Sopenharmony_ci#define V3_LB_MAP_TYPE (7 << 1) 17562306a36Sopenharmony_ci#define V3_LB_MAP_AD_LOW_EN BIT(0) 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#define V3_LB_MAP_TYPE_IACK (0 << 1) 17862306a36Sopenharmony_ci#define V3_LB_MAP_TYPE_IO (1 << 1) 17962306a36Sopenharmony_ci#define V3_LB_MAP_TYPE_MEM (3 << 1) 18062306a36Sopenharmony_ci#define V3_LB_MAP_TYPE_CONFIG (5 << 1) 18162306a36Sopenharmony_ci#define V3_LB_MAP_TYPE_MEM_MULTIPLE (6 << 1) 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#define v3_addr_to_lb_map(a) (((a) >> 16) & V3_LB_MAP_MAP_ADR) 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* LB_BASE2 bits (Local bus -> PCI IO) */ 18662306a36Sopenharmony_ci#define V3_LB_BASE2_ADR_BASE 0xff00U 18762306a36Sopenharmony_ci#define V3_LB_BASE2_SWAP_AUTO (3 << 6) 18862306a36Sopenharmony_ci#define V3_LB_BASE2_ENABLE BIT(0) 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci#define v3_addr_to_lb_base2(a) (((a) >> 16) & V3_LB_BASE2_ADR_BASE) 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* LB_MAP2 bits (Local bus -> PCI IO) */ 19362306a36Sopenharmony_ci#define V3_LB_MAP2_MAP_ADR 0xff00U 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#define v3_addr_to_lb_map2(a) (((a) >> 16) & V3_LB_MAP2_MAP_ADR) 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* FIFO priority bits */ 19862306a36Sopenharmony_ci#define V3_FIFO_PRIO_LOCAL BIT(12) 19962306a36Sopenharmony_ci#define V3_FIFO_PRIO_LB_RD1_FLUSH_EOB BIT(10) 20062306a36Sopenharmony_ci#define V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 BIT(11) 20162306a36Sopenharmony_ci#define V3_FIFO_PRIO_LB_RD1_FLUSH_ANY (BIT(10)|BIT(11)) 20262306a36Sopenharmony_ci#define V3_FIFO_PRIO_LB_RD0_FLUSH_EOB BIT(8) 20362306a36Sopenharmony_ci#define V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 BIT(9) 20462306a36Sopenharmony_ci#define V3_FIFO_PRIO_LB_RD0_FLUSH_ANY (BIT(8)|BIT(9)) 20562306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI BIT(4) 20662306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI_RD1_FLUSH_EOB BIT(2) 20762306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 BIT(3) 20862306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI_RD1_FLUSH_ANY (BIT(2)|BIT(3)) 20962306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI_RD0_FLUSH_EOB BIT(0) 21062306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1 BIT(1) 21162306a36Sopenharmony_ci#define V3_FIFO_PRIO_PCI_RD0_FLUSH_ANY (BIT(0)|BIT(1)) 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* Local bus configuration bits */ 21462306a36Sopenharmony_ci#define V3_LB_CFG_LB_TO_64_CYCLES 0x0000 21562306a36Sopenharmony_ci#define V3_LB_CFG_LB_TO_256_CYCLES BIT(13) 21662306a36Sopenharmony_ci#define V3_LB_CFG_LB_TO_512_CYCLES BIT(14) 21762306a36Sopenharmony_ci#define V3_LB_CFG_LB_TO_1024_CYCLES (BIT(13)|BIT(14)) 21862306a36Sopenharmony_ci#define V3_LB_CFG_LB_RST BIT(12) 21962306a36Sopenharmony_ci#define V3_LB_CFG_LB_PPC_RDY BIT(11) 22062306a36Sopenharmony_ci#define V3_LB_CFG_LB_LB_INT BIT(10) 22162306a36Sopenharmony_ci#define V3_LB_CFG_LB_ERR_EN BIT(9) 22262306a36Sopenharmony_ci#define V3_LB_CFG_LB_RDY_EN BIT(8) 22362306a36Sopenharmony_ci#define V3_LB_CFG_LB_BE_IMODE BIT(7) 22462306a36Sopenharmony_ci#define V3_LB_CFG_LB_BE_OMODE BIT(6) 22562306a36Sopenharmony_ci#define V3_LB_CFG_LB_ENDIAN BIT(5) 22662306a36Sopenharmony_ci#define V3_LB_CFG_LB_PARK_EN BIT(4) 22762306a36Sopenharmony_ci#define V3_LB_CFG_LB_FBB_DIS BIT(2) 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* ARM Integrator-specific extended control registers */ 23062306a36Sopenharmony_ci#define INTEGRATOR_SC_PCI_OFFSET 0x18 23162306a36Sopenharmony_ci#define INTEGRATOR_SC_PCI_ENABLE BIT(0) 23262306a36Sopenharmony_ci#define INTEGRATOR_SC_PCI_INTCLR BIT(1) 23362306a36Sopenharmony_ci#define INTEGRATOR_SC_LBFADDR_OFFSET 0x20 23462306a36Sopenharmony_ci#define INTEGRATOR_SC_LBFCODE_OFFSET 0x24 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistruct v3_pci { 23762306a36Sopenharmony_ci struct device *dev; 23862306a36Sopenharmony_ci void __iomem *base; 23962306a36Sopenharmony_ci void __iomem *config_base; 24062306a36Sopenharmony_ci u32 config_mem; 24162306a36Sopenharmony_ci u32 non_pre_mem; 24262306a36Sopenharmony_ci u32 pre_mem; 24362306a36Sopenharmony_ci phys_addr_t non_pre_bus_addr; 24462306a36Sopenharmony_ci phys_addr_t pre_bus_addr; 24562306a36Sopenharmony_ci struct regmap *map; 24662306a36Sopenharmony_ci}; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * The V3 PCI interface chip in Integrator provides several windows from 25062306a36Sopenharmony_ci * local bus memory into the PCI memory areas. Unfortunately, there 25162306a36Sopenharmony_ci * are not really enough windows for our usage, therefore we reuse 25262306a36Sopenharmony_ci * one of the windows for access to PCI configuration space. On the 25362306a36Sopenharmony_ci * Integrator/AP, the memory map is as follows: 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Local Bus Memory Usage 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * 40000000 - 4FFFFFFF PCI memory. 256M non-prefetchable 25862306a36Sopenharmony_ci * 50000000 - 5FFFFFFF PCI memory. 256M prefetchable 25962306a36Sopenharmony_ci * 60000000 - 60FFFFFF PCI IO. 16M 26062306a36Sopenharmony_ci * 61000000 - 61FFFFFF PCI Configuration. 16M 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * There are three V3 windows, each described by a pair of V3 registers. 26362306a36Sopenharmony_ci * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2. 26462306a36Sopenharmony_ci * Base0 and Base1 can be used for any type of PCI memory access. Base2 26562306a36Sopenharmony_ci * can be used either for PCI I/O or for I20 accesses. By default, uHAL 26662306a36Sopenharmony_ci * uses this only for PCI IO space. 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * Normally these spaces are mapped using the following base registers: 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Usage Local Bus Memory Base/Map registers used 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 27362306a36Sopenharmony_ci * Mem 50000000 - 5FFFFFFF LB_BASE1/LB_MAP1 27462306a36Sopenharmony_ci * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 27562306a36Sopenharmony_ci * Cfg 61000000 - 61FFFFFF 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * This means that I20 and PCI configuration space accesses will fail. 27862306a36Sopenharmony_ci * When PCI configuration accesses are needed (via the uHAL PCI 27962306a36Sopenharmony_ci * configuration space primitives) we must remap the spaces as follows: 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Usage Local Bus Memory Base/Map registers used 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 28462306a36Sopenharmony_ci * Mem 50000000 - 5FFFFFFF LB_BASE0/LB_MAP0 28562306a36Sopenharmony_ci * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 28662306a36Sopenharmony_ci * Cfg 61000000 - 61FFFFFF LB_BASE1/LB_MAP1 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * To make this work, the code depends on overlapping windows working. 28962306a36Sopenharmony_ci * The V3 chip translates an address by checking its range within 29062306a36Sopenharmony_ci * each of the BASE/MAP pairs in turn (in ascending register number 29162306a36Sopenharmony_ci * order). It will use the first matching pair. So, for example, 29262306a36Sopenharmony_ci * if the same address is mapped by both LB_BASE0/LB_MAP0 and 29362306a36Sopenharmony_ci * LB_BASE1/LB_MAP1, the V3 will use the translation from 29462306a36Sopenharmony_ci * LB_BASE0/LB_MAP0. 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * To allow PCI Configuration space access, the code enlarges the 29762306a36Sopenharmony_ci * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M. This occludes 29862306a36Sopenharmony_ci * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can 29962306a36Sopenharmony_ci * be remapped for use by configuration cycles. 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * At the end of the PCI Configuration space accesses, 30262306a36Sopenharmony_ci * LB_BASE1/LB_MAP1 is reset to map PCI Memory. Finally the window 30362306a36Sopenharmony_ci * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to 30462306a36Sopenharmony_ci * reveal the now restored LB_BASE1/LB_MAP1 window. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * NOTE: We do not set up I2O mapping. I suspect that this is only 30762306a36Sopenharmony_ci * for an intelligent (target) device. Using I2O disables most of 30862306a36Sopenharmony_ci * the mappings into PCI memory. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_cistatic void __iomem *v3_map_bus(struct pci_bus *bus, 31162306a36Sopenharmony_ci unsigned int devfn, int offset) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct v3_pci *v3 = bus->sysdata; 31462306a36Sopenharmony_ci unsigned int address, mapaddress, busnr; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci busnr = bus->number; 31762306a36Sopenharmony_ci if (busnr == 0) { 31862306a36Sopenharmony_ci int slot = PCI_SLOT(devfn); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* 32162306a36Sopenharmony_ci * local bus segment so need a type 0 config cycle 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * build the PCI configuration "address" with one-hot in 32462306a36Sopenharmony_ci * A31-A11 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * mapaddress: 32762306a36Sopenharmony_ci * 3:1 = config cycle (101) 32862306a36Sopenharmony_ci * 0 = PCI A1 & A0 are 0 (0) 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci address = PCI_FUNC(devfn) << 8; 33162306a36Sopenharmony_ci mapaddress = V3_LB_MAP_TYPE_CONFIG; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (slot > 12) 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * high order bits are handled by the MAP register 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci mapaddress |= BIT(slot - 5); 33862306a36Sopenharmony_ci else 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * low order bits handled directly in the address 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci address |= BIT(slot + 11); 34362306a36Sopenharmony_ci } else { 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * not the local bus segment so need a type 1 config cycle 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * address: 34862306a36Sopenharmony_ci * 23:16 = bus number 34962306a36Sopenharmony_ci * 15:11 = slot number (7:3 of devfn) 35062306a36Sopenharmony_ci * 10:8 = func number (2:0 of devfn) 35162306a36Sopenharmony_ci * 35262306a36Sopenharmony_ci * mapaddress: 35362306a36Sopenharmony_ci * 3:1 = config cycle (101) 35462306a36Sopenharmony_ci * 0 = PCI A1 & A0 from host bus (1) 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci mapaddress = V3_LB_MAP_TYPE_CONFIG | V3_LB_MAP_AD_LOW_EN; 35762306a36Sopenharmony_ci address = (busnr << 16) | (devfn << 8); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * Set up base0 to see all 512Mbytes of memory space (not 36262306a36Sopenharmony_ci * prefetchable), this frees up base1 for re-use by 36362306a36Sopenharmony_ci * configuration memory 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci writel(v3_addr_to_lb_base(v3->non_pre_mem) | 36662306a36Sopenharmony_ci V3_LB_BASE_ADR_SIZE_512MB | V3_LB_BASE_ENABLE, 36762306a36Sopenharmony_ci v3->base + V3_LB_BASE0); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * Set up base1/map1 to point into configuration space. 37162306a36Sopenharmony_ci * The config mem is always 16MB. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci writel(v3_addr_to_lb_base(v3->config_mem) | 37462306a36Sopenharmony_ci V3_LB_BASE_ADR_SIZE_16MB | V3_LB_BASE_ENABLE, 37562306a36Sopenharmony_ci v3->base + V3_LB_BASE1); 37662306a36Sopenharmony_ci writew(mapaddress, v3->base + V3_LB_MAP1); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return v3->config_base + address + offset; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void v3_unmap_bus(struct v3_pci *v3) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * Reassign base1 for use by prefetchable PCI memory 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci writel(v3_addr_to_lb_base(v3->pre_mem) | 38762306a36Sopenharmony_ci V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH | 38862306a36Sopenharmony_ci V3_LB_BASE_ENABLE, 38962306a36Sopenharmony_ci v3->base + V3_LB_BASE1); 39062306a36Sopenharmony_ci writew(v3_addr_to_lb_map(v3->pre_bus_addr) | 39162306a36Sopenharmony_ci V3_LB_MAP_TYPE_MEM, /* was V3_LB_MAP_TYPE_MEM_MULTIPLE */ 39262306a36Sopenharmony_ci v3->base + V3_LB_MAP1); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * And shrink base0 back to a 256M window (NOTE: MAP0 already correct) 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci writel(v3_addr_to_lb_base(v3->non_pre_mem) | 39862306a36Sopenharmony_ci V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE, 39962306a36Sopenharmony_ci v3->base + V3_LB_BASE0); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int v3_pci_read_config(struct pci_bus *bus, unsigned int fn, 40362306a36Sopenharmony_ci int config, int size, u32 *value) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct v3_pci *v3 = bus->sysdata; 40662306a36Sopenharmony_ci int ret; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci dev_dbg(&bus->dev, 40962306a36Sopenharmony_ci "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", 41062306a36Sopenharmony_ci PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); 41162306a36Sopenharmony_ci ret = pci_generic_config_read(bus, fn, config, size, value); 41262306a36Sopenharmony_ci v3_unmap_bus(v3); 41362306a36Sopenharmony_ci return ret; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int v3_pci_write_config(struct pci_bus *bus, unsigned int fn, 41762306a36Sopenharmony_ci int config, int size, u32 value) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct v3_pci *v3 = bus->sysdata; 42062306a36Sopenharmony_ci int ret; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci dev_dbg(&bus->dev, 42362306a36Sopenharmony_ci "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", 42462306a36Sopenharmony_ci PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); 42562306a36Sopenharmony_ci ret = pci_generic_config_write(bus, fn, config, size, value); 42662306a36Sopenharmony_ci v3_unmap_bus(v3); 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic struct pci_ops v3_pci_ops = { 43162306a36Sopenharmony_ci .map_bus = v3_map_bus, 43262306a36Sopenharmony_ci .read = v3_pci_read_config, 43362306a36Sopenharmony_ci .write = v3_pci_write_config, 43462306a36Sopenharmony_ci}; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic irqreturn_t v3_irq(int irq, void *data) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct v3_pci *v3 = data; 43962306a36Sopenharmony_ci struct device *dev = v3->dev; 44062306a36Sopenharmony_ci u32 status; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci status = readw(v3->base + V3_PCI_STAT); 44362306a36Sopenharmony_ci if (status & V3_PCI_STAT_PAR_ERR) 44462306a36Sopenharmony_ci dev_err(dev, "parity error interrupt\n"); 44562306a36Sopenharmony_ci if (status & V3_PCI_STAT_SYS_ERR) 44662306a36Sopenharmony_ci dev_err(dev, "system error interrupt\n"); 44762306a36Sopenharmony_ci if (status & V3_PCI_STAT_M_ABORT_ERR) 44862306a36Sopenharmony_ci dev_err(dev, "master abort error interrupt\n"); 44962306a36Sopenharmony_ci if (status & V3_PCI_STAT_T_ABORT_ERR) 45062306a36Sopenharmony_ci dev_err(dev, "target abort error interrupt\n"); 45162306a36Sopenharmony_ci writew(status, v3->base + V3_PCI_STAT); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci status = readb(v3->base + V3_LB_ISTAT); 45462306a36Sopenharmony_ci if (status & V3_LB_ISTAT_MAILBOX) 45562306a36Sopenharmony_ci dev_info(dev, "PCI mailbox interrupt\n"); 45662306a36Sopenharmony_ci if (status & V3_LB_ISTAT_PCI_RD) 45762306a36Sopenharmony_ci dev_err(dev, "PCI target LB->PCI READ abort interrupt\n"); 45862306a36Sopenharmony_ci if (status & V3_LB_ISTAT_PCI_WR) 45962306a36Sopenharmony_ci dev_err(dev, "PCI target LB->PCI WRITE abort interrupt\n"); 46062306a36Sopenharmony_ci if (status & V3_LB_ISTAT_PCI_INT) 46162306a36Sopenharmony_ci dev_info(dev, "PCI pin interrupt\n"); 46262306a36Sopenharmony_ci if (status & V3_LB_ISTAT_PCI_PERR) 46362306a36Sopenharmony_ci dev_err(dev, "PCI parity error interrupt\n"); 46462306a36Sopenharmony_ci if (status & V3_LB_ISTAT_I2O_QWR) 46562306a36Sopenharmony_ci dev_info(dev, "I2O inbound post queue interrupt\n"); 46662306a36Sopenharmony_ci if (status & V3_LB_ISTAT_DMA1) 46762306a36Sopenharmony_ci dev_info(dev, "DMA channel 1 interrupt\n"); 46862306a36Sopenharmony_ci if (status & V3_LB_ISTAT_DMA0) 46962306a36Sopenharmony_ci dev_info(dev, "DMA channel 0 interrupt\n"); 47062306a36Sopenharmony_ci /* Clear all possible interrupts on the local bus */ 47162306a36Sopenharmony_ci writeb(0, v3->base + V3_LB_ISTAT); 47262306a36Sopenharmony_ci if (v3->map) 47362306a36Sopenharmony_ci regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, 47462306a36Sopenharmony_ci INTEGRATOR_SC_PCI_ENABLE | 47562306a36Sopenharmony_ci INTEGRATOR_SC_PCI_INTCLR); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return IRQ_HANDLED; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int v3_integrator_init(struct v3_pci *v3) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci unsigned int val; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci v3->map = 48562306a36Sopenharmony_ci syscon_regmap_lookup_by_compatible("arm,integrator-ap-syscon"); 48662306a36Sopenharmony_ci if (IS_ERR(v3->map)) { 48762306a36Sopenharmony_ci dev_err(v3->dev, "no syscon\n"); 48862306a36Sopenharmony_ci return -ENODEV; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci regmap_read(v3->map, INTEGRATOR_SC_PCI_OFFSET, &val); 49262306a36Sopenharmony_ci /* Take the PCI bridge out of reset, clear IRQs */ 49362306a36Sopenharmony_ci regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, 49462306a36Sopenharmony_ci INTEGRATOR_SC_PCI_ENABLE | 49562306a36Sopenharmony_ci INTEGRATOR_SC_PCI_INTCLR); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (!(val & INTEGRATOR_SC_PCI_ENABLE)) { 49862306a36Sopenharmony_ci /* If we were in reset we need to sleep a bit */ 49962306a36Sopenharmony_ci msleep(230); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Set the physical base for the controller itself */ 50262306a36Sopenharmony_ci writel(0x6200, v3->base + V3_LB_IO_BASE); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* Wait for the mailbox to settle after reset */ 50562306a36Sopenharmony_ci do { 50662306a36Sopenharmony_ci writeb(0xaa, v3->base + V3_MAIL_DATA); 50762306a36Sopenharmony_ci writeb(0x55, v3->base + V3_MAIL_DATA + 4); 50862306a36Sopenharmony_ci } while (readb(v3->base + V3_MAIL_DATA) != 0xaa && 50962306a36Sopenharmony_ci readb(v3->base + V3_MAIL_DATA) != 0x55); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci dev_info(v3->dev, "initialized PCI V3 Integrator/AP integration\n"); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int v3_pci_setup_resource(struct v3_pci *v3, 51862306a36Sopenharmony_ci struct pci_host_bridge *host, 51962306a36Sopenharmony_ci struct resource_entry *win) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct device *dev = v3->dev; 52262306a36Sopenharmony_ci struct resource *mem; 52362306a36Sopenharmony_ci struct resource *io; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci switch (resource_type(win->res)) { 52662306a36Sopenharmony_ci case IORESOURCE_IO: 52762306a36Sopenharmony_ci io = win->res; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Setup window 2 - PCI I/O */ 53062306a36Sopenharmony_ci writel(v3_addr_to_lb_base2(pci_pio_to_address(io->start)) | 53162306a36Sopenharmony_ci V3_LB_BASE2_ENABLE, 53262306a36Sopenharmony_ci v3->base + V3_LB_BASE2); 53362306a36Sopenharmony_ci writew(v3_addr_to_lb_map2(io->start - win->offset), 53462306a36Sopenharmony_ci v3->base + V3_LB_MAP2); 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci case IORESOURCE_MEM: 53762306a36Sopenharmony_ci mem = win->res; 53862306a36Sopenharmony_ci if (mem->flags & IORESOURCE_PREFETCH) { 53962306a36Sopenharmony_ci mem->name = "V3 PCI PRE-MEM"; 54062306a36Sopenharmony_ci v3->pre_mem = mem->start; 54162306a36Sopenharmony_ci v3->pre_bus_addr = mem->start - win->offset; 54262306a36Sopenharmony_ci dev_dbg(dev, "PREFETCHABLE MEM window %pR, bus addr %pap\n", 54362306a36Sopenharmony_ci mem, &v3->pre_bus_addr); 54462306a36Sopenharmony_ci if (resource_size(mem) != SZ_256M) { 54562306a36Sopenharmony_ci dev_err(dev, "prefetchable memory range is not 256MB\n"); 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci if (v3->non_pre_mem && 54962306a36Sopenharmony_ci (mem->start != v3->non_pre_mem + SZ_256M)) { 55062306a36Sopenharmony_ci dev_err(dev, 55162306a36Sopenharmony_ci "prefetchable memory is not adjacent to non-prefetchable memory\n"); 55262306a36Sopenharmony_ci return -EINVAL; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci /* Setup window 1 - PCI prefetchable memory */ 55562306a36Sopenharmony_ci writel(v3_addr_to_lb_base(v3->pre_mem) | 55662306a36Sopenharmony_ci V3_LB_BASE_ADR_SIZE_256MB | 55762306a36Sopenharmony_ci V3_LB_BASE_PREFETCH | 55862306a36Sopenharmony_ci V3_LB_BASE_ENABLE, 55962306a36Sopenharmony_ci v3->base + V3_LB_BASE1); 56062306a36Sopenharmony_ci writew(v3_addr_to_lb_map(v3->pre_bus_addr) | 56162306a36Sopenharmony_ci V3_LB_MAP_TYPE_MEM, /* Was V3_LB_MAP_TYPE_MEM_MULTIPLE */ 56262306a36Sopenharmony_ci v3->base + V3_LB_MAP1); 56362306a36Sopenharmony_ci } else { 56462306a36Sopenharmony_ci mem->name = "V3 PCI NON-PRE-MEM"; 56562306a36Sopenharmony_ci v3->non_pre_mem = mem->start; 56662306a36Sopenharmony_ci v3->non_pre_bus_addr = mem->start - win->offset; 56762306a36Sopenharmony_ci dev_dbg(dev, "NON-PREFETCHABLE MEM window %pR, bus addr %pap\n", 56862306a36Sopenharmony_ci mem, &v3->non_pre_bus_addr); 56962306a36Sopenharmony_ci if (resource_size(mem) != SZ_256M) { 57062306a36Sopenharmony_ci dev_err(dev, 57162306a36Sopenharmony_ci "non-prefetchable memory range is not 256MB\n"); 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci /* Setup window 0 - PCI non-prefetchable memory */ 57562306a36Sopenharmony_ci writel(v3_addr_to_lb_base(v3->non_pre_mem) | 57662306a36Sopenharmony_ci V3_LB_BASE_ADR_SIZE_256MB | 57762306a36Sopenharmony_ci V3_LB_BASE_ENABLE, 57862306a36Sopenharmony_ci v3->base + V3_LB_BASE0); 57962306a36Sopenharmony_ci writew(v3_addr_to_lb_map(v3->non_pre_bus_addr) | 58062306a36Sopenharmony_ci V3_LB_MAP_TYPE_MEM, 58162306a36Sopenharmony_ci v3->base + V3_LB_MAP0); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci case IORESOURCE_BUS: 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci default: 58762306a36Sopenharmony_ci dev_info(dev, "Unknown resource type %lu\n", 58862306a36Sopenharmony_ci resource_type(win->res)); 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int v3_get_dma_range_config(struct v3_pci *v3, 59662306a36Sopenharmony_ci struct resource_entry *entry, 59762306a36Sopenharmony_ci u32 *pci_base, u32 *pci_map) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct device *dev = v3->dev; 60062306a36Sopenharmony_ci u64 cpu_addr = entry->res->start; 60162306a36Sopenharmony_ci u64 cpu_end = entry->res->end; 60262306a36Sopenharmony_ci u64 pci_end = cpu_end - entry->offset; 60362306a36Sopenharmony_ci u64 pci_addr = entry->res->start - entry->offset; 60462306a36Sopenharmony_ci u32 val; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (pci_addr & ~V3_PCI_BASE_M_ADR_BASE) { 60762306a36Sopenharmony_ci dev_err(dev, "illegal range, only PCI bits 31..20 allowed\n"); 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci val = ((u32)pci_addr) & V3_PCI_BASE_M_ADR_BASE; 61162306a36Sopenharmony_ci *pci_base = val; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) { 61462306a36Sopenharmony_ci dev_err(dev, "illegal range, only CPU bits 31..20 allowed\n"); 61562306a36Sopenharmony_ci return -EINVAL; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci val = ((u32)cpu_addr) & V3_PCI_MAP_M_MAP_ADR; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci switch (resource_size(entry->res)) { 62062306a36Sopenharmony_ci case SZ_1M: 62162306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_1MB; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci case SZ_2M: 62462306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_2MB; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case SZ_4M: 62762306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_4MB; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case SZ_8M: 63062306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_8MB; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case SZ_16M: 63362306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_16MB; 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case SZ_32M: 63662306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_32MB; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci case SZ_64M: 63962306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_64MB; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case SZ_128M: 64262306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_128MB; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case SZ_256M: 64562306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_256MB; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci case SZ_512M: 64862306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_512MB; 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci case SZ_1G: 65162306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_1GB; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case SZ_2G: 65462306a36Sopenharmony_ci val |= V3_LB_BASE_ADR_SIZE_2GB; 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci default: 65762306a36Sopenharmony_ci dev_err(v3->dev, "illegal dma memory chunk size\n"); 65862306a36Sopenharmony_ci return -EINVAL; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE; 66162306a36Sopenharmony_ci *pci_map = val; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci dev_dbg(dev, 66462306a36Sopenharmony_ci "DMA MEM CPU: 0x%016llx -> 0x%016llx => " 66562306a36Sopenharmony_ci "PCI: 0x%016llx -> 0x%016llx base %08x map %08x\n", 66662306a36Sopenharmony_ci cpu_addr, cpu_end, 66762306a36Sopenharmony_ci pci_addr, pci_end, 66862306a36Sopenharmony_ci *pci_base, *pci_map); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int v3_pci_parse_map_dma_ranges(struct v3_pci *v3, 67462306a36Sopenharmony_ci struct device_node *np) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct pci_host_bridge *bridge = pci_host_bridge_from_priv(v3); 67762306a36Sopenharmony_ci struct device *dev = v3->dev; 67862306a36Sopenharmony_ci struct resource_entry *entry; 67962306a36Sopenharmony_ci int i = 0; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci resource_list_for_each_entry(entry, &bridge->dma_ranges) { 68262306a36Sopenharmony_ci int ret; 68362306a36Sopenharmony_ci u32 pci_base, pci_map; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ret = v3_get_dma_range_config(v3, entry, &pci_base, &pci_map); 68662306a36Sopenharmony_ci if (ret) 68762306a36Sopenharmony_ci return ret; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (i == 0) { 69062306a36Sopenharmony_ci writel(pci_base, v3->base + V3_PCI_BASE0); 69162306a36Sopenharmony_ci writel(pci_map, v3->base + V3_PCI_MAP0); 69262306a36Sopenharmony_ci } else if (i == 1) { 69362306a36Sopenharmony_ci writel(pci_base, v3->base + V3_PCI_BASE1); 69462306a36Sopenharmony_ci writel(pci_map, v3->base + V3_PCI_MAP1); 69562306a36Sopenharmony_ci } else { 69662306a36Sopenharmony_ci dev_err(dev, "too many ranges, only two supported\n"); 69762306a36Sopenharmony_ci dev_err(dev, "range %d ignored\n", i); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci i++; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int v3_pci_probe(struct platform_device *pdev) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 70762306a36Sopenharmony_ci struct device_node *np = dev->of_node; 70862306a36Sopenharmony_ci struct resource *regs; 70962306a36Sopenharmony_ci struct resource_entry *win; 71062306a36Sopenharmony_ci struct v3_pci *v3; 71162306a36Sopenharmony_ci struct pci_host_bridge *host; 71262306a36Sopenharmony_ci struct clk *clk; 71362306a36Sopenharmony_ci u16 val; 71462306a36Sopenharmony_ci int irq; 71562306a36Sopenharmony_ci int ret; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci host = devm_pci_alloc_host_bridge(dev, sizeof(*v3)); 71862306a36Sopenharmony_ci if (!host) 71962306a36Sopenharmony_ci return -ENOMEM; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci host->ops = &v3_pci_ops; 72262306a36Sopenharmony_ci v3 = pci_host_bridge_priv(host); 72362306a36Sopenharmony_ci host->sysdata = v3; 72462306a36Sopenharmony_ci v3->dev = dev; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Get and enable host clock */ 72762306a36Sopenharmony_ci clk = devm_clk_get(dev, NULL); 72862306a36Sopenharmony_ci if (IS_ERR(clk)) { 72962306a36Sopenharmony_ci dev_err(dev, "clock not found\n"); 73062306a36Sopenharmony_ci return PTR_ERR(clk); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci ret = clk_prepare_enable(clk); 73362306a36Sopenharmony_ci if (ret) { 73462306a36Sopenharmony_ci dev_err(dev, "unable to enable clock\n"); 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci v3->base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); 73962306a36Sopenharmony_ci if (IS_ERR(v3->base)) 74062306a36Sopenharmony_ci return PTR_ERR(v3->base); 74162306a36Sopenharmony_ci /* 74262306a36Sopenharmony_ci * The hardware has a register with the physical base address 74362306a36Sopenharmony_ci * of the V3 controller itself, verify that this is the same 74462306a36Sopenharmony_ci * as the physical memory we've remapped it from. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci if (readl(v3->base + V3_LB_IO_BASE) != (regs->start >> 16)) 74762306a36Sopenharmony_ci dev_err(dev, "V3_LB_IO_BASE = %08x but device is @%pR\n", 74862306a36Sopenharmony_ci readl(v3->base + V3_LB_IO_BASE), regs); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* Configuration space is 16MB directly mapped */ 75162306a36Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); 75262306a36Sopenharmony_ci if (resource_size(regs) != SZ_16M) { 75362306a36Sopenharmony_ci dev_err(dev, "config mem is not 16MB!\n"); 75462306a36Sopenharmony_ci return -EINVAL; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci v3->config_mem = regs->start; 75762306a36Sopenharmony_ci v3->config_base = devm_ioremap_resource(dev, regs); 75862306a36Sopenharmony_ci if (IS_ERR(v3->config_base)) 75962306a36Sopenharmony_ci return PTR_ERR(v3->config_base); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* Get and request error IRQ resource */ 76262306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 76362306a36Sopenharmony_ci if (irq < 0) 76462306a36Sopenharmony_ci return irq; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, v3_irq, 0, 76762306a36Sopenharmony_ci "PCIv3 error", v3); 76862306a36Sopenharmony_ci if (ret < 0) { 76962306a36Sopenharmony_ci dev_err(dev, 77062306a36Sopenharmony_ci "unable to request PCIv3 error IRQ %d (%d)\n", 77162306a36Sopenharmony_ci irq, ret); 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* 77662306a36Sopenharmony_ci * Unlock V3 registers, but only if they were previously locked. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci if (readw(v3->base + V3_SYSTEM) & V3_SYSTEM_M_LOCK) 77962306a36Sopenharmony_ci writew(V3_SYSTEM_UNLOCK, v3->base + V3_SYSTEM); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Disable all slave access while we set up the windows */ 78262306a36Sopenharmony_ci val = readw(v3->base + V3_PCI_CMD); 78362306a36Sopenharmony_ci val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); 78462306a36Sopenharmony_ci writew(val, v3->base + V3_PCI_CMD); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Put the PCI bus into reset */ 78762306a36Sopenharmony_ci val = readw(v3->base + V3_SYSTEM); 78862306a36Sopenharmony_ci val &= ~V3_SYSTEM_M_RST_OUT; 78962306a36Sopenharmony_ci writew(val, v3->base + V3_SYSTEM); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Retry until we're ready */ 79262306a36Sopenharmony_ci val = readw(v3->base + V3_PCI_CFG); 79362306a36Sopenharmony_ci val |= V3_PCI_CFG_M_RETRY_EN; 79462306a36Sopenharmony_ci writew(val, v3->base + V3_PCI_CFG); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Set up the local bus protocol */ 79762306a36Sopenharmony_ci val = readw(v3->base + V3_LB_CFG); 79862306a36Sopenharmony_ci val |= V3_LB_CFG_LB_BE_IMODE; /* Byte enable input */ 79962306a36Sopenharmony_ci val |= V3_LB_CFG_LB_BE_OMODE; /* Byte enable output */ 80062306a36Sopenharmony_ci val &= ~V3_LB_CFG_LB_ENDIAN; /* Little endian */ 80162306a36Sopenharmony_ci val &= ~V3_LB_CFG_LB_PPC_RDY; /* TODO: when using on PPC403Gx, set to 1 */ 80262306a36Sopenharmony_ci writew(val, v3->base + V3_LB_CFG); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Enable the PCI bus master */ 80562306a36Sopenharmony_ci val = readw(v3->base + V3_PCI_CMD); 80662306a36Sopenharmony_ci val |= PCI_COMMAND_MASTER; 80762306a36Sopenharmony_ci writew(val, v3->base + V3_PCI_CMD); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Get the I/O and memory ranges from DT */ 81062306a36Sopenharmony_ci resource_list_for_each_entry(win, &host->windows) { 81162306a36Sopenharmony_ci ret = v3_pci_setup_resource(v3, host, win); 81262306a36Sopenharmony_ci if (ret) { 81362306a36Sopenharmony_ci dev_err(dev, "error setting up resources\n"); 81462306a36Sopenharmony_ci return ret; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci ret = v3_pci_parse_map_dma_ranges(v3, np); 81862306a36Sopenharmony_ci if (ret) 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* 82262306a36Sopenharmony_ci * Disable PCI to host IO cycles, enable I/O buffers @3.3V, 82362306a36Sopenharmony_ci * set AD_LOW0 to 1 if one of the LB_MAP registers choose 82462306a36Sopenharmony_ci * to use this (should be unused). 82562306a36Sopenharmony_ci */ 82662306a36Sopenharmony_ci writel(0x00000000, v3->base + V3_PCI_IO_BASE); 82762306a36Sopenharmony_ci val = V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS | 82862306a36Sopenharmony_ci V3_PCI_CFG_M_EN3V | V3_PCI_CFG_M_AD_LOW0; 82962306a36Sopenharmony_ci /* 83062306a36Sopenharmony_ci * DMA read and write from PCI bus commands types 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_ci val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_RTYPE_SHIFT; 83362306a36Sopenharmony_ci val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_WTYPE_SHIFT; 83462306a36Sopenharmony_ci writew(val, v3->base + V3_PCI_CFG); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* 83762306a36Sopenharmony_ci * Set the V3 FIFO such that writes have higher priority than 83862306a36Sopenharmony_ci * reads, and local bus write causes local bus read fifo flush 83962306a36Sopenharmony_ci * on aperture 1. Same for PCI. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci writew(V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 | 84262306a36Sopenharmony_ci V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 | 84362306a36Sopenharmony_ci V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 | 84462306a36Sopenharmony_ci V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1, 84562306a36Sopenharmony_ci v3->base + V3_FIFO_PRIORITY); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* 84962306a36Sopenharmony_ci * Clear any error interrupts, and enable parity and write error 85062306a36Sopenharmony_ci * interrupts 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci writeb(0, v3->base + V3_LB_ISTAT); 85362306a36Sopenharmony_ci val = readw(v3->base + V3_LB_CFG); 85462306a36Sopenharmony_ci val |= V3_LB_CFG_LB_LB_INT; 85562306a36Sopenharmony_ci writew(val, v3->base + V3_LB_CFG); 85662306a36Sopenharmony_ci writeb(V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, 85762306a36Sopenharmony_ci v3->base + V3_LB_IMASK); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Special Integrator initialization */ 86062306a36Sopenharmony_ci if (of_device_is_compatible(np, "arm,integrator-ap-pci")) { 86162306a36Sopenharmony_ci ret = v3_integrator_init(v3); 86262306a36Sopenharmony_ci if (ret) 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* Post-init: enable PCI memory and invalidate (master already on) */ 86762306a36Sopenharmony_ci val = readw(v3->base + V3_PCI_CMD); 86862306a36Sopenharmony_ci val |= PCI_COMMAND_MEMORY | PCI_COMMAND_INVALIDATE; 86962306a36Sopenharmony_ci writew(val, v3->base + V3_PCI_CMD); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* Clear pending interrupts */ 87262306a36Sopenharmony_ci writeb(0, v3->base + V3_LB_ISTAT); 87362306a36Sopenharmony_ci /* Read or write errors and parity errors cause interrupts */ 87462306a36Sopenharmony_ci writeb(V3_LB_ISTAT_PCI_RD | V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, 87562306a36Sopenharmony_ci v3->base + V3_LB_IMASK); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Take the PCI bus out of reset so devices can initialize */ 87862306a36Sopenharmony_ci val = readw(v3->base + V3_SYSTEM); 87962306a36Sopenharmony_ci val |= V3_SYSTEM_M_RST_OUT; 88062306a36Sopenharmony_ci writew(val, v3->base + V3_SYSTEM); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* 88362306a36Sopenharmony_ci * Re-lock the system register. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci val = readw(v3->base + V3_SYSTEM); 88662306a36Sopenharmony_ci val |= V3_SYSTEM_M_LOCK; 88762306a36Sopenharmony_ci writew(val, v3->base + V3_SYSTEM); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return pci_host_probe(host); 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic const struct of_device_id v3_pci_of_match[] = { 89362306a36Sopenharmony_ci { 89462306a36Sopenharmony_ci .compatible = "v3,v360epc-pci", 89562306a36Sopenharmony_ci }, 89662306a36Sopenharmony_ci {}, 89762306a36Sopenharmony_ci}; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic struct platform_driver v3_pci_driver = { 90062306a36Sopenharmony_ci .driver = { 90162306a36Sopenharmony_ci .name = "pci-v3-semi", 90262306a36Sopenharmony_ci .of_match_table = v3_pci_of_match, 90362306a36Sopenharmony_ci .suppress_bind_attrs = true, 90462306a36Sopenharmony_ci }, 90562306a36Sopenharmony_ci .probe = v3_pci_probe, 90662306a36Sopenharmony_ci}; 90762306a36Sopenharmony_cibuiltin_platform_driver(v3_pci_driver); 908