| /* | 
 |  * | 
 |  * (C) Copyright 2003 | 
 |  * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | 
 |  * | 
 |  * (C) Copyright 2004 Red Hat, Inc. | 
 |  * | 
 |  * 2005 (c) MontaVista Software, Inc. | 
 |  * Vitaly Bordug <vbordug@ru.mvista.com> | 
 |  * | 
 |  * See file CREDITS for list of people who contributed to this | 
 |  * project. | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU General Public License as | 
 |  * published by the Free Software Foundation; either version 2 of | 
 |  * the License, or (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
 |  * MA 02111-1307 USA | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/init.h> | 
 | #include <linux/pci.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/irq.h> | 
 | #include <linux/interrupt.h> | 
 |  | 
 | #include <asm/byteorder.h> | 
 | #include <asm/io.h> | 
 | #include <asm/irq.h> | 
 | #include <asm/uaccess.h> | 
 | #include <asm/machdep.h> | 
 | #include <asm/pci-bridge.h> | 
 | #include <asm/immap_cpm2.h> | 
 | #include <asm/mpc8260.h> | 
 | #include <asm/cpm2.h> | 
 |  | 
 | #include "m82xx_pci.h" | 
 |  | 
 | /* | 
 |  * Interrupt routing | 
 |  */ | 
 |  | 
 | static inline int | 
 | pq2pci_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) | 
 | { | 
 | 	static char pci_irq_table[][4] = | 
 | 	/* | 
 | 	 *	PCI IDSEL/INTPIN->INTLINE | 
 | 	 * 	  A      B      C      D | 
 | 	 */ | 
 | 	{ | 
 | 		{ PIRQA, PIRQB, PIRQC, PIRQD },	/* IDSEL 22 - PCI slot 0 */ | 
 | 		{ PIRQD, PIRQA, PIRQB, PIRQC },	/* IDSEL 23 - PCI slot 1 */ | 
 | 		{ PIRQC, PIRQD, PIRQA, PIRQB },	/* IDSEL 24 - PCI slot 2 */ | 
 | 	}; | 
 |  | 
 | 	const long min_idsel = 22, max_idsel = 24, irqs_per_slot = 4; | 
 | 	return PCI_IRQ_TABLE_LOOKUP; | 
 | } | 
 |  | 
 | static void | 
 | pq2pci_mask_irq(unsigned int irq) | 
 | { | 
 | 	int bit = irq - NR_CPM_INTS; | 
 |  | 
 | 	*(volatile unsigned long *) PCI_INT_MASK_REG |= (1 << (31 - bit)); | 
 | 	return; | 
 | } | 
 |  | 
 | static void | 
 | pq2pci_unmask_irq(unsigned int irq) | 
 | { | 
 | 	int bit = irq - NR_CPM_INTS; | 
 |  | 
 | 	*(volatile unsigned long *) PCI_INT_MASK_REG &= ~(1 << (31 - bit)); | 
 | 	return; | 
 | } | 
 |  | 
 | static void | 
 | pq2pci_mask_and_ack(unsigned int irq) | 
 | { | 
 | 	int bit = irq - NR_CPM_INTS; | 
 |  | 
 | 	*(volatile unsigned long *) PCI_INT_MASK_REG |= (1 << (31 - bit)); | 
 | 	return; | 
 | } | 
 |  | 
 | static void | 
 | pq2pci_end_irq(unsigned int irq) | 
 | { | 
 | 	int bit = irq - NR_CPM_INTS; | 
 |  | 
 | 	*(volatile unsigned long *) PCI_INT_MASK_REG &= ~(1 << (31 - bit)); | 
 | 	return; | 
 | } | 
 |  | 
 | struct hw_interrupt_type pq2pci_ic = { | 
 | 	"PQ2 PCI", | 
 | 	NULL, | 
 | 	NULL, | 
 | 	pq2pci_unmask_irq, | 
 | 	pq2pci_mask_irq, | 
 | 	pq2pci_mask_and_ack, | 
 | 	pq2pci_end_irq, | 
 | 	0 | 
 | }; | 
 |  | 
 | static irqreturn_t | 
 | pq2pci_irq_demux(int irq, void *dev_id, struct pt_regs *regs) | 
 | { | 
 | 	unsigned long stat, mask, pend; | 
 | 	int bit; | 
 |  | 
 | 	for(;;) { | 
 | 		stat = *(volatile unsigned long *) PCI_INT_STAT_REG; | 
 | 		mask = *(volatile unsigned long *) PCI_INT_MASK_REG; | 
 | 		pend = stat & ~mask & 0xf0000000; | 
 | 		if (!pend) | 
 | 			break; | 
 | 		for (bit = 0; pend != 0; ++bit, pend <<= 1) { | 
 | 			if (pend & 0x80000000) | 
 | 				__do_IRQ(NR_CPM_INTS + bit, regs); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static struct irqaction pq2pci_irqaction = { | 
 | 	.handler = pq2pci_irq_demux, | 
 | 	.flags 	 = SA_INTERRUPT, | 
 | 	.mask	 = CPU_MASK_NONE, | 
 | 	.name	 = "PQ2 PCI cascade", | 
 | }; | 
 |  | 
 |  | 
 | void | 
 | pq2pci_init_irq(void) | 
 | { | 
 | 	int irq; | 
 | 	volatile cpm2_map_t *immap = cpm2_immr; | 
 | #if defined CONFIG_ADS8272 | 
 | 	/* configure chip select for PCI interrupt controller */ | 
 | 	immap->im_memctl.memc_br3 = PCI_INT_STAT_REG | 0x00001801; | 
 | 	immap->im_memctl.memc_or3 = 0xffff8010; | 
 | #elif defined CONFIG_PQ2FADS | 
 | 	immap->im_memctl.memc_br8 = PCI_INT_STAT_REG | 0x00001801; | 
 | 	immap->im_memctl.memc_or8 = 0xffff8010; | 
 | #endif | 
 | 	for (irq = NR_CPM_INTS; irq < NR_CPM_INTS + 4; irq++) | 
 | 		irq_desc[irq].handler = &pq2pci_ic; | 
 |  | 
 | 	/* make PCI IRQ level sensitive */ | 
 | 	immap->im_intctl.ic_siexr &= | 
 | 		~(1 << (14 - (PCI_INT_TO_SIU - SIU_INT_IRQ1))); | 
 |  | 
 | 	/* mask all PCI interrupts */ | 
 | 	*(volatile unsigned long *) PCI_INT_MASK_REG |= 0xfff00000; | 
 |  | 
 | 	/* install the demultiplexer for the PCI cascade interrupt */ | 
 | 	setup_irq(PCI_INT_TO_SIU, &pq2pci_irqaction); | 
 | 	return; | 
 | } | 
 |  | 
 | static int | 
 | pq2pci_exclude_device(u_char bus, u_char devfn) | 
 | { | 
 | 	return PCIBIOS_SUCCESSFUL; | 
 | } | 
 |  | 
 | /* PCI bus configuration registers. | 
 |  */ | 
 | static void | 
 | pq2ads_setup_pci(struct pci_controller *hose) | 
 | { | 
 | 	__u32 val; | 
 | 	volatile cpm2_map_t *immap = cpm2_immr; | 
 | 	bd_t* binfo = (bd_t*) __res; | 
 | 	u32 sccr = immap->im_clkrst.car_sccr; | 
 | 	uint pci_div,freq,time; | 
 | 		/* PCI int lowest prio */ | 
 | 	/* Each 4 bits is a device bus request	and the MS 4bits | 
 | 	 is highest priority */ | 
 | 	/* Bus                4bit value | 
 | 	   ---                ---------- | 
 | 	   CPM high      	0b0000 | 
 | 	   CPM middle           0b0001 | 
 | 	   CPM low       	0b0010 | 
 | 	   PCI reguest          0b0011 | 
 | 	   Reserved      	0b0100 | 
 | 	   Reserved      	0b0101 | 
 | 	   Internal Core     	0b0110 | 
 | 	   External Master 1 	0b0111 | 
 | 	   External Master 2 	0b1000 | 
 | 	   External Master 3 	0b1001 | 
 | 	   The rest are reserved | 
 | 	 */ | 
 | 	immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893; | 
 | 	/* park bus on core */ | 
 | 	immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_CORE; | 
 | 	/* | 
 | 	 * Set up master windows that allow the CPU to access PCI space. These | 
 | 	 * windows are set up using the two SIU PCIBR registers. | 
 | 	 */ | 
 |  | 
 | 	immap->im_memctl.memc_pcimsk0 = M82xx_PCI_PRIM_WND_SIZE; | 
 | 	immap->im_memctl.memc_pcibr0  = M82xx_PCI_PRIM_WND_BASE | PCIBR_ENABLE; | 
 |  | 
 | #ifdef M82xx_PCI_SEC_WND_SIZE | 
 | 	immap->im_memctl.memc_pcimsk1 = M82xx_PCI_SEC_WND_SIZE; | 
 | 	immap->im_memctl.memc_pcibr1  = M82xx_PCI_SEC_WND_BASE | PCIBR_ENABLE; | 
 | #endif | 
 |  | 
 | #if defined CONFIG_ADS8272 | 
 | 	immap->im_siu_conf.siu_82xx.sc_siumcr = | 
 | 		(immap->im_siu_conf.siu_82xx.sc_siumcr & | 
 | 		~(SIUMCR_BBD | SIUMCR_ESE | SIUMCR_PBSE | | 
 | 		SIUMCR_CDIS | SIUMCR_DPPC11 | SIUMCR_L2CPC11 | | 
 | 		SIUMCR_LBPC11 | SIUMCR_APPC11 | | 
 | 		SIUMCR_CS10PC11 | SIUMCR_BCTLC11 | SIUMCR_MMR11)) | | 
 | 		SIUMCR_DPPC11 | SIUMCR_L2CPC01 | SIUMCR_LBPC00 | | 
 | 		SIUMCR_APPC10 | SIUMCR_CS10PC00 | | 
 | 		SIUMCR_BCTLC00 | SIUMCR_MMR11 ; | 
 |  | 
 | #elif defined CONFIG_PQ2FADS | 
 | 	/* | 
 | 	 * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]), | 
 | 	 * and local bus for PCI (SIUMCR [LBPC]). | 
 | 	 */ | 
 | 	immap->im_siu_conf.siu_82xx.sc_siumcr = (immap->im_siu_conf.siu_82xx.sc_siumcr & | 
 | 				~(SIUMCR_L2CPC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11 | SIUMCR_APPC11) | | 
 | 				SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 | SIUMCR_APPC10); | 
 | #endif | 
 | 	/* Enable PCI  */ | 
 | 	immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN); | 
 |  | 
 | 	pci_div = ( (sccr & SCCR_PCI_MODCK) ? 2 : 1) * | 
 | 			( ( (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT) + 1); | 
 | 	freq = (uint)((2*binfo->bi_cpmfreq)/(pci_div)); | 
 | 	time = (int)66666666/freq; | 
 |  | 
 | 	/* due to PCI Local Bus spec, some devices needs to wait such a long | 
 | 	time after RST 	deassertion. More specifically, 0.508s for 66MHz & twice more for 33 */ | 
 | 	printk("%s: The PCI bus is %d Mhz.\nWaiting %s after deasserting RST...\n",__FILE__,freq, | 
 | 	(time==1) ? "0.5 seconds":"1 second" ); | 
 |  | 
 | 	{ | 
 | 		int i; | 
 | 		for(i=0;i<(500*time);i++) | 
 | 			udelay(1000); | 
 | 	} | 
 |  | 
 | 	/* setup ATU registers */ | 
 | 	immap->im_pci.pci_pocmr0 = cpu_to_le32(POCMR_ENABLE | POCMR_PCI_IO | | 
 | 				((~(M82xx_PCI_IO_SIZE - 1U)) >> POTA_ADDR_SHIFT)); | 
 | 	immap->im_pci.pci_potar0 = cpu_to_le32(M82xx_PCI_LOWER_IO >> POTA_ADDR_SHIFT); | 
 | 	immap->im_pci.pci_pobar0 = cpu_to_le32(M82xx_PCI_IO_BASE >> POTA_ADDR_SHIFT); | 
 |  | 
 | 	/* Set-up non-prefetchable window */ | 
 | 	immap->im_pci.pci_pocmr1 = cpu_to_le32(POCMR_ENABLE | ((~(M82xx_PCI_MMIO_SIZE-1U)) >> POTA_ADDR_SHIFT)); | 
 | 	immap->im_pci.pci_potar1 = cpu_to_le32(M82xx_PCI_LOWER_MMIO >> POTA_ADDR_SHIFT); | 
 | 	immap->im_pci.pci_pobar1 = cpu_to_le32((M82xx_PCI_LOWER_MMIO - M82xx_PCI_MMIO_OFFSET) >> POTA_ADDR_SHIFT); | 
 |  | 
 | 	/* Set-up prefetchable window */ | 
 | 	immap->im_pci.pci_pocmr2 = cpu_to_le32(POCMR_ENABLE |POCMR_PREFETCH_EN | | 
 | 		(~(M82xx_PCI_MEM_SIZE-1U) >> POTA_ADDR_SHIFT)); | 
 | 	immap->im_pci.pci_potar2 = cpu_to_le32(M82xx_PCI_LOWER_MEM >> POTA_ADDR_SHIFT); | 
 | 	immap->im_pci.pci_pobar2 = cpu_to_le32((M82xx_PCI_LOWER_MEM - M82xx_PCI_MEM_OFFSET) >> POTA_ADDR_SHIFT); | 
 |  | 
 |  	/* Inbound transactions from PCI memory space */ | 
 | 	immap->im_pci.pci_picmr0 = cpu_to_le32(PICMR_ENABLE | PICMR_PREFETCH_EN | | 
 | 					((~(M82xx_PCI_SLAVE_MEM_SIZE-1U)) >> PITA_ADDR_SHIFT)); | 
 | 	immap->im_pci.pci_pibar0 = cpu_to_le32(M82xx_PCI_SLAVE_MEM_BUS  >> PITA_ADDR_SHIFT); | 
 | 	immap->im_pci.pci_pitar0 = cpu_to_le32(M82xx_PCI_SLAVE_MEM_LOCAL>> PITA_ADDR_SHIFT); | 
 |  | 
 | #if defined CONFIG_ADS8272 | 
 | 	/* PCI int highest prio */ | 
 | 	immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x01236745; | 
 | #elif defined CONFIG_PQ2FADS | 
 | 	immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567; | 
 | #endif | 
 | 	/* park bus on PCI */ | 
 | 	immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI; | 
 |  | 
 | 	/* Enable bus mastering and inbound memory transactions */ | 
 | 	early_read_config_dword(hose, hose->first_busno, 0, PCI_COMMAND, &val); | 
 | 	val &= 0xffff0000; | 
 | 	val |= PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER; | 
 | 	early_write_config_dword(hose, hose->first_busno, 0, PCI_COMMAND, val); | 
 |  | 
 | } | 
 |  | 
 | void __init pq2_find_bridges(void) | 
 | { | 
 | 	extern int pci_assign_all_buses; | 
 | 	struct pci_controller * hose; | 
 | 	int host_bridge; | 
 |  | 
 | 	pci_assign_all_buses = 1; | 
 |  | 
 | 	hose = pcibios_alloc_controller(); | 
 |  | 
 | 	if (!hose) | 
 | 		return; | 
 |  | 
 | 	ppc_md.pci_swizzle = common_swizzle; | 
 |  | 
 | 	hose->first_busno = 0; | 
 | 	hose->bus_offset = 0; | 
 | 	hose->last_busno = 0xff; | 
 |  | 
 | #ifdef CONFIG_ADS8272 | 
 | 	hose->set_cfg_type = 1; | 
 | #endif | 
 |  | 
 | 	setup_m8260_indirect_pci(hose, | 
 | 				 (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr, | 
 | 				 (unsigned long)&cpm2_immr->im_pci.pci_cfg_data); | 
 |  | 
 | 	/* Make sure it is a supported bridge */ | 
 | 	early_read_config_dword(hose, | 
 | 				0, | 
 | 				PCI_DEVFN(0,0), | 
 | 				PCI_VENDOR_ID, | 
 | 				&host_bridge); | 
 | 	switch (host_bridge) { | 
 | 		case PCI_DEVICE_ID_MPC8265: | 
 | 			break; | 
 | 		case PCI_DEVICE_ID_MPC8272: | 
 | 			break; | 
 | 		default: | 
 | 			printk("Attempting to use unrecognized host bridge ID" | 
 | 				" 0x%08x.\n", host_bridge); | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	pq2ads_setup_pci(hose); | 
 |  | 
 | 	hose->io_space.start =	M82xx_PCI_LOWER_IO; | 
 | 	hose->io_space.end = M82xx_PCI_UPPER_IO; | 
 | 	hose->mem_space.start = M82xx_PCI_LOWER_MEM; | 
 | 	hose->mem_space.end = M82xx_PCI_UPPER_MMIO; | 
 | 	hose->pci_mem_offset = M82xx_PCI_MEM_OFFSET; | 
 |  | 
 | 	isa_io_base = | 
 | 	(unsigned long) ioremap(M82xx_PCI_IO_BASE, | 
 | 					M82xx_PCI_IO_SIZE); | 
 | 	hose->io_base_virt = (void *) isa_io_base; | 
 |  | 
 | 	/* setup resources */ | 
 | 	pci_init_resource(&hose->mem_resources[0], | 
 | 			M82xx_PCI_LOWER_MEM, | 
 | 			M82xx_PCI_UPPER_MEM, | 
 | 			IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory"); | 
 |  | 
 | 	pci_init_resource(&hose->mem_resources[1], | 
 | 			M82xx_PCI_LOWER_MMIO, | 
 | 			M82xx_PCI_UPPER_MMIO, | 
 | 			IORESOURCE_MEM, "PCI memory"); | 
 |  | 
 | 	pci_init_resource(&hose->io_resource, | 
 | 			M82xx_PCI_LOWER_IO, | 
 | 			M82xx_PCI_UPPER_IO, | 
 | 			IORESOURCE_IO | 1, "PCI I/O"); | 
 |  | 
 | 	ppc_md.pci_exclude_device = pq2pci_exclude_device; | 
 | 	hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); | 
 |  | 
 | 	ppc_md.pci_map_irq = pq2pci_map_irq; | 
 | 	ppc_md.pcibios_fixup = NULL; | 
 | 	ppc_md.pcibios_fixup_bus = NULL; | 
 |  | 
 | } |