162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/kernel/dec21285.c: PCI functions for DC21285 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1998-2001 Russell King 662306a36Sopenharmony_ci * Copyright (C) 1998-2000 Phil Blundell 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/ioport.h> 1662306a36Sopenharmony_ci#include <linux/irq.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/irq.h> 2162306a36Sopenharmony_ci#include <asm/mach/pci.h> 2262306a36Sopenharmony_ci#include <asm/hardware/dec21285.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MAX_SLOTS 21 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define PCICMD_ABORT ((PCI_STATUS_REC_MASTER_ABORT| \ 2762306a36Sopenharmony_ci PCI_STATUS_REC_TARGET_ABORT)<<16) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define PCICMD_ERROR_BITS ((PCI_STATUS_DETECTED_PARITY | \ 3062306a36Sopenharmony_ci PCI_STATUS_REC_MASTER_ABORT | \ 3162306a36Sopenharmony_ci PCI_STATUS_REC_TARGET_ABORT | \ 3262306a36Sopenharmony_ci PCI_STATUS_PARITY) << 16) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciextern int setup_arm_irq(int, struct irqaction *); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic unsigned long 3762306a36Sopenharmony_cidc21285_base_address(struct pci_bus *bus, unsigned int devfn) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci unsigned long addr = 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (bus->number == 0) { 4262306a36Sopenharmony_ci if (PCI_SLOT(devfn) == 0) 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * For devfn 0, point at the 21285 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci addr = ARMCSR_BASE; 4762306a36Sopenharmony_ci else { 4862306a36Sopenharmony_ci devfn -= 1 << 3; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (devfn < PCI_DEVFN(MAX_SLOTS, 0)) 5162306a36Sopenharmony_ci addr = PCICFG0_BASE | 0xc00000 | (devfn << 8); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } else 5462306a36Sopenharmony_ci addr = PCICFG1_BASE | (bus->number << 16) | (devfn << 8); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return addr; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int 6062306a36Sopenharmony_cidc21285_read_config(struct pci_bus *bus, unsigned int devfn, int where, 6162306a36Sopenharmony_ci int size, u32 *value) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned long addr = dc21285_base_address(bus, devfn); 6462306a36Sopenharmony_ci u32 v = 0xffffffff; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (addr) 6762306a36Sopenharmony_ci switch (size) { 6862306a36Sopenharmony_ci case 1: 6962306a36Sopenharmony_ci asm volatile("ldrb %0, [%1, %2]" 7062306a36Sopenharmony_ci : "=r" (v) : "r" (addr), "r" (where) : "cc"); 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case 2: 7362306a36Sopenharmony_ci asm volatile("ldrh %0, [%1, %2]" 7462306a36Sopenharmony_ci : "=r" (v) : "r" (addr), "r" (where) : "cc"); 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case 4: 7762306a36Sopenharmony_ci asm volatile("ldr %0, [%1, %2]" 7862306a36Sopenharmony_ci : "=r" (v) : "r" (addr), "r" (where) : "cc"); 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci *value = v; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci v = *CSR_PCICMD; 8562306a36Sopenharmony_ci if (v & PCICMD_ABORT) { 8662306a36Sopenharmony_ci *CSR_PCICMD = v & (0xffff|PCICMD_ABORT); 8762306a36Sopenharmony_ci return -1; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int 9462306a36Sopenharmony_cidc21285_write_config(struct pci_bus *bus, unsigned int devfn, int where, 9562306a36Sopenharmony_ci int size, u32 value) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci unsigned long addr = dc21285_base_address(bus, devfn); 9862306a36Sopenharmony_ci u32 v; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (addr) 10162306a36Sopenharmony_ci switch (size) { 10262306a36Sopenharmony_ci case 1: 10362306a36Sopenharmony_ci asm volatile("strb %0, [%1, %2]" 10462306a36Sopenharmony_ci : : "r" (value), "r" (addr), "r" (where) 10562306a36Sopenharmony_ci : "cc"); 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case 2: 10862306a36Sopenharmony_ci asm volatile("strh %0, [%1, %2]" 10962306a36Sopenharmony_ci : : "r" (value), "r" (addr), "r" (where) 11062306a36Sopenharmony_ci : "cc"); 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case 4: 11362306a36Sopenharmony_ci asm volatile("str %0, [%1, %2]" 11462306a36Sopenharmony_ci : : "r" (value), "r" (addr), "r" (where) 11562306a36Sopenharmony_ci : "cc"); 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci v = *CSR_PCICMD; 12062306a36Sopenharmony_ci if (v & PCICMD_ABORT) { 12162306a36Sopenharmony_ci *CSR_PCICMD = v & (0xffff|PCICMD_ABORT); 12262306a36Sopenharmony_ci return -1; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistruct pci_ops dc21285_ops = { 12962306a36Sopenharmony_ci .read = dc21285_read_config, 13062306a36Sopenharmony_ci .write = dc21285_write_config, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct timer_list serr_timer; 13462306a36Sopenharmony_cistatic struct timer_list perr_timer; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void dc21285_enable_error(struct timer_list *timer) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci del_timer(timer); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (timer == &serr_timer) 14162306a36Sopenharmony_ci enable_irq(IRQ_PCI_SERR); 14262306a36Sopenharmony_ci else if (timer == &perr_timer) 14362306a36Sopenharmony_ci enable_irq(IRQ_PCI_PERR); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Warn on PCI errors. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic irqreturn_t dc21285_abort_irq(int irq, void *dev_id) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned int cmd; 15262306a36Sopenharmony_ci unsigned int status; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci cmd = *CSR_PCICMD; 15562306a36Sopenharmony_ci status = cmd >> 16; 15662306a36Sopenharmony_ci cmd = cmd & 0xffff; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (status & PCI_STATUS_REC_MASTER_ABORT) { 15962306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n", 16062306a36Sopenharmony_ci instruction_pointer(get_irq_regs())); 16162306a36Sopenharmony_ci cmd |= PCI_STATUS_REC_MASTER_ABORT << 16; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (status & PCI_STATUS_REC_TARGET_ABORT) { 16562306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: target abort: "); 16662306a36Sopenharmony_ci pcibios_report_status(PCI_STATUS_REC_MASTER_ABORT | 16762306a36Sopenharmony_ci PCI_STATUS_SIG_TARGET_ABORT | 16862306a36Sopenharmony_ci PCI_STATUS_REC_TARGET_ABORT, 1); 16962306a36Sopenharmony_ci printk("\n"); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci cmd |= PCI_STATUS_REC_TARGET_ABORT << 16; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci *CSR_PCICMD = cmd; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return IRQ_HANDLED; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic irqreturn_t dc21285_serr_irq(int irq, void *dev_id) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct timer_list *timer = dev_id; 18262306a36Sopenharmony_ci unsigned int cntl; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: system error received: "); 18562306a36Sopenharmony_ci pcibios_report_status(PCI_STATUS_SIG_SYSTEM_ERROR, 1); 18662306a36Sopenharmony_ci printk("\n"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci cntl = *CSR_SA110_CNTL & 0xffffdf07; 18962306a36Sopenharmony_ci *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * back off this interrupt 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci disable_irq(irq); 19562306a36Sopenharmony_ci timer->expires = jiffies + HZ; 19662306a36Sopenharmony_ci add_timer(timer); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return IRQ_HANDLED; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic irqreturn_t dc21285_discard_irq(int irq, void *dev_id) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: discard timer expired\n"); 20462306a36Sopenharmony_ci *CSR_SA110_CNTL &= 0xffffde07; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return IRQ_HANDLED; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic irqreturn_t dc21285_dparity_irq(int irq, void *dev_id) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci unsigned int cmd; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: data parity error detected: "); 21462306a36Sopenharmony_ci pcibios_report_status(PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY, 1); 21562306a36Sopenharmony_ci printk("\n"); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci cmd = *CSR_PCICMD & 0xffff; 21862306a36Sopenharmony_ci *CSR_PCICMD = cmd | 1 << 24; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return IRQ_HANDLED; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic irqreturn_t dc21285_parity_irq(int irq, void *dev_id) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct timer_list *timer = dev_id; 22662306a36Sopenharmony_ci unsigned int cmd; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: parity error detected: "); 22962306a36Sopenharmony_ci pcibios_report_status(PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY, 1); 23062306a36Sopenharmony_ci printk("\n"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci cmd = *CSR_PCICMD & 0xffff; 23362306a36Sopenharmony_ci *CSR_PCICMD = cmd | 1 << 31; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * back off this interrupt 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci disable_irq(irq); 23962306a36Sopenharmony_ci timer->expires = jiffies + HZ; 24062306a36Sopenharmony_ci add_timer(timer); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return IRQ_HANDLED; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int dc21285_pci_bus_notifier(struct notifier_block *nb, 24662306a36Sopenharmony_ci unsigned long action, 24762306a36Sopenharmony_ci void *data) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci if (action != BUS_NOTIFY_ADD_DEVICE) 25062306a36Sopenharmony_ci return NOTIFY_DONE; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dma_direct_set_offset(data, PHYS_OFFSET, BUS_OFFSET, SZ_256M); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return NOTIFY_OK; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic struct notifier_block dc21285_pci_bus_nb = { 25862306a36Sopenharmony_ci .notifier_call = dc21285_pci_bus_notifier, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciint __init dc21285_setup(int nr, struct pci_sys_data *sys) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct resource *res; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci res = kcalloc(2, sizeof(struct resource), GFP_KERNEL); 26662306a36Sopenharmony_ci if (!res) { 26762306a36Sopenharmony_ci printk("out of memory for root bus resources"); 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci res[0].flags = IORESOURCE_MEM; 27262306a36Sopenharmony_ci res[0].name = "Footbridge non-prefetch"; 27362306a36Sopenharmony_ci res[1].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; 27462306a36Sopenharmony_ci res[1].name = "Footbridge prefetch"; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci allocate_resource(&iomem_resource, &res[1], 0x20000000, 27762306a36Sopenharmony_ci 0xa0000000, 0xffffffff, 0x20000000, NULL, NULL); 27862306a36Sopenharmony_ci allocate_resource(&iomem_resource, &res[0], 0x40000000, 27962306a36Sopenharmony_ci 0x80000000, 0xffffffff, 0x40000000, NULL, NULL); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci sys->mem_offset = DC21285_PCI_MEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci pci_add_resource_offset(&sys->resources, &res[0], sys->mem_offset); 28462306a36Sopenharmony_ci pci_add_resource_offset(&sys->resources, &res[1], sys->mem_offset); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci bus_register_notifier(&pci_bus_type, &dc21285_pci_bus_nb); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 1; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#define dc21285_request_irq(_a, _b, _c, _d, _e) \ 29262306a36Sopenharmony_ci WARN_ON(request_irq(_a, _b, _c, _d, _e) < 0) 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_civoid __init dc21285_preinit(void) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned int mem_size, mem_mask; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci pcibios_min_mem = 0x81000000; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci mem_size = (unsigned int)high_memory - PAGE_OFFSET; 30162306a36Sopenharmony_ci for (mem_mask = 0x00100000; mem_mask < 0x10000000; mem_mask <<= 1) 30262306a36Sopenharmony_ci if (mem_mask >= mem_size) 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * These registers need to be set up whether we're the 30762306a36Sopenharmony_ci * central function or not. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci *CSR_SDRAMBASEMASK = (mem_mask - 1) & 0x0ffc0000; 31062306a36Sopenharmony_ci *CSR_SDRAMBASEOFFSET = 0; 31162306a36Sopenharmony_ci *CSR_ROMBASEMASK = 0x80000000; 31262306a36Sopenharmony_ci *CSR_CSRBASEMASK = 0; 31362306a36Sopenharmony_ci *CSR_CSRBASEOFFSET = 0; 31462306a36Sopenharmony_ci *CSR_PCIADDR_EXTN = 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci printk(KERN_INFO "PCI: DC21285 footbridge, revision %02lX, in " 31762306a36Sopenharmony_ci "central function mode\n", *CSR_CLASSREV & 0xff); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * Clear any existing errors - we aren't 32162306a36Sopenharmony_ci * interested in historical data... 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci *CSR_SA110_CNTL = (*CSR_SA110_CNTL & 0xffffde07) | SA110_CNTL_RXSERR; 32462306a36Sopenharmony_ci *CSR_PCICMD = (*CSR_PCICMD & 0xffff) | PCICMD_ERROR_BITS; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci timer_setup(&serr_timer, dc21285_enable_error, 0); 32762306a36Sopenharmony_ci timer_setup(&perr_timer, dc21285_enable_error, 0); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * We don't care if these fail. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci dc21285_request_irq(IRQ_PCI_SERR, dc21285_serr_irq, 0, 33362306a36Sopenharmony_ci "PCI system error", &serr_timer); 33462306a36Sopenharmony_ci dc21285_request_irq(IRQ_PCI_PERR, dc21285_parity_irq, 0, 33562306a36Sopenharmony_ci "PCI parity error", &perr_timer); 33662306a36Sopenharmony_ci dc21285_request_irq(IRQ_PCI_ABORT, dc21285_abort_irq, 0, 33762306a36Sopenharmony_ci "PCI abort", NULL); 33862306a36Sopenharmony_ci dc21285_request_irq(IRQ_DISCARD_TIMER, dc21285_discard_irq, 0, 33962306a36Sopenharmony_ci "Discard timer", NULL); 34062306a36Sopenharmony_ci dc21285_request_irq(IRQ_PCI_DPERR, dc21285_dparity_irq, 0, 34162306a36Sopenharmony_ci "PCI data parity", NULL); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* 34462306a36Sopenharmony_ci * Map our SDRAM at a known address in PCI space, just in case 34562306a36Sopenharmony_ci * the firmware had other ideas. Using a nonzero base is 34662306a36Sopenharmony_ci * necessary, since some VGA cards forcefully use PCI addresses 34762306a36Sopenharmony_ci * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards). 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci *CSR_PCICSRBASE = 0xf4000000; 35062306a36Sopenharmony_ci *CSR_PCICSRIOBASE = 0; 35162306a36Sopenharmony_ci *CSR_PCISDRAMBASE = BUS_OFFSET; 35262306a36Sopenharmony_ci *CSR_PCIROMBASE = 0; 35362306a36Sopenharmony_ci *CSR_PCICMD = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | 35462306a36Sopenharmony_ci PCI_COMMAND_INVALIDATE | PCICMD_ERROR_BITS; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_civoid __init dc21285_postinit(void) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci register_isa_ports(DC21285_PCI_MEM, DC21285_PCI_IO, 0); 36062306a36Sopenharmony_ci} 361