18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Common routines for Tundra Semiconductor TSI108 host bridge. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * 2004-2005 (c) Tundra Semiconductor Corp. 68c2ecf20Sopenharmony_ci * Author: Alex Bounine (alexandreb@tundra.com) 78c2ecf20Sopenharmony_ci * Author: Roy Zang (tie-fei.zang@freescale.com) 88c2ecf20Sopenharmony_ci * Add pci interrupt router host 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 188c2ecf20Sopenharmony_ci#include <asm/io.h> 198c2ecf20Sopenharmony_ci#include <asm/irq.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci#include <asm/machdep.h> 228c2ecf20Sopenharmony_ci#include <asm/pci-bridge.h> 238c2ecf20Sopenharmony_ci#include <asm/tsi108.h> 248c2ecf20Sopenharmony_ci#include <asm/tsi108_pci.h> 258c2ecf20Sopenharmony_ci#include <asm/tsi108_irq.h> 268c2ecf20Sopenharmony_ci#include <asm/prom.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#undef DEBUG 298c2ecf20Sopenharmony_ci#ifdef DEBUG 308c2ecf20Sopenharmony_ci#define DBG(x...) printk(x) 318c2ecf20Sopenharmony_ci#else 328c2ecf20Sopenharmony_ci#define DBG(x...) 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define tsi_mk_config_addr(bus, devfunc, offset) \ 368c2ecf20Sopenharmony_ci ((((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)) + tsi108_pci_cfg_base) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciu32 tsi108_pci_cfg_base; 398c2ecf20Sopenharmony_cistatic u32 tsi108_pci_cfg_phys; 408c2ecf20Sopenharmony_ciu32 tsi108_csr_vir_base; 418c2ecf20Sopenharmony_cistatic struct irq_domain *pci_irq_host; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciextern u32 get_vir_csrbase(void); 448c2ecf20Sopenharmony_ciextern u32 tsi108_read_reg(u32 reg_offset); 458c2ecf20Sopenharmony_ciextern void tsi108_write_reg(u32 reg_offset, u32 val); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ciint 488c2ecf20Sopenharmony_citsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc, 498c2ecf20Sopenharmony_ci int offset, int len, u32 val) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci volatile unsigned char *cfg_addr; 528c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (ppc_md.pci_exclude_device) 558c2ecf20Sopenharmony_ci if (ppc_md.pci_exclude_device(hose, bus->number, devfunc)) 568c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, 598c2ecf20Sopenharmony_ci devfunc, offset) | 608c2ecf20Sopenharmony_ci (offset & 0x03)); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#ifdef DEBUG 638c2ecf20Sopenharmony_ci printk("PCI CFG write : "); 648c2ecf20Sopenharmony_ci printk("%d:0x%x:0x%x ", bus->number, devfunc, offset); 658c2ecf20Sopenharmony_ci printk("%d ADDR=0x%08x ", len, (uint) cfg_addr); 668c2ecf20Sopenharmony_ci printk("data = 0x%08x\n", val); 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci switch (len) { 708c2ecf20Sopenharmony_ci case 1: 718c2ecf20Sopenharmony_ci out_8((u8 *) cfg_addr, val); 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case 2: 748c2ecf20Sopenharmony_ci out_le16((u16 *) cfg_addr, val); 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci out_le32((u32 *) cfg_addr, val); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_civoid tsi108_clear_pci_error(u32 pci_cfg_base) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci u32 err_stat, err_addr, pci_stat; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * Quietly clear PB and PCI error flags set as result 908c2ecf20Sopenharmony_ci * of PCI/X configuration read requests. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Read PB Error Log Registers */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci err_stat = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS); 968c2ecf20Sopenharmony_ci err_addr = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_AERR); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (err_stat & TSI108_PB_ERRCS_ES) { 998c2ecf20Sopenharmony_ci /* Clear error flag */ 1008c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS, 1018c2ecf20Sopenharmony_ci TSI108_PB_ERRCS_ES); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Clear read error reported in PB_ISR */ 1048c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ISR, 1058c2ecf20Sopenharmony_ci TSI108_PB_ISR_PBS_RD_ERR); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Clear PCI/X bus cfg errors if applicable */ 1088c2ecf20Sopenharmony_ci if ((err_addr & 0xFF000000) == pci_cfg_base) { 1098c2ecf20Sopenharmony_ci pci_stat = 1108c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR); 1118c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR, 1128c2ecf20Sopenharmony_ci pci_stat); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define __tsi108_read_pci_config(x, addr, op) \ 1208c2ecf20Sopenharmony_ci __asm__ __volatile__( \ 1218c2ecf20Sopenharmony_ci " "op" %0,0,%1\n" \ 1228c2ecf20Sopenharmony_ci "1: eieio\n" \ 1238c2ecf20Sopenharmony_ci "2:\n" \ 1248c2ecf20Sopenharmony_ci ".section .fixup,\"ax\"\n" \ 1258c2ecf20Sopenharmony_ci "3: li %0,-1\n" \ 1268c2ecf20Sopenharmony_ci " b 2b\n" \ 1278c2ecf20Sopenharmony_ci ".previous\n" \ 1288c2ecf20Sopenharmony_ci EX_TABLE(1b, 3b) \ 1298c2ecf20Sopenharmony_ci : "=r"(x) : "r"(addr)) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint 1328c2ecf20Sopenharmony_citsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset, 1338c2ecf20Sopenharmony_ci int len, u32 * val) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci volatile unsigned char *cfg_addr; 1368c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 1378c2ecf20Sopenharmony_ci u32 temp; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (ppc_md.pci_exclude_device) 1408c2ecf20Sopenharmony_ci if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) 1418c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, 1448c2ecf20Sopenharmony_ci devfn, 1458c2ecf20Sopenharmony_ci offset) | (offset & 1468c2ecf20Sopenharmony_ci 0x03)); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci switch (len) { 1498c2ecf20Sopenharmony_ci case 1: 1508c2ecf20Sopenharmony_ci __tsi108_read_pci_config(temp, cfg_addr, "lbzx"); 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci case 2: 1538c2ecf20Sopenharmony_ci __tsi108_read_pci_config(temp, cfg_addr, "lhbrx"); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci default: 1568c2ecf20Sopenharmony_ci __tsi108_read_pci_config(temp, cfg_addr, "lwbrx"); 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci *val = temp; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#ifdef DEBUG 1638c2ecf20Sopenharmony_ci if ((0xFFFFFFFF != temp) && (0xFFFF != temp) && (0xFF != temp)) { 1648c2ecf20Sopenharmony_ci printk("PCI CFG read : "); 1658c2ecf20Sopenharmony_ci printk("%d:0x%x:0x%x ", bus->number, devfn, offset); 1668c2ecf20Sopenharmony_ci printk("%d ADDR=0x%08x ", len, (uint) cfg_addr); 1678c2ecf20Sopenharmony_ci printk("data = 0x%x\n", *val); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_civoid tsi108_clear_pci_cfg_error(void) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci tsi108_clear_pci_error(tsi108_pci_cfg_phys); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct pci_ops tsi108_direct_pci_ops = { 1798c2ecf20Sopenharmony_ci .read = tsi108_direct_read_config, 1808c2ecf20Sopenharmony_ci .write = tsi108_direct_write_config, 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciint __init tsi108_setup_pci(struct device_node *dev, u32 cfg_phys, int primary) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int len; 1868c2ecf20Sopenharmony_ci struct pci_controller *hose; 1878c2ecf20Sopenharmony_ci struct resource rsrc; 1888c2ecf20Sopenharmony_ci const int *bus_range; 1898c2ecf20Sopenharmony_ci int has_address = 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* PCI Config mapping */ 1928c2ecf20Sopenharmony_ci tsi108_pci_cfg_base = (u32)ioremap(cfg_phys, TSI108_PCI_CFG_SIZE); 1938c2ecf20Sopenharmony_ci tsi108_pci_cfg_phys = cfg_phys; 1948c2ecf20Sopenharmony_ci DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __func__, 1958c2ecf20Sopenharmony_ci tsi108_pci_cfg_base); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Fetch host bridge registers address */ 1988c2ecf20Sopenharmony_ci has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Get bus range if any */ 2018c2ecf20Sopenharmony_ci bus_range = of_get_property(dev, "bus-range", &len); 2028c2ecf20Sopenharmony_ci if (bus_range == NULL || len < 2 * sizeof(int)) { 2038c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF, assume" 2048c2ecf20Sopenharmony_ci " bus 0\n", dev); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci hose = pcibios_alloc_controller(dev); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!hose) { 2108c2ecf20Sopenharmony_ci printk("PCI Host bridge init failed\n"); 2118c2ecf20Sopenharmony_ci return -ENOMEM; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0; 2158c2ecf20Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci (hose)->ops = &tsi108_direct_pci_ops; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pr_info("Found tsi108 PCI host bridge at 0x%pa. Firmware bus number: %d->%d\n", 2208c2ecf20Sopenharmony_ci &rsrc.start, hose->first_busno, hose->last_busno); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Interpret the "ranges" property */ 2238c2ecf20Sopenharmony_ci /* This also maps the I/O region and sets isa_io/mem_base */ 2248c2ecf20Sopenharmony_ci pci_process_bridge_OF_ranges(hose, dev, primary); 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * Low level utility functions 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void tsi108_pci_int_mask(u_int irq) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci u_int irp_cfg; 2358c2ecf20Sopenharmony_ci int int_line = (irq - IRQ_PCI_INTAD_BASE); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 2388c2ecf20Sopenharmony_ci mb(); 2398c2ecf20Sopenharmony_ci irp_cfg |= (1 << int_line); /* INTx_DIR = output */ 2408c2ecf20Sopenharmony_ci irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */ 2418c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg); 2428c2ecf20Sopenharmony_ci mb(); 2438c2ecf20Sopenharmony_ci irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void tsi108_pci_int_unmask(u_int irq) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci u_int irp_cfg; 2498c2ecf20Sopenharmony_ci int int_line = (irq - IRQ_PCI_INTAD_BASE); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 2528c2ecf20Sopenharmony_ci mb(); 2538c2ecf20Sopenharmony_ci irp_cfg &= ~(1 << int_line); 2548c2ecf20Sopenharmony_ci irp_cfg |= (3 << (8 + (int_line * 2))); 2558c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg); 2568c2ecf20Sopenharmony_ci mb(); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void init_pci_source(void) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, 2628c2ecf20Sopenharmony_ci 0x0000ff00); 2638c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, 2648c2ecf20Sopenharmony_ci TSI108_PCI_IRP_ENABLE_P_INT); 2658c2ecf20Sopenharmony_ci mb(); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic inline unsigned int get_pci_source(void) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci u_int temp = 0; 2718c2ecf20Sopenharmony_ci int irq = -1; 2728c2ecf20Sopenharmony_ci int i; 2738c2ecf20Sopenharmony_ci u_int pci_irp_stat; 2748c2ecf20Sopenharmony_ci static int mask = 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Read PCI/X block interrupt status register */ 2778c2ecf20Sopenharmony_ci pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT); 2788c2ecf20Sopenharmony_ci mb(); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) { 2818c2ecf20Sopenharmony_ci /* Process Interrupt from PCI bus INTA# - INTD# lines */ 2828c2ecf20Sopenharmony_ci temp = 2838c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + 2848c2ecf20Sopenharmony_ci TSI108_PCI_IRP_INTAD) & 0xf; 2858c2ecf20Sopenharmony_ci mb(); 2868c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++, mask++) { 2878c2ecf20Sopenharmony_ci if (temp & (1 << mask % 4)) { 2888c2ecf20Sopenharmony_ci irq = IRQ_PCI_INTA + mask % 4; 2898c2ecf20Sopenharmony_ci mask++; 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Disable interrupts from PCI block */ 2958c2ecf20Sopenharmony_ci temp = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); 2968c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, 2978c2ecf20Sopenharmony_ci temp & ~TSI108_PCI_IRP_ENABLE_P_INT); 2988c2ecf20Sopenharmony_ci mb(); 2998c2ecf20Sopenharmony_ci (void)tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); 3008c2ecf20Sopenharmony_ci mb(); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci#ifdef DEBUG 3038c2ecf20Sopenharmony_ci else { 3048c2ecf20Sopenharmony_ci printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n"); 3058c2ecf20Sopenharmony_ci pci_irp_stat = 3068c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT); 3078c2ecf20Sopenharmony_ci temp = 3088c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD); 3098c2ecf20Sopenharmony_ci mb(); 3108c2ecf20Sopenharmony_ci printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp); 3118c2ecf20Sopenharmony_ci temp = 3128c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 3138c2ecf20Sopenharmony_ci mb(); 3148c2ecf20Sopenharmony_ci printk("cfg_ctl=0x%08x ", temp); 3158c2ecf20Sopenharmony_ci temp = 3168c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); 3178c2ecf20Sopenharmony_ci mb(); 3188c2ecf20Sopenharmony_ci printk("irp_enable=0x%08x\n", temp); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci#endif /* end of DEBUG */ 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return irq; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* 3278c2ecf20Sopenharmony_ci * Linux descriptor level callbacks 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void tsi108_pci_irq_unmask(struct irq_data *d) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci tsi108_pci_int_unmask(d->irq); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Enable interrupts from PCI block */ 3358c2ecf20Sopenharmony_ci tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, 3368c2ecf20Sopenharmony_ci tsi108_read_reg(TSI108_PCI_OFFSET + 3378c2ecf20Sopenharmony_ci TSI108_PCI_IRP_ENABLE) | 3388c2ecf20Sopenharmony_ci TSI108_PCI_IRP_ENABLE_P_INT); 3398c2ecf20Sopenharmony_ci mb(); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void tsi108_pci_irq_mask(struct irq_data *d) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci tsi108_pci_int_mask(d->irq); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void tsi108_pci_irq_ack(struct irq_data *d) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci tsi108_pci_int_mask(d->irq); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* 3538c2ecf20Sopenharmony_ci * Interrupt controller descriptor for cascaded PCI interrupt controller. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic struct irq_chip tsi108_pci_irq = { 3578c2ecf20Sopenharmony_ci .name = "tsi108_PCI_int", 3588c2ecf20Sopenharmony_ci .irq_mask = tsi108_pci_irq_mask, 3598c2ecf20Sopenharmony_ci .irq_ack = tsi108_pci_irq_ack, 3608c2ecf20Sopenharmony_ci .irq_unmask = tsi108_pci_irq_unmask, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int pci_irq_host_xlate(struct irq_domain *h, struct device_node *ct, 3648c2ecf20Sopenharmony_ci const u32 *intspec, unsigned int intsize, 3658c2ecf20Sopenharmony_ci irq_hw_number_t *out_hwirq, unsigned int *out_flags) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci *out_hwirq = intspec[0]; 3688c2ecf20Sopenharmony_ci *out_flags = IRQ_TYPE_LEVEL_HIGH; 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int pci_irq_host_map(struct irq_domain *h, unsigned int virq, 3738c2ecf20Sopenharmony_ci irq_hw_number_t hw) 3748c2ecf20Sopenharmony_ci{ unsigned int irq; 3758c2ecf20Sopenharmony_ci DBG("%s(%d, 0x%lx)\n", __func__, virq, hw); 3768c2ecf20Sopenharmony_ci if ((virq >= 1) && (virq <= 4)){ 3778c2ecf20Sopenharmony_ci irq = virq + IRQ_PCI_INTAD_BASE - 1; 3788c2ecf20Sopenharmony_ci irq_set_status_flags(irq, IRQ_LEVEL); 3798c2ecf20Sopenharmony_ci irq_set_chip(irq, &tsi108_pci_irq); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic const struct irq_domain_ops pci_irq_domain_ops = { 3858c2ecf20Sopenharmony_ci .map = pci_irq_host_map, 3868c2ecf20Sopenharmony_ci .xlate = pci_irq_host_xlate, 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * Exported functions 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci * The Tsi108 PCI interrupts initialization routine. 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * The INTA# - INTD# interrupts on the PCI bus are reported by the PCI block 3978c2ecf20Sopenharmony_ci * to the MPIC using single interrupt source (IRQ_TSI108_PCI). Therefore the 3988c2ecf20Sopenharmony_ci * PCI block has to be treated as a cascaded interrupt controller connected 3998c2ecf20Sopenharmony_ci * to the MPIC. 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_civoid __init tsi108_pci_int_init(struct device_node *node) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci DBG("Tsi108_pci_int_init: initializing PCI interrupts\n"); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci pci_irq_host = irq_domain_add_legacy_isa(node, &pci_irq_domain_ops, NULL); 4078c2ecf20Sopenharmony_ci if (pci_irq_host == NULL) { 4088c2ecf20Sopenharmony_ci printk(KERN_ERR "pci_irq_host: failed to allocate irq domain!\n"); 4098c2ecf20Sopenharmony_ci return; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci init_pci_source(); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_civoid tsi108_irq_cascade(struct irq_desc *desc) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 4188c2ecf20Sopenharmony_ci unsigned int cascade_irq = get_pci_source(); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (cascade_irq) 4218c2ecf20Sopenharmony_ci generic_handle_irq(cascade_irq); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci chip->irq_eoi(&desc->irq_data); 4248c2ecf20Sopenharmony_ci} 425