1 /* 2 * Common routines for Tundra Semiconductor TSI108 host bridge. 3 * 4 * 2004-2005 (c) Tundra Semiconductor Corp. 5 * Author: Alex Bounine (alexandreb@tundra.com) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program; if not, write to the Free Software Foundation, Inc., 59 19 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 */ 21 22 #include <linux/kernel.h> 23 #include <linux/init.h> 24 #include <linux/pci.h> 25 #include <linux/slab.h> 26 #include <linux/irq.h> 27 #include <linux/interrupt.h> 28 29 30 #include <asm/byteorder.h> 31 #include <asm/io.h> 32 #include <asm/irq.h> 33 #include <asm/uaccess.h> 34 #include <asm/machdep.h> 35 #include <asm/pci-bridge.h> 36 #include <asm/tsi108.h> 37 #include <asm/tsi108_irq.h> 38 #include <asm/prom.h> 39 40 #undef DEBUG 41 #ifdef DEBUG 42 #define DBG(x...) printk(x) 43 #else 44 #define DBG(x...) 45 #endif 46 47 #define tsi_mk_config_addr(bus, devfunc, offset) \ 48 ((((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)) + tsi108_pci_cfg_base) 49 50 u32 tsi108_pci_cfg_base; 51 u32 tsi108_csr_vir_base; 52 53 extern u32 get_vir_csrbase(void); 54 extern u32 tsi108_read_reg(u32 reg_offset); 55 extern void tsi108_write_reg(u32 reg_offset, u32 val); 56 57 int 58 tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc, 59 int offset, int len, u32 val) 60 { 61 volatile unsigned char *cfg_addr; 62 63 if (ppc_md.pci_exclude_device) 64 if (ppc_md.pci_exclude_device(bus->number, devfunc)) 65 return PCIBIOS_DEVICE_NOT_FOUND; 66 67 cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, 68 devfunc, offset) | 69 (offset & 0x03)); 70 71 #ifdef DEBUG 72 printk("PCI CFG write : "); 73 printk("%d:0x%x:0x%x ", bus->number, devfunc, offset); 74 printk("%d ADDR=0x%08x ", len, (uint) cfg_addr); 75 printk("data = 0x%08x\n", val); 76 #endif 77 78 switch (len) { 79 case 1: 80 out_8((u8 *) cfg_addr, val); 81 break; 82 case 2: 83 out_le16((u16 *) cfg_addr, val); 84 break; 85 default: 86 out_le32((u32 *) cfg_addr, val); 87 break; 88 } 89 90 return PCIBIOS_SUCCESSFUL; 91 } 92 93 void tsi108_clear_pci_error(u32 pci_cfg_base) 94 { 95 u32 err_stat, err_addr, pci_stat; 96 97 /* 98 * Quietly clear PB and PCI error flags set as result 99 * of PCI/X configuration read requests. 100 */ 101 102 /* Read PB Error Log Registers */ 103 104 err_stat = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS); 105 err_addr = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_AERR); 106 107 if (err_stat & TSI108_PB_ERRCS_ES) { 108 /* Clear error flag */ 109 tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS, 110 TSI108_PB_ERRCS_ES); 111 112 /* Clear read error reported in PB_ISR */ 113 tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ISR, 114 TSI108_PB_ISR_PBS_RD_ERR); 115 116 /* Clear PCI/X bus cfg errors if applicable */ 117 if ((err_addr & 0xFF000000) == pci_cfg_base) { 118 pci_stat = 119 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR); 120 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR, 121 pci_stat); 122 } 123 } 124 125 return; 126 } 127 128 #define __tsi108_read_pci_config(x, addr, op) \ 129 __asm__ __volatile__( \ 130 " "op" %0,0,%1\n" \ 131 "1: eieio\n" \ 132 "2:\n" \ 133 ".section .fixup,\"ax\"\n" \ 134 "3: li %0,-1\n" \ 135 " b 2b\n" \ 136 ".section __ex_table,\"a\"\n" \ 137 " .align 2\n" \ 138 " .long 1b,3b\n" \ 139 ".text" \ 140 : "=r"(x) : "r"(addr)) 141 142 int 143 tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset, 144 int len, u32 * val) 145 { 146 volatile unsigned char *cfg_addr; 147 u32 temp; 148 149 if (ppc_md.pci_exclude_device) 150 if (ppc_md.pci_exclude_device(bus->number, devfn)) 151 return PCIBIOS_DEVICE_NOT_FOUND; 152 153 cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, 154 devfn, 155 offset) | (offset & 156 0x03)); 157 158 switch (len) { 159 case 1: 160 __tsi108_read_pci_config(temp, cfg_addr, "lbzx"); 161 break; 162 case 2: 163 __tsi108_read_pci_config(temp, cfg_addr, "lhbrx"); 164 break; 165 default: 166 __tsi108_read_pci_config(temp, cfg_addr, "lwbrx"); 167 break; 168 } 169 170 *val = temp; 171 172 #ifdef DEBUG 173 if ((0xFFFFFFFF != temp) && (0xFFFF != temp) && (0xFF != temp)) { 174 printk("PCI CFG read : "); 175 printk("%d:0x%x:0x%x ", bus->number, devfn, offset); 176 printk("%d ADDR=0x%08x ", len, (uint) cfg_addr); 177 printk("data = 0x%x\n", *val); 178 } 179 #endif 180 return PCIBIOS_SUCCESSFUL; 181 } 182 183 void tsi108_clear_pci_cfg_error(void) 184 { 185 tsi108_clear_pci_error(TSI108_PCI_CFG_BASE_PHYS); 186 } 187 188 static struct pci_ops tsi108_direct_pci_ops = { 189 tsi108_direct_read_config, 190 tsi108_direct_write_config 191 }; 192 193 int __init tsi108_setup_pci(struct device_node *dev) 194 { 195 int len; 196 struct pci_controller *hose; 197 struct resource rsrc; 198 int *bus_range; 199 int primary = 0, has_address = 0; 200 201 /* PCI Config mapping */ 202 tsi108_pci_cfg_base = (u32)ioremap(TSI108_PCI_CFG_BASE_PHYS, 203 TSI108_PCI_CFG_SIZE); 204 DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__, 205 tsi108_pci_cfg_base); 206 207 /* Fetch host bridge registers address */ 208 has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); 209 210 /* Get bus range if any */ 211 bus_range = (int *)get_property(dev, "bus-range", &len); 212 if (bus_range == NULL || len < 2 * sizeof(int)) { 213 printk(KERN_WARNING "Can't get bus-range for %s, assume" 214 " bus 0\n", dev->full_name); 215 } 216 217 hose = pcibios_alloc_controller(); 218 219 if (!hose) { 220 printk("PCI Host bridge init failed\n"); 221 return -ENOMEM; 222 } 223 hose->arch_data = dev; 224 hose->set_cfg_type = 1; 225 226 hose->first_busno = bus_range ? bus_range[0] : 0; 227 hose->last_busno = bus_range ? bus_range[1] : 0xff; 228 229 (hose)->ops = &tsi108_direct_pci_ops; 230 231 printk(KERN_INFO "Found tsi108 PCI host bridge at 0x%08lx. " 232 "Firmware bus number: %d->%d\n", 233 rsrc.start, hose->first_busno, hose->last_busno); 234 235 /* Interpret the "ranges" property */ 236 /* This also maps the I/O region and sets isa_io/mem_base */ 237 pci_process_bridge_OF_ranges(hose, dev, primary); 238 return 0; 239 } 240 241 /* 242 * Low level utility functions 243 */ 244 245 static void tsi108_pci_int_mask(u_int irq) 246 { 247 u_int irp_cfg; 248 int int_line = (irq - IRQ_PCI_INTAD_BASE); 249 250 irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 251 mb(); 252 irp_cfg |= (1 << int_line); /* INTx_DIR = output */ 253 irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */ 254 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg); 255 mb(); 256 irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 257 } 258 259 static void tsi108_pci_int_unmask(u_int irq) 260 { 261 u_int irp_cfg; 262 int int_line = (irq - IRQ_PCI_INTAD_BASE); 263 264 irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 265 mb(); 266 irp_cfg &= ~(1 << int_line); 267 irp_cfg |= (3 << (8 + (int_line * 2))); 268 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg); 269 mb(); 270 } 271 272 static void init_pci_source(void) 273 { 274 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, 275 0x0000ff00); 276 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, 277 TSI108_PCI_IRP_ENABLE_P_INT); 278 mb(); 279 } 280 281 static inline int get_pci_source(void) 282 { 283 u_int temp = 0; 284 int irq = -1; 285 int i; 286 u_int pci_irp_stat; 287 static int mask = 0; 288 289 /* Read PCI/X block interrupt status register */ 290 pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT); 291 mb(); 292 293 if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) { 294 /* Process Interrupt from PCI bus INTA# - INTD# lines */ 295 temp = 296 tsi108_read_reg(TSI108_PCI_OFFSET + 297 TSI108_PCI_IRP_INTAD) & 0xf; 298 mb(); 299 for (i = 0; i < 4; i++, mask++) { 300 if (temp & (1 << mask % 4)) { 301 irq = IRQ_PCI_INTA + mask % 4; 302 mask++; 303 break; 304 } 305 } 306 307 /* Disable interrupts from PCI block */ 308 temp = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); 309 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, 310 temp & ~TSI108_PCI_IRP_ENABLE_P_INT); 311 mb(); 312 (void)tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); 313 mb(); 314 } 315 #ifdef DEBUG 316 else { 317 printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n"); 318 pci_irp_stat = 319 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT); 320 temp = 321 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD); 322 mb(); 323 printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp); 324 temp = 325 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); 326 mb(); 327 printk("cfg_ctl=0x%08x ", temp); 328 temp = 329 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); 330 mb(); 331 printk("irp_enable=0x%08x\n", temp); 332 } 333 #endif /* end of DEBUG */ 334 335 return irq; 336 } 337 338 339 /* 340 * Linux descriptor level callbacks 341 */ 342 343 static void tsi108_pci_irq_enable(u_int irq) 344 { 345 tsi108_pci_int_unmask(irq); 346 } 347 348 static void tsi108_pci_irq_disable(u_int irq) 349 { 350 tsi108_pci_int_mask(irq); 351 } 352 353 static void tsi108_pci_irq_ack(u_int irq) 354 { 355 tsi108_pci_int_mask(irq); 356 } 357 358 static void tsi108_pci_irq_end(u_int irq) 359 { 360 tsi108_pci_int_unmask(irq); 361 362 /* Enable interrupts from PCI block */ 363 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, 364 tsi108_read_reg(TSI108_PCI_OFFSET + 365 TSI108_PCI_IRP_ENABLE) | 366 TSI108_PCI_IRP_ENABLE_P_INT); 367 mb(); 368 } 369 370 /* 371 * Interrupt controller descriptor for cascaded PCI interrupt controller. 372 */ 373 374 struct hw_interrupt_type tsi108_pci_irq = { 375 .typename = "tsi108_PCI_int", 376 .enable = tsi108_pci_irq_enable, 377 .disable = tsi108_pci_irq_disable, 378 .ack = tsi108_pci_irq_ack, 379 .end = tsi108_pci_irq_end, 380 }; 381 382 /* 383 * Exported functions 384 */ 385 386 /* 387 * The Tsi108 PCI interrupts initialization routine. 388 * 389 * The INTA# - INTD# interrupts on the PCI bus are reported by the PCI block 390 * to the MPIC using single interrupt source (IRQ_TSI108_PCI). Therefore the 391 * PCI block has to be treated as a cascaded interrupt controller connected 392 * to the MPIC. 393 */ 394 395 void __init tsi108_pci_int_init(void) 396 { 397 u_int i; 398 399 DBG("Tsi108_pci_int_init: initializing PCI interrupts\n"); 400 401 for (i = 0; i < NUM_PCI_IRQS; i++) { 402 irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq; 403 irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL; 404 } 405 406 init_pci_source(); 407 } 408 409 int tsi108_irq_cascade(struct pt_regs *regs, void *unused) 410 { 411 return get_pci_source(); 412 } 413