root/arch/powerpc/platforms/embedded6xx/mvme5100.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Board setup routines for the Motorola/Emerson MVME5100.
 *
 * Copyright 2013 CSC Australia Pty. Ltd.
 *
 * Based on earlier code by:
 *
 *    Matt Porter, MontaVista Software Inc.
 *    Copyright 2001 MontaVista Software Inc.
 *
 * Author: Stephen Chivers <schivers@csc.com>
 */

#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/seq_file.h>

#include <asm/i8259.h>
#include <asm/pci-bridge.h>
#include <asm/mpic.h>
#include <mm/mmu_decl.h>
#include <asm/udbg.h>

#define HAWK_MPIC_SIZE          0x00040000U
#define MVME5100_PCI_MEM_OFFSET 0x00000000

/* Board register addresses. */
#define BOARD_STATUS_REG        0xfef88080
#define BOARD_MODFAIL_REG       0xfef88090
#define BOARD_MODRST_REG        0xfef880a0
#define BOARD_TBEN_REG          0xfef880c0
#define BOARD_SW_READ_REG       0xfef880e0
#define BOARD_GEO_ADDR_REG      0xfef880e8
#define BOARD_EXT_FEATURE1_REG  0xfef880f0
#define BOARD_EXT_FEATURE2_REG  0xfef88100

static phys_addr_t pci_membase;
static u_char *restart;

static void mvme5100_8259_cascade(struct irq_desc *desc)
{
        struct irq_chip *chip = irq_desc_get_chip(desc);
        unsigned int cascade_irq = i8259_irq();

        if (cascade_irq)
                generic_handle_irq(cascade_irq);

        chip->irq_eoi(&desc->irq_data);
}

static void __init mvme5100_pic_init(void)
{
        struct mpic *mpic;
        struct device_node *np;
        struct device_node *cp = NULL;
        unsigned int cirq;
        unsigned long intack = 0;
        const u32 *prop = NULL;

        np = of_find_node_by_type(NULL, "open-pic");
        if (!np) {
                pr_err("Could not find open-pic node\n");
                return;
        }

        mpic = mpic_alloc(np, pci_membase, 0, 16, 256, " OpenPIC  ");

        BUG_ON(mpic == NULL);
        of_node_put(np);

        mpic_assign_isu(mpic, 0, pci_membase + 0x10000);

        mpic_init(mpic);

        cp = of_find_compatible_node(NULL, NULL, "chrp,iic");
        if (cp == NULL) {
                pr_warn("mvme5100_pic_init: couldn't find i8259\n");
                return;
        }

        cirq = irq_of_parse_and_map(cp, 0);
        if (!cirq) {
                pr_warn("mvme5100_pic_init: no cascade interrupt?\n");
                return;
        }

        np = of_find_compatible_node(NULL, "pci", "mpc10x-pci");
        if (np) {
                prop = of_get_property(np, "8259-interrupt-acknowledge", NULL);

                if (prop)
                        intack = prop[0];

                of_node_put(np);
        }

        if (intack)
                pr_debug("mvme5100_pic_init: PCI 8259 intack at 0x%016lx\n",
                   intack);

        i8259_init(cp, intack);
        of_node_put(cp);
        irq_set_chained_handler(cirq, mvme5100_8259_cascade);
}

static int __init mvme5100_add_bridge(struct device_node *dev)
{
        const int               *bus_range;
        int                     len;
        struct pci_controller   *hose;
        unsigned short          devid;

        pr_info("Adding PCI host bridge %pOF\n", dev);

        bus_range = of_get_property(dev, "bus-range", &len);

        hose = pcibios_alloc_controller(dev);
        if (hose == NULL)
                return -ENOMEM;

        hose->first_busno = bus_range ? bus_range[0] : 0;
        hose->last_busno = bus_range ? bus_range[1] : 0xff;

        setup_indirect_pci(hose, 0xfe000cf8, 0xfe000cfc, 0);

        pci_process_bridge_OF_ranges(hose, dev, 1);

        early_read_config_word(hose, 0, 0, PCI_DEVICE_ID, &devid);

        if (devid != PCI_DEVICE_ID_MOTOROLA_HAWK) {
                pr_err("HAWK PHB not present?\n");
                return 0;
        }

        early_read_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);

        if (pci_membase == 0) {
                pr_err("HAWK PHB mibar not correctly set?\n");
                return 0;
        }

        pr_info("mvme5100_pic_init: pci_membase: %x\n", pci_membase);

        return 0;
}

static const struct of_device_id mvme5100_of_bus_ids[] __initconst = {
        { .compatible = "hawk-bridge", },
        {},
};

/*
 * Setup the architecture
 */
static void __init mvme5100_setup_arch(void)
{
        if (ppc_md.progress)
                ppc_md.progress("mvme5100_setup_arch()", 0);

        restart = ioremap(BOARD_MODRST_REG, 4);
}

static void __init mvme5100_setup_pci(void)
{
        struct device_node *np;

        for_each_compatible_node(np, "pci", "hawk-pci")
                mvme5100_add_bridge(np);
}

static void mvme5100_show_cpuinfo(struct seq_file *m)
{
        seq_puts(m, "Vendor\t\t: Motorola/Emerson\n");
        seq_puts(m, "Machine\t\t: MVME5100\n");
}

static void __noreturn mvme5100_restart(char *cmd)
{

        local_irq_disable();
        mtmsr(mfmsr() | MSR_IP);

        out_8((u_char *) restart, 0x01);

        while (1)
                ;
}

static int __init probe_of_platform_devices(void)
{

        of_platform_bus_probe(NULL, mvme5100_of_bus_ids, NULL);
        return 0;
}

machine_device_initcall(mvme5100, probe_of_platform_devices);

define_machine(mvme5100) {
        .name                   = "MVME5100",
        .compatible             = "MVME5100",
        .setup_arch             = mvme5100_setup_arch,
        .discover_phbs          = mvme5100_setup_pci,
        .init_IRQ               = mvme5100_pic_init,
        .show_cpuinfo           = mvme5100_show_cpuinfo,
        .get_irq                = mpic_get_irq,
        .restart                = mvme5100_restart,
        .progress               = udbg_progress,
};