162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Board setup routines for the Motorola/Emerson MVME5100.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2013 CSC Australia Pty. Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on earlier code by:
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *    Matt Porter, MontaVista Software Inc.
1062306a36Sopenharmony_ci *    Copyright 2001 MontaVista Software Inc.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Author: Stephen Chivers <schivers@csc.com>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/of_irq.h>
1662306a36Sopenharmony_ci#include <linux/of_platform.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/i8259.h>
1962306a36Sopenharmony_ci#include <asm/pci-bridge.h>
2062306a36Sopenharmony_ci#include <asm/mpic.h>
2162306a36Sopenharmony_ci#include <mm/mmu_decl.h>
2262306a36Sopenharmony_ci#include <asm/udbg.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define HAWK_MPIC_SIZE		0x00040000U
2562306a36Sopenharmony_ci#define MVME5100_PCI_MEM_OFFSET 0x00000000
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Board register addresses. */
2862306a36Sopenharmony_ci#define BOARD_STATUS_REG	0xfef88080
2962306a36Sopenharmony_ci#define BOARD_MODFAIL_REG	0xfef88090
3062306a36Sopenharmony_ci#define BOARD_MODRST_REG	0xfef880a0
3162306a36Sopenharmony_ci#define BOARD_TBEN_REG		0xfef880c0
3262306a36Sopenharmony_ci#define BOARD_SW_READ_REG	0xfef880e0
3362306a36Sopenharmony_ci#define BOARD_GEO_ADDR_REG	0xfef880e8
3462306a36Sopenharmony_ci#define BOARD_EXT_FEATURE1_REG	0xfef880f0
3562306a36Sopenharmony_ci#define BOARD_EXT_FEATURE2_REG	0xfef88100
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic phys_addr_t pci_membase;
3862306a36Sopenharmony_cistatic u_char *restart;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void mvme5100_8259_cascade(struct irq_desc *desc)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
4362306a36Sopenharmony_ci	unsigned int cascade_irq = i8259_irq();
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (cascade_irq)
4662306a36Sopenharmony_ci		generic_handle_irq(cascade_irq);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	chip->irq_eoi(&desc->irq_data);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void __init mvme5100_pic_init(void)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct mpic *mpic;
5462306a36Sopenharmony_ci	struct device_node *np;
5562306a36Sopenharmony_ci	struct device_node *cp = NULL;
5662306a36Sopenharmony_ci	unsigned int cirq;
5762306a36Sopenharmony_ci	unsigned long intack = 0;
5862306a36Sopenharmony_ci	const u32 *prop = NULL;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	np = of_find_node_by_type(NULL, "open-pic");
6162306a36Sopenharmony_ci	if (!np) {
6262306a36Sopenharmony_ci		pr_err("Could not find open-pic node\n");
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	mpic = mpic_alloc(np, pci_membase, 0, 16, 256, " OpenPIC  ");
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	BUG_ON(mpic == NULL);
6962306a36Sopenharmony_ci	of_node_put(np);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	mpic_assign_isu(mpic, 0, pci_membase + 0x10000);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mpic_init(mpic);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	cp = of_find_compatible_node(NULL, NULL, "chrp,iic");
7662306a36Sopenharmony_ci	if (cp == NULL) {
7762306a36Sopenharmony_ci		pr_warn("mvme5100_pic_init: couldn't find i8259\n");
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	cirq = irq_of_parse_and_map(cp, 0);
8262306a36Sopenharmony_ci	if (!cirq) {
8362306a36Sopenharmony_ci		pr_warn("mvme5100_pic_init: no cascade interrupt?\n");
8462306a36Sopenharmony_ci		return;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, "pci", "mpc10x-pci");
8862306a36Sopenharmony_ci	if (np) {
8962306a36Sopenharmony_ci		prop = of_get_property(np, "8259-interrupt-acknowledge", NULL);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		if (prop)
9262306a36Sopenharmony_ci			intack = prop[0];
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		of_node_put(np);
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (intack)
9862306a36Sopenharmony_ci		pr_debug("mvme5100_pic_init: PCI 8259 intack at 0x%016lx\n",
9962306a36Sopenharmony_ci		   intack);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	i8259_init(cp, intack);
10262306a36Sopenharmony_ci	of_node_put(cp);
10362306a36Sopenharmony_ci	irq_set_chained_handler(cirq, mvme5100_8259_cascade);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int __init mvme5100_add_bridge(struct device_node *dev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	const int		*bus_range;
10962306a36Sopenharmony_ci	int			len;
11062306a36Sopenharmony_ci	struct pci_controller	*hose;
11162306a36Sopenharmony_ci	unsigned short		devid;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	pr_info("Adding PCI host bridge %pOF\n", dev);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	bus_range = of_get_property(dev, "bus-range", &len);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	hose = pcibios_alloc_controller(dev);
11862306a36Sopenharmony_ci	if (hose == NULL)
11962306a36Sopenharmony_ci		return -ENOMEM;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	hose->first_busno = bus_range ? bus_range[0] : 0;
12262306a36Sopenharmony_ci	hose->last_busno = bus_range ? bus_range[1] : 0xff;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	setup_indirect_pci(hose, 0xfe000cf8, 0xfe000cfc, 0);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	pci_process_bridge_OF_ranges(hose, dev, 1);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	early_read_config_word(hose, 0, 0, PCI_DEVICE_ID, &devid);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (devid != PCI_DEVICE_ID_MOTOROLA_HAWK) {
13162306a36Sopenharmony_ci		pr_err("HAWK PHB not present?\n");
13262306a36Sopenharmony_ci		return 0;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	early_read_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (pci_membase == 0) {
13862306a36Sopenharmony_ci		pr_err("HAWK PHB mibar not correctly set?\n");
13962306a36Sopenharmony_ci		return 0;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	pr_info("mvme5100_pic_init: pci_membase: %x\n", pci_membase);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic const struct of_device_id mvme5100_of_bus_ids[] __initconst = {
14862306a36Sopenharmony_ci	{ .compatible = "hawk-bridge", },
14962306a36Sopenharmony_ci	{},
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/*
15362306a36Sopenharmony_ci * Setup the architecture
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic void __init mvme5100_setup_arch(void)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	if (ppc_md.progress)
15862306a36Sopenharmony_ci		ppc_md.progress("mvme5100_setup_arch()", 0);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	restart = ioremap(BOARD_MODRST_REG, 4);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic void __init mvme5100_setup_pci(void)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct device_node *np;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for_each_compatible_node(np, "pci", "hawk-pci")
16862306a36Sopenharmony_ci		mvme5100_add_bridge(np);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void mvme5100_show_cpuinfo(struct seq_file *m)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	seq_puts(m, "Vendor\t\t: Motorola/Emerson\n");
17462306a36Sopenharmony_ci	seq_puts(m, "Machine\t\t: MVME5100\n");
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void __noreturn mvme5100_restart(char *cmd)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	local_irq_disable();
18162306a36Sopenharmony_ci	mtmsr(mfmsr() | MSR_IP);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	out_8((u_char *) restart, 0x01);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	while (1)
18662306a36Sopenharmony_ci		;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int __init probe_of_platform_devices(void)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	of_platform_bus_probe(NULL, mvme5100_of_bus_ids, NULL);
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cimachine_device_initcall(mvme5100, probe_of_platform_devices);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cidefine_machine(mvme5100) {
19962306a36Sopenharmony_ci	.name			= "MVME5100",
20062306a36Sopenharmony_ci	.compatible		= "MVME5100",
20162306a36Sopenharmony_ci	.setup_arch		= mvme5100_setup_arch,
20262306a36Sopenharmony_ci	.discover_phbs		= mvme5100_setup_pci,
20362306a36Sopenharmony_ci	.init_IRQ		= mvme5100_pic_init,
20462306a36Sopenharmony_ci	.show_cpuinfo		= mvme5100_show_cpuinfo,
20562306a36Sopenharmony_ci	.get_irq		= mpic_get_irq,
20662306a36Sopenharmony_ci	.restart		= mvme5100_restart,
20762306a36Sopenharmony_ci	.progress		= udbg_progress,
20862306a36Sopenharmony_ci};
209