| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * | 
|  | 3 | * (C) Copyright 2003 | 
|  | 4 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | 
|  | 5 | * | 
|  | 6 | * (C) Copyright 2004 Red Hat, Inc. | 
|  | 7 | * | 
|  | 8 | * 2005 (c) MontaVista Software, Inc. | 
|  | 9 | * Vitaly Bordug <vbordug@ru.mvista.com> | 
|  | 10 | * | 
|  | 11 | * See file CREDITS for list of people who contributed to this | 
|  | 12 | * project. | 
|  | 13 | * | 
|  | 14 | * This program is free software; you can redistribute it and/or | 
|  | 15 | * modify it under the terms of the GNU General Public License as | 
|  | 16 | * published by the Free Software Foundation; either version 2 of | 
|  | 17 | * the License, or (at your option) any later version. | 
|  | 18 | * | 
|  | 19 | * This program is distributed in the hope that it will be useful, | 
|  | 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the | 
|  | 22 | * GNU General Public License for more details. | 
|  | 23 | * | 
|  | 24 | * You should have received a copy of the GNU General Public License | 
|  | 25 | * along with this program; if not, write to the Free Software | 
|  | 26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
|  | 27 | * MA 02111-1307 USA | 
|  | 28 | */ | 
|  | 29 |  | 
|  | 30 | #include <linux/kernel.h> | 
|  | 31 | #include <linux/init.h> | 
|  | 32 | #include <linux/pci.h> | 
|  | 33 | #include <linux/slab.h> | 
|  | 34 | #include <linux/delay.h> | 
|  | 35 | #include <linux/irq.h> | 
|  | 36 | #include <linux/interrupt.h> | 
|  | 37 |  | 
|  | 38 | #include <asm/byteorder.h> | 
|  | 39 | #include <asm/io.h> | 
|  | 40 | #include <asm/irq.h> | 
|  | 41 | #include <asm/uaccess.h> | 
|  | 42 | #include <asm/machdep.h> | 
|  | 43 | #include <asm/pci-bridge.h> | 
|  | 44 | #include <asm/immap_cpm2.h> | 
|  | 45 | #include <asm/mpc8260.h> | 
|  | 46 | #include <asm/cpm2.h> | 
|  | 47 |  | 
|  | 48 | #include "m82xx_pci.h" | 
|  | 49 |  | 
|  | 50 | /* | 
|  | 51 | * Interrupt routing | 
|  | 52 | */ | 
|  | 53 |  | 
|  | 54 | static inline int | 
|  | 55 | pq2pci_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) | 
|  | 56 | { | 
|  | 57 | static char pci_irq_table[][4] = | 
|  | 58 | /* | 
|  | 59 | *	PCI IDSEL/INTPIN->INTLINE | 
|  | 60 | * 	  A      B      C      D | 
|  | 61 | */ | 
|  | 62 | { | 
|  | 63 | { PIRQA, PIRQB, PIRQC, PIRQD },	/* IDSEL 22 - PCI slot 0 */ | 
|  | 64 | { PIRQD, PIRQA, PIRQB, PIRQC },	/* IDSEL 23 - PCI slot 1 */ | 
|  | 65 | { PIRQC, PIRQD, PIRQA, PIRQB },	/* IDSEL 24 - PCI slot 2 */ | 
|  | 66 | }; | 
|  | 67 |  | 
|  | 68 | const long min_idsel = 22, max_idsel = 24, irqs_per_slot = 4; | 
|  | 69 | return PCI_IRQ_TABLE_LOOKUP; | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | static void | 
|  | 73 | pq2pci_mask_irq(unsigned int irq) | 
|  | 74 | { | 
|  | 75 | int bit = irq - NR_CPM_INTS; | 
|  | 76 |  | 
|  | 77 | *(volatile unsigned long *) PCI_INT_MASK_REG |= (1 << (31 - bit)); | 
|  | 78 | return; | 
|  | 79 | } | 
|  | 80 |  | 
|  | 81 | static void | 
|  | 82 | pq2pci_unmask_irq(unsigned int irq) | 
|  | 83 | { | 
|  | 84 | int bit = irq - NR_CPM_INTS; | 
|  | 85 |  | 
|  | 86 | *(volatile unsigned long *) PCI_INT_MASK_REG &= ~(1 << (31 - bit)); | 
|  | 87 | return; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | static void | 
|  | 91 | pq2pci_mask_and_ack(unsigned int irq) | 
|  | 92 | { | 
|  | 93 | int bit = irq - NR_CPM_INTS; | 
|  | 94 |  | 
|  | 95 | *(volatile unsigned long *) PCI_INT_MASK_REG |= (1 << (31 - bit)); | 
|  | 96 | return; | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | static void | 
|  | 100 | pq2pci_end_irq(unsigned int irq) | 
|  | 101 | { | 
|  | 102 | int bit = irq - NR_CPM_INTS; | 
|  | 103 |  | 
|  | 104 | *(volatile unsigned long *) PCI_INT_MASK_REG &= ~(1 << (31 - bit)); | 
|  | 105 | return; | 
|  | 106 | } | 
|  | 107 |  | 
|  | 108 | struct hw_interrupt_type pq2pci_ic = { | 
|  | 109 | "PQ2 PCI", | 
|  | 110 | NULL, | 
|  | 111 | NULL, | 
|  | 112 | pq2pci_unmask_irq, | 
|  | 113 | pq2pci_mask_irq, | 
|  | 114 | pq2pci_mask_and_ack, | 
|  | 115 | pq2pci_end_irq, | 
|  | 116 | 0 | 
|  | 117 | }; | 
|  | 118 |  | 
|  | 119 | static irqreturn_t | 
| Al Viro | 39e3eb7 | 2006-10-09 12:48:42 +0100 | [diff] [blame] | 120 | pq2pci_irq_demux(int irq, void *dev_id) | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 121 | { | 
|  | 122 | unsigned long stat, mask, pend; | 
|  | 123 | int bit; | 
|  | 124 |  | 
|  | 125 | for(;;) { | 
|  | 126 | stat = *(volatile unsigned long *) PCI_INT_STAT_REG; | 
|  | 127 | mask = *(volatile unsigned long *) PCI_INT_MASK_REG; | 
|  | 128 | pend = stat & ~mask & 0xf0000000; | 
|  | 129 | if (!pend) | 
|  | 130 | break; | 
|  | 131 | for (bit = 0; pend != 0; ++bit, pend <<= 1) { | 
|  | 132 | if (pend & 0x80000000) | 
| Al Viro | 39e3eb7 | 2006-10-09 12:48:42 +0100 | [diff] [blame] | 133 | __do_IRQ(NR_CPM_INTS + bit); | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 134 | } | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | return IRQ_HANDLED; | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | static struct irqaction pq2pci_irqaction = { | 
|  | 141 | .handler = pq2pci_irq_demux, | 
| Thomas Gleixner | bc59d28 | 2006-07-01 19:29:22 -0700 | [diff] [blame] | 142 | .flags 	 = IRQF_DISABLED, | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 143 | .mask	 = CPU_MASK_NONE, | 
|  | 144 | .name	 = "PQ2 PCI cascade", | 
|  | 145 | }; | 
|  | 146 |  | 
|  | 147 |  | 
|  | 148 | void | 
|  | 149 | pq2pci_init_irq(void) | 
|  | 150 | { | 
|  | 151 | int irq; | 
|  | 152 | volatile cpm2_map_t *immap = cpm2_immr; | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 153 | for (irq = NR_CPM_INTS; irq < NR_CPM_INTS + 4; irq++) | 
| Ingo Molnar | d1bef4e | 2006-06-29 02:24:36 -0700 | [diff] [blame] | 154 | irq_desc[irq].chip = &pq2pci_ic; | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 155 |  | 
|  | 156 | /* make PCI IRQ level sensitive */ | 
|  | 157 | immap->im_intctl.ic_siexr &= | 
|  | 158 | ~(1 << (14 - (PCI_INT_TO_SIU - SIU_INT_IRQ1))); | 
|  | 159 |  | 
|  | 160 | /* mask all PCI interrupts */ | 
|  | 161 | *(volatile unsigned long *) PCI_INT_MASK_REG |= 0xfff00000; | 
|  | 162 |  | 
|  | 163 | /* install the demultiplexer for the PCI cascade interrupt */ | 
|  | 164 | setup_irq(PCI_INT_TO_SIU, &pq2pci_irqaction); | 
|  | 165 | return; | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | static int | 
|  | 169 | pq2pci_exclude_device(u_char bus, u_char devfn) | 
|  | 170 | { | 
|  | 171 | return PCIBIOS_SUCCESSFUL; | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | /* PCI bus configuration registers. | 
|  | 175 | */ | 
|  | 176 | static void | 
|  | 177 | pq2ads_setup_pci(struct pci_controller *hose) | 
|  | 178 | { | 
|  | 179 | __u32 val; | 
|  | 180 | volatile cpm2_map_t *immap = cpm2_immr; | 
|  | 181 | bd_t* binfo = (bd_t*) __res; | 
|  | 182 | u32 sccr = immap->im_clkrst.car_sccr; | 
|  | 183 | uint pci_div,freq,time; | 
|  | 184 | /* PCI int lowest prio */ | 
|  | 185 | /* Each 4 bits is a device bus request	and the MS 4bits | 
|  | 186 | is highest priority */ | 
|  | 187 | /* Bus                4bit value | 
|  | 188 | ---                ---------- | 
|  | 189 | CPM high      	0b0000 | 
|  | 190 | CPM middle           0b0001 | 
|  | 191 | CPM low       	0b0010 | 
| Simon Arlott | a8de5ce | 2007-05-12 05:42:54 +1000 | [diff] [blame] | 192 | PCI request          0b0011 | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 193 | Reserved      	0b0100 | 
|  | 194 | Reserved      	0b0101 | 
|  | 195 | Internal Core     	0b0110 | 
|  | 196 | External Master 1 	0b0111 | 
|  | 197 | External Master 2 	0b1000 | 
|  | 198 | External Master 3 	0b1001 | 
|  | 199 | The rest are reserved | 
|  | 200 | */ | 
|  | 201 | immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893; | 
|  | 202 | /* park bus on core */ | 
|  | 203 | immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_CORE; | 
|  | 204 | /* | 
|  | 205 | * Set up master windows that allow the CPU to access PCI space. These | 
|  | 206 | * windows are set up using the two SIU PCIBR registers. | 
|  | 207 | */ | 
|  | 208 |  | 
|  | 209 | immap->im_memctl.memc_pcimsk0 = M82xx_PCI_PRIM_WND_SIZE; | 
|  | 210 | immap->im_memctl.memc_pcibr0  = M82xx_PCI_PRIM_WND_BASE | PCIBR_ENABLE; | 
|  | 211 |  | 
|  | 212 | #ifdef M82xx_PCI_SEC_WND_SIZE | 
|  | 213 | immap->im_memctl.memc_pcimsk1 = M82xx_PCI_SEC_WND_SIZE; | 
|  | 214 | immap->im_memctl.memc_pcibr1  = M82xx_PCI_SEC_WND_BASE | PCIBR_ENABLE; | 
|  | 215 | #endif | 
|  | 216 |  | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 217 | /* Enable PCI  */ | 
|  | 218 | immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN); | 
|  | 219 |  | 
|  | 220 | pci_div = ( (sccr & SCCR_PCI_MODCK) ? 2 : 1) * | 
|  | 221 | ( ( (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT) + 1); | 
|  | 222 | freq = (uint)((2*binfo->bi_cpmfreq)/(pci_div)); | 
| Vitaly Bordug | 42ea0d0 | 2005-12-01 00:51:15 -0800 | [diff] [blame] | 223 | time = (int)66666666/freq; | 
|  | 224 |  | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 225 | /* due to PCI Local Bus spec, some devices needs to wait such a long | 
|  | 226 | time after RST 	deassertion. More specifically, 0.508s for 66MHz & twice more for 33 */ | 
|  | 227 | printk("%s: The PCI bus is %d Mhz.\nWaiting %s after deasserting RST...\n",__FILE__,freq, | 
|  | 228 | (time==1) ? "0.5 seconds":"1 second" ); | 
|  | 229 |  | 
|  | 230 | { | 
|  | 231 | int i; | 
|  | 232 | for(i=0;i<(500*time);i++) | 
|  | 233 | udelay(1000); | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | /* setup ATU registers */ | 
|  | 237 | immap->im_pci.pci_pocmr0 = cpu_to_le32(POCMR_ENABLE | POCMR_PCI_IO | | 
|  | 238 | ((~(M82xx_PCI_IO_SIZE - 1U)) >> POTA_ADDR_SHIFT)); | 
|  | 239 | immap->im_pci.pci_potar0 = cpu_to_le32(M82xx_PCI_LOWER_IO >> POTA_ADDR_SHIFT); | 
|  | 240 | immap->im_pci.pci_pobar0 = cpu_to_le32(M82xx_PCI_IO_BASE >> POTA_ADDR_SHIFT); | 
|  | 241 |  | 
|  | 242 | /* Set-up non-prefetchable window */ | 
|  | 243 | immap->im_pci.pci_pocmr1 = cpu_to_le32(POCMR_ENABLE | ((~(M82xx_PCI_MMIO_SIZE-1U)) >> POTA_ADDR_SHIFT)); | 
|  | 244 | immap->im_pci.pci_potar1 = cpu_to_le32(M82xx_PCI_LOWER_MMIO >> POTA_ADDR_SHIFT); | 
|  | 245 | immap->im_pci.pci_pobar1 = cpu_to_le32((M82xx_PCI_LOWER_MMIO - M82xx_PCI_MMIO_OFFSET) >> POTA_ADDR_SHIFT); | 
|  | 246 |  | 
|  | 247 | /* Set-up prefetchable window */ | 
|  | 248 | immap->im_pci.pci_pocmr2 = cpu_to_le32(POCMR_ENABLE |POCMR_PREFETCH_EN | | 
|  | 249 | (~(M82xx_PCI_MEM_SIZE-1U) >> POTA_ADDR_SHIFT)); | 
|  | 250 | immap->im_pci.pci_potar2 = cpu_to_le32(M82xx_PCI_LOWER_MEM >> POTA_ADDR_SHIFT); | 
|  | 251 | immap->im_pci.pci_pobar2 = cpu_to_le32((M82xx_PCI_LOWER_MEM - M82xx_PCI_MEM_OFFSET) >> POTA_ADDR_SHIFT); | 
|  | 252 |  | 
|  | 253 | /* Inbound transactions from PCI memory space */ | 
|  | 254 | immap->im_pci.pci_picmr0 = cpu_to_le32(PICMR_ENABLE | PICMR_PREFETCH_EN | | 
|  | 255 | ((~(M82xx_PCI_SLAVE_MEM_SIZE-1U)) >> PITA_ADDR_SHIFT)); | 
|  | 256 | immap->im_pci.pci_pibar0 = cpu_to_le32(M82xx_PCI_SLAVE_MEM_BUS  >> PITA_ADDR_SHIFT); | 
|  | 257 | immap->im_pci.pci_pitar0 = cpu_to_le32(M82xx_PCI_SLAVE_MEM_LOCAL>> PITA_ADDR_SHIFT); | 
|  | 258 |  | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 259 | /* park bus on PCI */ | 
|  | 260 | immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI; | 
|  | 261 |  | 
|  | 262 | /* Enable bus mastering and inbound memory transactions */ | 
|  | 263 | early_read_config_dword(hose, hose->first_busno, 0, PCI_COMMAND, &val); | 
|  | 264 | val &= 0xffff0000; | 
|  | 265 | val |= PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER; | 
|  | 266 | early_write_config_dword(hose, hose->first_busno, 0, PCI_COMMAND, val); | 
|  | 267 |  | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 | void __init pq2_find_bridges(void) | 
|  | 271 | { | 
| Paul Mackerras | 399fe2b | 2005-10-20 20:57:05 +1000 | [diff] [blame] | 272 | extern int pci_assign_all_buses; | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 273 | struct pci_controller * hose; | 
|  | 274 | int host_bridge; | 
|  | 275 |  | 
| Becky Bruce | ab590cc | 2005-10-24 14:50:46 -0500 | [diff] [blame] | 276 | pci_assign_all_buses = 1; | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 277 |  | 
|  | 278 | hose = pcibios_alloc_controller(); | 
|  | 279 |  | 
|  | 280 | if (!hose) | 
|  | 281 | return; | 
|  | 282 |  | 
|  | 283 | ppc_md.pci_swizzle = common_swizzle; | 
|  | 284 |  | 
|  | 285 | hose->first_busno = 0; | 
|  | 286 | hose->bus_offset = 0; | 
|  | 287 | hose->last_busno = 0xff; | 
|  | 288 |  | 
| Vitaly Bordug | a6dbba7 | 2005-05-28 15:52:09 -0700 | [diff] [blame] | 289 | setup_m8260_indirect_pci(hose, | 
|  | 290 | (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr, | 
|  | 291 | (unsigned long)&cpm2_immr->im_pci.pci_cfg_data); | 
|  | 292 |  | 
|  | 293 | /* Make sure it is a supported bridge */ | 
|  | 294 | early_read_config_dword(hose, | 
|  | 295 | 0, | 
|  | 296 | PCI_DEVFN(0,0), | 
|  | 297 | PCI_VENDOR_ID, | 
|  | 298 | &host_bridge); | 
|  | 299 | switch (host_bridge) { | 
|  | 300 | case PCI_DEVICE_ID_MPC8265: | 
|  | 301 | break; | 
|  | 302 | case PCI_DEVICE_ID_MPC8272: | 
|  | 303 | break; | 
|  | 304 | default: | 
|  | 305 | printk("Attempting to use unrecognized host bridge ID" | 
|  | 306 | " 0x%08x.\n", host_bridge); | 
|  | 307 | break; | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | pq2ads_setup_pci(hose); | 
|  | 311 |  | 
|  | 312 | hose->io_space.start =	M82xx_PCI_LOWER_IO; | 
|  | 313 | hose->io_space.end = M82xx_PCI_UPPER_IO; | 
|  | 314 | hose->mem_space.start = M82xx_PCI_LOWER_MEM; | 
|  | 315 | hose->mem_space.end = M82xx_PCI_UPPER_MMIO; | 
|  | 316 | hose->pci_mem_offset = M82xx_PCI_MEM_OFFSET; | 
|  | 317 |  | 
|  | 318 | isa_io_base = | 
|  | 319 | (unsigned long) ioremap(M82xx_PCI_IO_BASE, | 
|  | 320 | M82xx_PCI_IO_SIZE); | 
|  | 321 | hose->io_base_virt = (void *) isa_io_base; | 
|  | 322 |  | 
|  | 323 | /* setup resources */ | 
|  | 324 | pci_init_resource(&hose->mem_resources[0], | 
|  | 325 | M82xx_PCI_LOWER_MEM, | 
|  | 326 | M82xx_PCI_UPPER_MEM, | 
|  | 327 | IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory"); | 
|  | 328 |  | 
|  | 329 | pci_init_resource(&hose->mem_resources[1], | 
|  | 330 | M82xx_PCI_LOWER_MMIO, | 
|  | 331 | M82xx_PCI_UPPER_MMIO, | 
|  | 332 | IORESOURCE_MEM, "PCI memory"); | 
|  | 333 |  | 
|  | 334 | pci_init_resource(&hose->io_resource, | 
|  | 335 | M82xx_PCI_LOWER_IO, | 
|  | 336 | M82xx_PCI_UPPER_IO, | 
|  | 337 | IORESOURCE_IO | 1, "PCI I/O"); | 
|  | 338 |  | 
|  | 339 | ppc_md.pci_exclude_device = pq2pci_exclude_device; | 
|  | 340 | hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); | 
|  | 341 |  | 
|  | 342 | ppc_md.pci_map_irq = pq2pci_map_irq; | 
|  | 343 | ppc_md.pcibios_fixup = NULL; | 
|  | 344 | ppc_md.pcibios_fixup_bus = NULL; | 
|  | 345 |  | 
|  | 346 | } |