162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * pcic.c: MicroSPARC-IIep PCI controller support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1998 V. Roganov and G. Raiko
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Code is derived from Ultra/PCI PSYCHO controller support, see that
862306a36Sopenharmony_ci * for author info.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Support for diverse IIep based platforms by Pete Zaitcev.
1162306a36Sopenharmony_ci * CP-1200 by Eric Brower.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/mm.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/jiffies.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <asm/swift.h> /* for cache flushing. */
2262306a36Sopenharmony_ci#include <asm/io.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/ctype.h>
2562306a36Sopenharmony_ci#include <linux/pci.h>
2662306a36Sopenharmony_ci#include <linux/time.h>
2762306a36Sopenharmony_ci#include <linux/timex.h>
2862306a36Sopenharmony_ci#include <linux/interrupt.h>
2962306a36Sopenharmony_ci#include <linux/export.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <asm/irq.h>
3262306a36Sopenharmony_ci#include <asm/oplib.h>
3362306a36Sopenharmony_ci#include <asm/prom.h>
3462306a36Sopenharmony_ci#include <asm/pcic.h>
3562306a36Sopenharmony_ci#include <asm/timex.h>
3662306a36Sopenharmony_ci#include <asm/timer.h>
3762306a36Sopenharmony_ci#include <linux/uaccess.h>
3862306a36Sopenharmony_ci#include <asm/irq_regs.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include "kernel.h"
4162306a36Sopenharmony_ci#include "irq.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * I studied different documents and many live PROMs both from 2.30
4562306a36Sopenharmony_ci * family and 3.xx versions. I came to the amazing conclusion: there is
4662306a36Sopenharmony_ci * absolutely no way to route interrupts in IIep systems relying on
4762306a36Sopenharmony_ci * information which PROM presents. We must hardcode interrupt routing
4862306a36Sopenharmony_ci * schematics. And this actually sucks.   -- zaitcev 1999/05/12
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * To find irq for a device we determine which routing map
5162306a36Sopenharmony_ci * is in effect or, in other words, on which machine we are running.
5262306a36Sopenharmony_ci * We use PROM name for this although other techniques may be used
5362306a36Sopenharmony_ci * in special cases (Gleb reports a PROMless IIep based system).
5462306a36Sopenharmony_ci * Once we know the map we take device configuration address and
5562306a36Sopenharmony_ci * find PCIC pin number where INT line goes. Then we may either program
5662306a36Sopenharmony_ci * preferred irq into the PCIC or supply the preexisting irq to the device.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cistruct pcic_ca2irq {
5962306a36Sopenharmony_ci	unsigned char busno;		/* PCI bus number */
6062306a36Sopenharmony_ci	unsigned char devfn;		/* Configuration address */
6162306a36Sopenharmony_ci	unsigned char pin;		/* PCIC external interrupt pin */
6262306a36Sopenharmony_ci	unsigned char irq;		/* Preferred IRQ (mappable in PCIC) */
6362306a36Sopenharmony_ci	unsigned int force;		/* Enforce preferred IRQ */
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct pcic_sn2list {
6762306a36Sopenharmony_ci	char *sysname;
6862306a36Sopenharmony_ci	struct pcic_ca2irq *intmap;
6962306a36Sopenharmony_ci	int mapdim;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * JavaEngine-1 apparently has different versions.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * According to communications with Sun folks, for P2 build 501-4628-03:
7662306a36Sopenharmony_ci * pin 0 - parallel, audio;
7762306a36Sopenharmony_ci * pin 1 - Ethernet;
7862306a36Sopenharmony_ci * pin 2 - su;
7962306a36Sopenharmony_ci * pin 3 - PS/2 kbd and mouse.
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * OEM manual (805-1486):
8262306a36Sopenharmony_ci * pin 0: Ethernet
8362306a36Sopenharmony_ci * pin 1: All EBus
8462306a36Sopenharmony_ci * pin 2: IGA (unused)
8562306a36Sopenharmony_ci * pin 3: Not connected
8662306a36Sopenharmony_ci * OEM manual says that 501-4628 & 501-4811 are the same thing,
8762306a36Sopenharmony_ci * only the latter has NAND flash in place.
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * So far unofficial Sun wins over the OEM manual. Poor OEMs...
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic struct pcic_ca2irq pcic_i_je1a[] = {	/* 501-4811-03 */
9262306a36Sopenharmony_ci	{ 0, 0x00, 2, 12, 0 },		/* EBus: hogs all */
9362306a36Sopenharmony_ci	{ 0, 0x01, 1,  6, 1 },		/* Happy Meal */
9462306a36Sopenharmony_ci	{ 0, 0x80, 0,  7, 0 },		/* IGA (unused) */
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* XXX JS-E entry is incomplete - PCI Slot 2 address (pin 7)? */
9862306a36Sopenharmony_cistatic struct pcic_ca2irq pcic_i_jse[] = {
9962306a36Sopenharmony_ci	{ 0, 0x00, 0, 13, 0 },		/* Ebus - serial and keyboard */
10062306a36Sopenharmony_ci	{ 0, 0x01, 1,  6, 0 },		/* hme */
10162306a36Sopenharmony_ci	{ 0, 0x08, 2,  9, 0 },		/* VGA - we hope not used :) */
10262306a36Sopenharmony_ci	{ 0, 0x10, 6,  8, 0 },		/* PCI INTA# in Slot 1 */
10362306a36Sopenharmony_ci	{ 0, 0x18, 7, 12, 0 },		/* PCI INTA# in Slot 2, shared w. RTC */
10462306a36Sopenharmony_ci	{ 0, 0x38, 4,  9, 0 },		/* All ISA devices. Read 8259. */
10562306a36Sopenharmony_ci	{ 0, 0x80, 5, 11, 0 },		/* EIDE */
10662306a36Sopenharmony_ci	/* {0,0x88, 0,0,0} - unknown device... PMU? Probably no interrupt. */
10762306a36Sopenharmony_ci	{ 0, 0xA0, 4,  9, 0 },		/* USB */
10862306a36Sopenharmony_ci	/*
10962306a36Sopenharmony_ci	 * Some pins belong to non-PCI devices, we hardcode them in drivers.
11062306a36Sopenharmony_ci	 * sun4m timers - irq 10, 14
11162306a36Sopenharmony_ci	 * PC style RTC - pin 7, irq 4 ?
11262306a36Sopenharmony_ci	 * Smart card, Parallel - pin 4 shared with USB, ISA
11362306a36Sopenharmony_ci	 * audio - pin 3, irq 5 ?
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* SPARCengine-6 was the original release name of CP1200.
11862306a36Sopenharmony_ci * The documentation differs between the two versions
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic struct pcic_ca2irq pcic_i_se6[] = {
12162306a36Sopenharmony_ci	{ 0, 0x08, 0,  2, 0 },		/* SCSI	*/
12262306a36Sopenharmony_ci	{ 0, 0x01, 1,  6, 0 },		/* HME	*/
12362306a36Sopenharmony_ci	{ 0, 0x00, 3, 13, 0 },		/* EBus	*/
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * Krups (courtesy of Varol Kaptan)
12862306a36Sopenharmony_ci * No documentation available, but it was easy to guess
12962306a36Sopenharmony_ci * because it was very similar to Espresso.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * pin 0 - kbd, mouse, serial;
13262306a36Sopenharmony_ci * pin 1 - Ethernet;
13362306a36Sopenharmony_ci * pin 2 - igs (we do not use it);
13462306a36Sopenharmony_ci * pin 3 - audio;
13562306a36Sopenharmony_ci * pin 4,5,6 - unused;
13662306a36Sopenharmony_ci * pin 7 - RTC (from P2 onwards as David B. says).
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic struct pcic_ca2irq pcic_i_jk[] = {
13962306a36Sopenharmony_ci	{ 0, 0x00, 0, 13, 0 },		/* Ebus - serial and keyboard */
14062306a36Sopenharmony_ci	{ 0, 0x01, 1,  6, 0 },		/* hme */
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * Several entries in this list may point to the same routing map
14562306a36Sopenharmony_ci * as several PROMs may be installed on the same physical board.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_ci#define SN2L_INIT(name, map)	\
14862306a36Sopenharmony_ci  { name, map, ARRAY_SIZE(map) }
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct pcic_sn2list pcic_known_sysnames[] = {
15162306a36Sopenharmony_ci	SN2L_INIT("SUNW,JavaEngine1", pcic_i_je1a),	/* JE1, PROM 2.32 */
15262306a36Sopenharmony_ci	SN2L_INIT("SUNW,JS-E", pcic_i_jse),	/* PROLL JavaStation-E */
15362306a36Sopenharmony_ci	SN2L_INIT("SUNW,SPARCengine-6", pcic_i_se6), /* SPARCengine-6/CP-1200 */
15462306a36Sopenharmony_ci	SN2L_INIT("SUNW,JS-NC", pcic_i_jk),	/* PROLL JavaStation-NC */
15562306a36Sopenharmony_ci	SN2L_INIT("SUNW,JSIIep", pcic_i_jk),	/* OBP JavaStation-NC */
15662306a36Sopenharmony_ci	{ NULL, NULL, 0 }
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/*
16062306a36Sopenharmony_ci * Only one PCIC per IIep,
16162306a36Sopenharmony_ci * and since we have no SMP IIep, only one per system.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic int pcic0_up;
16462306a36Sopenharmony_cistatic struct linux_pcic pcic0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_civoid __iomem *pcic_regs;
16762306a36Sopenharmony_cistatic volatile int pcic_speculative;
16862306a36Sopenharmony_cistatic volatile int pcic_trapped;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/* forward */
17162306a36Sopenharmony_ciunsigned int pcic_build_device_irq(struct platform_device *op,
17262306a36Sopenharmony_ci                                   unsigned int real_irq);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (((unsigned int)bus) << 16) | (((unsigned int)device_fn) << 8) | (where & ~3))
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int pcic_read_config_dword(unsigned int busno, unsigned int devfn,
17762306a36Sopenharmony_ci    int where, u32 *value)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct linux_pcic *pcic;
18062306a36Sopenharmony_ci	unsigned long flags;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	pcic = &pcic0;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	local_irq_save(flags);
18562306a36Sopenharmony_ci#if 0 /* does not fail here */
18662306a36Sopenharmony_ci	pcic_speculative = 1;
18762306a36Sopenharmony_ci	pcic_trapped = 0;
18862306a36Sopenharmony_ci#endif
18962306a36Sopenharmony_ci	writel(CONFIG_CMD(busno, devfn, where), pcic->pcic_config_space_addr);
19062306a36Sopenharmony_ci#if 0 /* does not fail here */
19162306a36Sopenharmony_ci	nop();
19262306a36Sopenharmony_ci	if (pcic_trapped) {
19362306a36Sopenharmony_ci		local_irq_restore(flags);
19462306a36Sopenharmony_ci		*value = ~0;
19562306a36Sopenharmony_ci		return 0;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci#endif
19862306a36Sopenharmony_ci	pcic_speculative = 2;
19962306a36Sopenharmony_ci	pcic_trapped = 0;
20062306a36Sopenharmony_ci	*value = readl(pcic->pcic_config_space_data + (where&4));
20162306a36Sopenharmony_ci	nop();
20262306a36Sopenharmony_ci	if (pcic_trapped) {
20362306a36Sopenharmony_ci		pcic_speculative = 0;
20462306a36Sopenharmony_ci		local_irq_restore(flags);
20562306a36Sopenharmony_ci		*value = ~0;
20662306a36Sopenharmony_ci		return 0;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	pcic_speculative = 0;
20962306a36Sopenharmony_ci	local_irq_restore(flags);
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int pcic_read_config(struct pci_bus *bus, unsigned int devfn,
21462306a36Sopenharmony_ci   int where, int size, u32 *val)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	unsigned int v;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (bus->number != 0) return -EINVAL;
21962306a36Sopenharmony_ci	switch (size) {
22062306a36Sopenharmony_ci	case 1:
22162306a36Sopenharmony_ci		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
22262306a36Sopenharmony_ci		*val = 0xff & (v >> (8*(where & 3)));
22362306a36Sopenharmony_ci		return 0;
22462306a36Sopenharmony_ci	case 2:
22562306a36Sopenharmony_ci		if (where&1) return -EINVAL;
22662306a36Sopenharmony_ci		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
22762306a36Sopenharmony_ci		*val = 0xffff & (v >> (8*(where & 3)));
22862306a36Sopenharmony_ci		return 0;
22962306a36Sopenharmony_ci	case 4:
23062306a36Sopenharmony_ci		if (where&3) return -EINVAL;
23162306a36Sopenharmony_ci		pcic_read_config_dword(bus->number, devfn, where&~3, val);
23262306a36Sopenharmony_ci		return 0;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	return -EINVAL;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int pcic_write_config_dword(unsigned int busno, unsigned int devfn,
23862306a36Sopenharmony_ci    int where, u32 value)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct linux_pcic *pcic;
24162306a36Sopenharmony_ci	unsigned long flags;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	pcic = &pcic0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	local_irq_save(flags);
24662306a36Sopenharmony_ci	writel(CONFIG_CMD(busno, devfn, where), pcic->pcic_config_space_addr);
24762306a36Sopenharmony_ci	writel(value, pcic->pcic_config_space_data + (where&4));
24862306a36Sopenharmony_ci	local_irq_restore(flags);
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int pcic_write_config(struct pci_bus *bus, unsigned int devfn,
25362306a36Sopenharmony_ci   int where, int size, u32 val)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	unsigned int v;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (bus->number != 0) return -EINVAL;
25862306a36Sopenharmony_ci	switch (size) {
25962306a36Sopenharmony_ci	case 1:
26062306a36Sopenharmony_ci		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
26162306a36Sopenharmony_ci		v = (v & ~(0xff << (8*(where&3)))) |
26262306a36Sopenharmony_ci		    ((0xff&val) << (8*(where&3)));
26362306a36Sopenharmony_ci		return pcic_write_config_dword(bus->number, devfn, where&~3, v);
26462306a36Sopenharmony_ci	case 2:
26562306a36Sopenharmony_ci		if (where&1) return -EINVAL;
26662306a36Sopenharmony_ci		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
26762306a36Sopenharmony_ci		v = (v & ~(0xffff << (8*(where&3)))) |
26862306a36Sopenharmony_ci		    ((0xffff&val) << (8*(where&3)));
26962306a36Sopenharmony_ci		return pcic_write_config_dword(bus->number, devfn, where&~3, v);
27062306a36Sopenharmony_ci	case 4:
27162306a36Sopenharmony_ci		if (where&3) return -EINVAL;
27262306a36Sopenharmony_ci		return pcic_write_config_dword(bus->number, devfn, where, val);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	return -EINVAL;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic struct pci_ops pcic_ops = {
27862306a36Sopenharmony_ci	.read =		pcic_read_config,
27962306a36Sopenharmony_ci	.write =	pcic_write_config,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/*
28362306a36Sopenharmony_ci * On sparc64 pcibios_init() calls pci_controller_probe().
28462306a36Sopenharmony_ci * We want PCIC probed little ahead so that interrupt controller
28562306a36Sopenharmony_ci * would be operational.
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_ciint __init pcic_probe(void)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct linux_pcic *pcic;
29062306a36Sopenharmony_ci	struct linux_prom_registers regs[PROMREG_MAX];
29162306a36Sopenharmony_ci	struct linux_pbm_info* pbm;
29262306a36Sopenharmony_ci	char namebuf[64];
29362306a36Sopenharmony_ci	phandle node;
29462306a36Sopenharmony_ci	int err;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (pcic0_up) {
29762306a36Sopenharmony_ci		prom_printf("PCIC: called twice!\n");
29862306a36Sopenharmony_ci		prom_halt();
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	pcic = &pcic0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	node = prom_getchild (prom_root_node);
30362306a36Sopenharmony_ci	node = prom_searchsiblings (node, "pci");
30462306a36Sopenharmony_ci	if (node == 0)
30562306a36Sopenharmony_ci		return -ENODEV;
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * Map in PCIC register set, config space, and IO base
30862306a36Sopenharmony_ci	 */
30962306a36Sopenharmony_ci	err = prom_getproperty(node, "reg", (char*)regs, sizeof(regs));
31062306a36Sopenharmony_ci	if (err == 0 || err == -1) {
31162306a36Sopenharmony_ci		prom_printf("PCIC: Error, cannot get PCIC registers "
31262306a36Sopenharmony_ci			    "from PROM.\n");
31362306a36Sopenharmony_ci		prom_halt();
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	pcic0_up = 1;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	pcic->pcic_res_regs.name = "pcic_registers";
31962306a36Sopenharmony_ci	pcic->pcic_regs = ioremap(regs[0].phys_addr, regs[0].reg_size);
32062306a36Sopenharmony_ci	if (!pcic->pcic_regs) {
32162306a36Sopenharmony_ci		prom_printf("PCIC: Error, cannot map PCIC registers.\n");
32262306a36Sopenharmony_ci		prom_halt();
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	pcic->pcic_res_io.name = "pcic_io";
32662306a36Sopenharmony_ci	if ((pcic->pcic_io = (unsigned long)
32762306a36Sopenharmony_ci	    ioremap(regs[1].phys_addr, 0x10000)) == 0) {
32862306a36Sopenharmony_ci		prom_printf("PCIC: Error, cannot map PCIC IO Base.\n");
32962306a36Sopenharmony_ci		prom_halt();
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	pcic->pcic_res_cfg_addr.name = "pcic_cfg_addr";
33362306a36Sopenharmony_ci	if ((pcic->pcic_config_space_addr =
33462306a36Sopenharmony_ci	    ioremap(regs[2].phys_addr, regs[2].reg_size * 2)) == NULL) {
33562306a36Sopenharmony_ci		prom_printf("PCIC: Error, cannot map "
33662306a36Sopenharmony_ci			    "PCI Configuration Space Address.\n");
33762306a36Sopenharmony_ci		prom_halt();
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * Docs say three least significant bits in address and data
34262306a36Sopenharmony_ci	 * must be the same. Thus, we need adjust size of data.
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	pcic->pcic_res_cfg_data.name = "pcic_cfg_data";
34562306a36Sopenharmony_ci	if ((pcic->pcic_config_space_data =
34662306a36Sopenharmony_ci	    ioremap(regs[3].phys_addr, regs[3].reg_size * 2)) == NULL) {
34762306a36Sopenharmony_ci		prom_printf("PCIC: Error, cannot map "
34862306a36Sopenharmony_ci			    "PCI Configuration Space Data.\n");
34962306a36Sopenharmony_ci		prom_halt();
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	pbm = &pcic->pbm;
35362306a36Sopenharmony_ci	pbm->prom_node = node;
35462306a36Sopenharmony_ci	prom_getstring(node, "name", namebuf, 63);  namebuf[63] = 0;
35562306a36Sopenharmony_ci	strcpy(pbm->prom_name, namebuf);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	{
35862306a36Sopenharmony_ci		extern int pcic_nmi_trap_patch[4];
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		t_nmi[0] = pcic_nmi_trap_patch[0];
36162306a36Sopenharmony_ci		t_nmi[1] = pcic_nmi_trap_patch[1];
36262306a36Sopenharmony_ci		t_nmi[2] = pcic_nmi_trap_patch[2];
36362306a36Sopenharmony_ci		t_nmi[3] = pcic_nmi_trap_patch[3];
36462306a36Sopenharmony_ci		swift_flush_dcache();
36562306a36Sopenharmony_ci		pcic_regs = pcic->pcic_regs;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	prom_getstring(prom_root_node, "name", namebuf, 63);  namebuf[63] = 0;
36962306a36Sopenharmony_ci	{
37062306a36Sopenharmony_ci		struct pcic_sn2list *p;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		for (p = pcic_known_sysnames; p->sysname != NULL; p++) {
37362306a36Sopenharmony_ci			if (strcmp(namebuf, p->sysname) == 0)
37462306a36Sopenharmony_ci				break;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci		pcic->pcic_imap = p->intmap;
37762306a36Sopenharmony_ci		pcic->pcic_imdim = p->mapdim;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	if (pcic->pcic_imap == NULL) {
38062306a36Sopenharmony_ci		/*
38162306a36Sopenharmony_ci		 * We do not panic here for the sake of embedded systems.
38262306a36Sopenharmony_ci		 */
38362306a36Sopenharmony_ci		printk("PCIC: System %s is unknown, cannot route interrupts\n",
38462306a36Sopenharmony_ci		    namebuf);
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void __init pcic_pbm_scan_bus(struct linux_pcic *pcic)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct linux_pbm_info *pbm = &pcic->pbm;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, &pcic_ops, pbm);
39562306a36Sopenharmony_ci	if (!pbm->pci_bus)
39662306a36Sopenharmony_ci		return;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci#if 0 /* deadwood transplanted from sparc64 */
39962306a36Sopenharmony_ci	pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
40062306a36Sopenharmony_ci	pci_record_assignments(pbm, pbm->pci_bus);
40162306a36Sopenharmony_ci	pci_assign_unassigned(pbm, pbm->pci_bus);
40262306a36Sopenharmony_ci	pci_fixup_irq(pbm, pbm->pci_bus);
40362306a36Sopenharmony_ci#endif
40462306a36Sopenharmony_ci	pci_bus_add_devices(pbm->pci_bus);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/*
40862306a36Sopenharmony_ci * Main entry point from the PCI subsystem.
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_cistatic int __init pcic_init(void)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct linux_pcic *pcic;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/*
41562306a36Sopenharmony_ci	 * PCIC should be initialized at start of the timer.
41662306a36Sopenharmony_ci	 * So, here we report the presence of PCIC and do some magic passes.
41762306a36Sopenharmony_ci	 */
41862306a36Sopenharmony_ci	if(!pcic0_up)
41962306a36Sopenharmony_ci		return 0;
42062306a36Sopenharmony_ci	pcic = &pcic0;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/*
42362306a36Sopenharmony_ci	 *      Switch off IOTLB translation.
42462306a36Sopenharmony_ci	 */
42562306a36Sopenharmony_ci	writeb(PCI_DVMA_CONTROL_IOTLB_DISABLE,
42662306a36Sopenharmony_ci	       pcic->pcic_regs+PCI_DVMA_CONTROL);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/*
42962306a36Sopenharmony_ci	 *      Increase mapped size for PCI memory space (DMA access).
43062306a36Sopenharmony_ci	 *      Should be done in that order (size first, address second).
43162306a36Sopenharmony_ci	 *      Why we couldn't set up 4GB and forget about it? XXX
43262306a36Sopenharmony_ci	 */
43362306a36Sopenharmony_ci	writel(0xF0000000UL, pcic->pcic_regs+PCI_SIZE_0);
43462306a36Sopenharmony_ci	writel(0+PCI_BASE_ADDRESS_SPACE_MEMORY,
43562306a36Sopenharmony_ci	       pcic->pcic_regs+PCI_BASE_ADDRESS_0);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	pcic_pbm_scan_bus(pcic);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciint pcic_present(void)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	return pcic0_up;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int pdev_to_pnode(struct linux_pbm_info *pbm, struct pci_dev *pdev)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct linux_prom_pci_registers regs[PROMREG_MAX];
45062306a36Sopenharmony_ci	int err;
45162306a36Sopenharmony_ci	phandle node = prom_getchild(pbm->prom_node);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	while(node) {
45462306a36Sopenharmony_ci		err = prom_getproperty(node, "reg",
45562306a36Sopenharmony_ci				       (char *)&regs[0], sizeof(regs));
45662306a36Sopenharmony_ci		if(err != 0 && err != -1) {
45762306a36Sopenharmony_ci			unsigned long devfn = (regs[0].which_io >> 8) & 0xff;
45862306a36Sopenharmony_ci			if(devfn == pdev->devfn)
45962306a36Sopenharmony_ci				return node;
46062306a36Sopenharmony_ci		}
46162306a36Sopenharmony_ci		node = prom_getsibling(node);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic inline struct pcidev_cookie *pci_devcookie_alloc(void)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	return kmalloc(sizeof(struct pcidev_cookie), GFP_ATOMIC);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic void pcic_map_pci_device(struct linux_pcic *pcic,
47262306a36Sopenharmony_ci    struct pci_dev *dev, int node)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	char namebuf[64];
47562306a36Sopenharmony_ci	unsigned long address;
47662306a36Sopenharmony_ci	unsigned long flags;
47762306a36Sopenharmony_ci	int j;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (node == 0 || node == -1) {
48062306a36Sopenharmony_ci		strcpy(namebuf, "???");
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		prom_getstring(node, "name", namebuf, 63); namebuf[63] = 0;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	for (j = 0; j < 6; j++) {
48662306a36Sopenharmony_ci		address = dev->resource[j].start;
48762306a36Sopenharmony_ci		if (address == 0) break;	/* are sequential */
48862306a36Sopenharmony_ci		flags = dev->resource[j].flags;
48962306a36Sopenharmony_ci		if ((flags & IORESOURCE_IO) != 0) {
49062306a36Sopenharmony_ci			if (address < 0x10000) {
49162306a36Sopenharmony_ci				/*
49262306a36Sopenharmony_ci				 * A device responds to I/O cycles on PCI.
49362306a36Sopenharmony_ci				 * We generate these cycles with memory
49462306a36Sopenharmony_ci				 * access into the fixed map (phys 0x30000000).
49562306a36Sopenharmony_ci				 *
49662306a36Sopenharmony_ci				 * Since a device driver does not want to
49762306a36Sopenharmony_ci				 * do ioremap() before accessing PC-style I/O,
49862306a36Sopenharmony_ci				 * we supply virtual, ready to access address.
49962306a36Sopenharmony_ci				 *
50062306a36Sopenharmony_ci				 * Note that request_region()
50162306a36Sopenharmony_ci				 * works for these devices.
50262306a36Sopenharmony_ci				 *
50362306a36Sopenharmony_ci				 * XXX Neat trick, but it's a *bad* idea
50462306a36Sopenharmony_ci				 * to shit into regions like that.
50562306a36Sopenharmony_ci				 * What if we want to allocate one more
50662306a36Sopenharmony_ci				 * PCI base address...
50762306a36Sopenharmony_ci				 */
50862306a36Sopenharmony_ci				dev->resource[j].start =
50962306a36Sopenharmony_ci				    pcic->pcic_io + address;
51062306a36Sopenharmony_ci				dev->resource[j].end = 1;  /* XXX */
51162306a36Sopenharmony_ci				dev->resource[j].flags =
51262306a36Sopenharmony_ci				    (flags & ~IORESOURCE_IO) | IORESOURCE_MEM;
51362306a36Sopenharmony_ci			} else {
51462306a36Sopenharmony_ci				/*
51562306a36Sopenharmony_ci				 * OOPS... PCI Spec allows this. Sun does
51662306a36Sopenharmony_ci				 * not have any devices getting above 64K
51762306a36Sopenharmony_ci				 * so it must be user with a weird I/O
51862306a36Sopenharmony_ci				 * board in a PCI slot. We must remap it
51962306a36Sopenharmony_ci				 * under 64K but it is not done yet. XXX
52062306a36Sopenharmony_ci				 */
52162306a36Sopenharmony_ci				pci_info(dev, "PCIC: Skipping I/O space at "
52262306a36Sopenharmony_ci					 "0x%lx, this will Oops if a driver "
52362306a36Sopenharmony_ci					 "attaches device '%s'\n", address,
52462306a36Sopenharmony_ci					 namebuf);
52562306a36Sopenharmony_ci			}
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void
53162306a36Sopenharmony_cipcic_fill_irq(struct linux_pcic *pcic, struct pci_dev *dev, int node)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct pcic_ca2irq *p;
53462306a36Sopenharmony_ci	unsigned int real_irq;
53562306a36Sopenharmony_ci	int i, ivec;
53662306a36Sopenharmony_ci	char namebuf[64];
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (node == 0 || node == -1) {
53962306a36Sopenharmony_ci		strcpy(namebuf, "???");
54062306a36Sopenharmony_ci	} else {
54162306a36Sopenharmony_ci		prom_getstring(node, "name", namebuf, sizeof(namebuf));
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if ((p = pcic->pcic_imap) == NULL) {
54562306a36Sopenharmony_ci		dev->irq = 0;
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci	for (i = 0; i < pcic->pcic_imdim; i++) {
54962306a36Sopenharmony_ci		if (p->busno == dev->bus->number && p->devfn == dev->devfn)
55062306a36Sopenharmony_ci			break;
55162306a36Sopenharmony_ci		p++;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci	if (i >= pcic->pcic_imdim) {
55462306a36Sopenharmony_ci		pci_info(dev, "PCIC: device %s not found in %d\n", namebuf,
55562306a36Sopenharmony_ci			 pcic->pcic_imdim);
55662306a36Sopenharmony_ci		dev->irq = 0;
55762306a36Sopenharmony_ci		return;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	i = p->pin;
56162306a36Sopenharmony_ci	if (i >= 0 && i < 4) {
56262306a36Sopenharmony_ci		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
56362306a36Sopenharmony_ci		real_irq = ivec >> (i << 2) & 0xF;
56462306a36Sopenharmony_ci	} else if (i >= 4 && i < 8) {
56562306a36Sopenharmony_ci		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
56662306a36Sopenharmony_ci		real_irq = ivec >> ((i-4) << 2) & 0xF;
56762306a36Sopenharmony_ci	} else {					/* Corrupted map */
56862306a36Sopenharmony_ci		pci_info(dev, "PCIC: BAD PIN %d\n", i); for (;;) {}
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci/* P3 */ /* printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq); */
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* real_irq means PROM did not bother to program the upper
57362306a36Sopenharmony_ci	 * half of PCIC. This happens on JS-E with PROM 3.11, for instance.
57462306a36Sopenharmony_ci	 */
57562306a36Sopenharmony_ci	if (real_irq == 0 || p->force) {
57662306a36Sopenharmony_ci		if (p->irq == 0 || p->irq >= 15) {	/* Corrupted map */
57762306a36Sopenharmony_ci			pci_info(dev, "PCIC: BAD IRQ %d\n", p->irq); for (;;) {}
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci		pci_info(dev, "PCIC: setting irq %d at pin %d\n", p->irq,
58062306a36Sopenharmony_ci			 p->pin);
58162306a36Sopenharmony_ci		real_irq = p->irq;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		i = p->pin;
58462306a36Sopenharmony_ci		if (i >= 4) {
58562306a36Sopenharmony_ci			ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
58662306a36Sopenharmony_ci			ivec &= ~(0xF << ((i - 4) << 2));
58762306a36Sopenharmony_ci			ivec |= p->irq << ((i - 4) << 2);
58862306a36Sopenharmony_ci			writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_HI);
58962306a36Sopenharmony_ci		} else {
59062306a36Sopenharmony_ci			ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
59162306a36Sopenharmony_ci			ivec &= ~(0xF << (i << 2));
59262306a36Sopenharmony_ci			ivec |= p->irq << (i << 2);
59362306a36Sopenharmony_ci			writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_LO);
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci	dev->irq = pcic_build_device_irq(NULL, real_irq);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/*
60062306a36Sopenharmony_ci * Normally called from {do_}pci_scan_bus...
60162306a36Sopenharmony_ci */
60262306a36Sopenharmony_civoid pcibios_fixup_bus(struct pci_bus *bus)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct pci_dev *dev;
60562306a36Sopenharmony_ci	struct linux_pcic *pcic;
60662306a36Sopenharmony_ci	/* struct linux_pbm_info* pbm = &pcic->pbm; */
60762306a36Sopenharmony_ci	int node;
60862306a36Sopenharmony_ci	struct pcidev_cookie *pcp;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (!pcic0_up) {
61162306a36Sopenharmony_ci		pci_info(bus, "pcibios_fixup_bus: no PCIC\n");
61262306a36Sopenharmony_ci		return;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	pcic = &pcic0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/*
61762306a36Sopenharmony_ci	 * Next crud is an equivalent of pbm = pcic_bus_to_pbm(bus);
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	if (bus->number != 0) {
62062306a36Sopenharmony_ci		pci_info(bus, "pcibios_fixup_bus: nonzero bus 0x%x\n",
62162306a36Sopenharmony_ci			 bus->number);
62262306a36Sopenharmony_ci		return;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	list_for_each_entry(dev, &bus->devices, bus_list) {
62662306a36Sopenharmony_ci		node = pdev_to_pnode(&pcic->pbm, dev);
62762306a36Sopenharmony_ci		if(node == 0)
62862306a36Sopenharmony_ci			node = -1;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		/* cookies */
63162306a36Sopenharmony_ci		pcp = pci_devcookie_alloc();
63262306a36Sopenharmony_ci		pcp->pbm = &pcic->pbm;
63362306a36Sopenharmony_ci		pcp->prom_node = of_find_node_by_phandle(node);
63462306a36Sopenharmony_ci		dev->sysdata = pcp;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		/* fixing I/O to look like memory */
63762306a36Sopenharmony_ci		if ((dev->class>>16) != PCI_BASE_CLASS_BRIDGE)
63862306a36Sopenharmony_ci			pcic_map_pci_device(pcic, dev, node);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		pcic_fill_irq(pcic, dev, node);
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ciint pcibios_enable_device(struct pci_dev *dev, int mask)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct resource *res;
64762306a36Sopenharmony_ci	u16 cmd, oldcmd;
64862306a36Sopenharmony_ci	int i;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	pci_read_config_word(dev, PCI_COMMAND, &cmd);
65162306a36Sopenharmony_ci	oldcmd = cmd;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	pci_dev_for_each_resource(dev, res, i) {
65462306a36Sopenharmony_ci		/* Only set up the requested stuff */
65562306a36Sopenharmony_ci		if (!(mask & (1<<i)))
65662306a36Sopenharmony_ci			continue;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		if (res->flags & IORESOURCE_IO)
65962306a36Sopenharmony_ci			cmd |= PCI_COMMAND_IO;
66062306a36Sopenharmony_ci		if (res->flags & IORESOURCE_MEM)
66162306a36Sopenharmony_ci			cmd |= PCI_COMMAND_MEMORY;
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (cmd != oldcmd) {
66562306a36Sopenharmony_ci		pci_info(dev, "enabling device (%04x -> %04x)\n", oldcmd, cmd);
66662306a36Sopenharmony_ci		pci_write_config_word(dev, PCI_COMMAND, cmd);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci/* Makes compiler happy */
67262306a36Sopenharmony_cistatic volatile int pcic_timer_dummy;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void pcic_clear_clock_irq(void)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/* CPU frequency is 100 MHz, timer increments every 4 CPU clocks */
68062306a36Sopenharmony_ci#define USECS_PER_JIFFY  (1000000 / HZ)
68162306a36Sopenharmony_ci#define TICK_TIMER_LIMIT ((100 * 1000000 / 4) / HZ)
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic unsigned int pcic_cycles_offset(void)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	u32 value, count;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	value = readl(pcic0.pcic_regs + PCI_SYS_COUNTER);
68862306a36Sopenharmony_ci	count = value & ~PCI_SYS_COUNTER_OVERFLOW;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (value & PCI_SYS_COUNTER_OVERFLOW)
69162306a36Sopenharmony_ci		count += TICK_TIMER_LIMIT;
69262306a36Sopenharmony_ci	/*
69362306a36Sopenharmony_ci	 * We divide all by HZ
69462306a36Sopenharmony_ci	 * to have microsecond resolution and to avoid overflow
69562306a36Sopenharmony_ci	 */
69662306a36Sopenharmony_ci	count = ((count / HZ) * USECS_PER_JIFFY) / (TICK_TIMER_LIMIT / HZ);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Coordinate with the sparc_config.clock_rate setting */
69962306a36Sopenharmony_ci	return count * 2;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_civoid __init pci_time_init(void)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct linux_pcic *pcic = &pcic0;
70562306a36Sopenharmony_ci	unsigned long v;
70662306a36Sopenharmony_ci	int timer_irq, irq;
70762306a36Sopenharmony_ci	int err;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci#ifndef CONFIG_SMP
71062306a36Sopenharmony_ci	/*
71162306a36Sopenharmony_ci	 * The clock_rate is in SBUS dimension.
71262306a36Sopenharmony_ci	 * We take into account this in pcic_cycles_offset()
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	sparc_config.clock_rate = SBUS_CLOCK_RATE / HZ;
71562306a36Sopenharmony_ci	sparc_config.features |= FEAT_L10_CLOCKEVENT;
71662306a36Sopenharmony_ci#endif
71762306a36Sopenharmony_ci	sparc_config.features |= FEAT_L10_CLOCKSOURCE;
71862306a36Sopenharmony_ci	sparc_config.get_cycles_offset = pcic_cycles_offset;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT);
72162306a36Sopenharmony_ci	/* PROM should set appropriate irq */
72262306a36Sopenharmony_ci	v = readb(pcic->pcic_regs+PCI_COUNTER_IRQ);
72362306a36Sopenharmony_ci	timer_irq = PCI_COUNTER_IRQ_SYS(v);
72462306a36Sopenharmony_ci	writel (PCI_COUNTER_IRQ_SET(timer_irq, 0),
72562306a36Sopenharmony_ci		pcic->pcic_regs+PCI_COUNTER_IRQ);
72662306a36Sopenharmony_ci	irq = pcic_build_device_irq(NULL, timer_irq);
72762306a36Sopenharmony_ci	err = request_irq(irq, timer_interrupt,
72862306a36Sopenharmony_ci			  IRQF_TIMER, "timer", NULL);
72962306a36Sopenharmony_ci	if (err) {
73062306a36Sopenharmony_ci		prom_printf("time_init: unable to attach IRQ%d\n", timer_irq);
73162306a36Sopenharmony_ci		prom_halt();
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci	local_irq_enable();
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci#if 0
73862306a36Sopenharmony_cistatic void watchdog_reset() {
73962306a36Sopenharmony_ci	writeb(0, pcic->pcic_regs+PCI_SYS_STATUS);
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci#endif
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/*
74462306a36Sopenharmony_ci * NMI
74562306a36Sopenharmony_ci */
74662306a36Sopenharmony_civoid pcic_nmi(unsigned int pend, struct pt_regs *regs)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	pend = swab32(pend);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (!pcic_speculative || (pend & PCI_SYS_INT_PENDING_PIO) == 0) {
75162306a36Sopenharmony_ci		/*
75262306a36Sopenharmony_ci		 * XXX On CP-1200 PCI #SERR may happen, we do not know
75362306a36Sopenharmony_ci		 * what to do about it yet.
75462306a36Sopenharmony_ci		 */
75562306a36Sopenharmony_ci		printk("Aiee, NMI pend 0x%x pc 0x%x spec %d, hanging\n",
75662306a36Sopenharmony_ci		    pend, (int)regs->pc, pcic_speculative);
75762306a36Sopenharmony_ci		for (;;) { }
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci	pcic_speculative = 0;
76062306a36Sopenharmony_ci	pcic_trapped = 1;
76162306a36Sopenharmony_ci	regs->pc = regs->npc;
76262306a36Sopenharmony_ci	regs->npc += 4;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic inline unsigned long get_irqmask(int irq_nr)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	return 1 << irq_nr;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic void pcic_mask_irq(struct irq_data *data)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	unsigned long mask, flags;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	mask = (unsigned long)data->chip_data;
77562306a36Sopenharmony_ci	local_irq_save(flags);
77662306a36Sopenharmony_ci	writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET);
77762306a36Sopenharmony_ci	local_irq_restore(flags);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic void pcic_unmask_irq(struct irq_data *data)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	unsigned long mask, flags;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	mask = (unsigned long)data->chip_data;
78562306a36Sopenharmony_ci	local_irq_save(flags);
78662306a36Sopenharmony_ci	writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR);
78762306a36Sopenharmony_ci	local_irq_restore(flags);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic unsigned int pcic_startup_irq(struct irq_data *data)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	irq_link(data->irq);
79362306a36Sopenharmony_ci	pcic_unmask_irq(data);
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic struct irq_chip pcic_irq = {
79862306a36Sopenharmony_ci	.name		= "pcic",
79962306a36Sopenharmony_ci	.irq_startup	= pcic_startup_irq,
80062306a36Sopenharmony_ci	.irq_mask	= pcic_mask_irq,
80162306a36Sopenharmony_ci	.irq_unmask	= pcic_unmask_irq,
80262306a36Sopenharmony_ci};
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ciunsigned int pcic_build_device_irq(struct platform_device *op,
80562306a36Sopenharmony_ci                                   unsigned int real_irq)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	unsigned int irq;
80862306a36Sopenharmony_ci	unsigned long mask;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	irq = 0;
81162306a36Sopenharmony_ci	mask = get_irqmask(real_irq);
81262306a36Sopenharmony_ci	if (mask == 0)
81362306a36Sopenharmony_ci		goto out;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	irq = irq_alloc(real_irq, real_irq);
81662306a36Sopenharmony_ci	if (irq == 0)
81762306a36Sopenharmony_ci		goto out;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	irq_set_chip_and_handler_name(irq, &pcic_irq,
82062306a36Sopenharmony_ci	                              handle_level_irq, "PCIC");
82162306a36Sopenharmony_ci	irq_set_chip_data(irq, (void *)mask);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ciout:
82462306a36Sopenharmony_ci	return irq;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic void pcic_load_profile_irq(int cpu, unsigned int limit)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_civoid __init sun4m_pci_init_IRQ(void)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	sparc_config.build_device_irq = pcic_build_device_irq;
83662306a36Sopenharmony_ci	sparc_config.clear_clock_irq  = pcic_clear_clock_irq;
83762306a36Sopenharmony_ci	sparc_config.load_profile_irq = pcic_load_profile_irq;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cisubsys_initcall(pcic_init);
841