root/arch/parisc/kernel/setup.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *    Initial setup-routines for HP 9000 based hardware.
 *
 *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
 *    Modifications for PA-RISC (C) 1999 Helge Deller <deller@gmx.de>
 *    Modifications copyright 1999 SuSE GmbH (Philipp Rumpf)
 *    Modifications copyright 2000 Martin K. Petersen <mkp@mkp.net>
 *    Modifications copyright 2000 Philipp Rumpf <prumpf@tux.org>
 *    Modifications copyright 2001 Ryan Bradetich <rbradetich@uswest.net>
 *
 *    Initial PA-RISC Version: 04-23-1999 by Helge Deller
 */

#include <linux/kernel.h>
#include <linux/initrd.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/seq_file.h>
#define PCI_DEBUG
#include <linux/pci.h>
#undef PCI_DEBUG
#include <linux/proc_fs.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/start_kernel.h>

#include <asm/cacheflush.h>
#include <asm/processor.h>
#include <asm/sections.h>
#include <asm/pdc.h>
#include <asm/led.h>
#include <asm/pdc_chassis.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/unwind.h>
#include <asm/smp.h>

static char __initdata command_line[COMMAND_LINE_SIZE];

static void __init setup_cmdline(char **cmdline_p)
{
        extern unsigned int boot_args[];
        char *p;

        *cmdline_p = command_line;

        /* boot_args[0] is free-mem start, boot_args[1] is ptr to command line */
        if (boot_args[0] < 64)
                return; /* return if called from hpux boot loader */

        /* Collect stuff passed in from the boot loader */
        strscpy(boot_command_line, (char *)__va(boot_args[1]),
                COMMAND_LINE_SIZE);

        /* autodetect console type (if not done by palo yet) */
        p = boot_command_line;
        if (!str_has_prefix(p, "console=") && !strstr(p, " console=")) {
                strlcat(p, " console=", COMMAND_LINE_SIZE);
                if (PAGE0->mem_cons.cl_class == CL_DUPLEX)
                        strlcat(p, "ttyS0", COMMAND_LINE_SIZE);
                else
                        strlcat(p, "tty0", COMMAND_LINE_SIZE);
        }

        /* default to use early console */
        if (!strstr(p, "earlycon"))
                strlcat(p, " earlycon=pdc", COMMAND_LINE_SIZE);

#ifdef CONFIG_BLK_DEV_INITRD
        /* did palo pass us a ramdisk? */
        if (boot_args[2] != 0) {
                initrd_start = (unsigned long)__va(boot_args[2]);
                initrd_end = (unsigned long)__va(boot_args[3]);
        }
#endif

        strscpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
}

#ifdef CONFIG_PA11
static void __init dma_ops_init(void)
{
        switch (boot_cpu_data.cpu_type) {
        case pcx:
                /*
                 * We've got way too many dependencies on 1.1 semantics
                 * to support 1.0 boxes at this point.
                 */
                panic(  "PA-RISC Linux currently only supports machines that conform to\n"
                        "the PA-RISC 1.1 or 2.0 architecture specification.\n");

        case pcxl2:
        default:
                break;
        }
}
#endif

void __init setup_arch(char **cmdline_p)
{
        unwind_init();

        init_per_cpu(smp_processor_id());       /* Set Modes & Enable FP */

#ifdef CONFIG_64BIT
        printk(KERN_INFO "The 64-bit Kernel has started...\n");
#else
        printk(KERN_INFO "The 32-bit Kernel has started...\n");
#endif

        printk(KERN_INFO "Kernel default page size is %d KB. Huge pages ",
                (int)(PAGE_SIZE / 1024));
#ifdef CONFIG_HUGETLB_PAGE
        printk(KERN_CONT "enabled with %d MB physical and %d MB virtual size",
                 1 << (REAL_HPAGE_SHIFT - 20), 1 << (HPAGE_SHIFT - 20));
#else
        printk(KERN_CONT "disabled");
#endif
        printk(KERN_CONT ".\n");

#ifdef CONFIG_64BIT
        if(parisc_narrow_firmware) {
                printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n");
        }
#endif
        setup_pdc();
        setup_cmdline(cmdline_p);
        collect_boot_cpu_data();
        do_memory_inventory();  /* probe for physical memory */
        parisc_cache_init();
        paging_init();

#ifdef CONFIG_PA11
        dma_ops_init();
#endif

        clear_sched_clock_stable();
}

/*
 * Display CPU info for all CPUs.
 */
static void *
c_start (struct seq_file *m, loff_t *pos)
{
        /* Looks like the caller will call repeatedly until we return
         * 0, signaling EOF perhaps.  This could be used to sequence
         * through CPUs for example.  Since we print all cpu info in our
         * show_cpuinfo() disregarding 'pos' (which I assume is 'v' above)
         * we only allow for one "position".  */
        return ((long)*pos < 1) ? (void *)1 : NULL;
}

static void *
c_next (struct seq_file *m, void *v, loff_t *pos)
{
        ++*pos;
        return c_start(m, pos);
}

static void
c_stop (struct seq_file *m, void *v)
{
}

const struct seq_operations cpuinfo_op = {
        .start  = c_start,
        .next   = c_next,
        .stop   = c_stop,
        .show   = show_cpuinfo
};

static struct resource central_bus = {
        .name   = "Central Bus",
        .start  = F_EXTEND(0xfff80000),
        .end    = F_EXTEND(0xfffaffff),
        .flags  = IORESOURCE_MEM,
};

static struct resource local_broadcast = {
        .name   = "Local Broadcast",
        .start  = F_EXTEND(0xfffb0000),
        .end    = F_EXTEND(0xfffdffff),
        .flags  = IORESOURCE_MEM,
};

static struct resource global_broadcast = {
        .name   = "Global Broadcast",
        .start  = F_EXTEND(0xfffe0000),
        .end    = F_EXTEND(0xffffffff),
        .flags  = IORESOURCE_MEM,
};

static int __init parisc_init_resources(void)
{
        int result;

        result = request_resource(&iomem_resource, &central_bus);
        if (result < 0) {
                printk(KERN_ERR 
                       "%s: failed to claim %s address space!\n", 
                       __FILE__, central_bus.name);
                return result;
        }

        result = request_resource(&iomem_resource, &local_broadcast);
        if (result < 0) {
                printk(KERN_ERR 
                       "%s: failed to claim %s address space!\n",
                       __FILE__, local_broadcast.name);
                return result;
        }

        result = request_resource(&iomem_resource, &global_broadcast);
        if (result < 0) {
                printk(KERN_ERR 
                       "%s: failed to claim %s address space!\n", 
                       __FILE__, global_broadcast.name);
                return result;
        }

        return 0;
}

static int __init parisc_init(void)
{
        u32 osid = (OS_ID_LINUX << 16);

        parisc_init_resources();
        do_device_inventory();                  /* probe for hardware */

        parisc_pdc_chassis_init();
        
        /* set up a new led state on systems shipped LED State panel */
        pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BSTART);

        /* tell PDC we're Linux. Nevermind failure. */
        pdc_stable_write(0x40, &osid, sizeof(osid));
        
        /* start with known state */
        flush_cache_all_local();
        flush_tlb_all_local(NULL);

        processor_init();
#ifdef CONFIG_SMP
        pr_info("CPU(s): %d out of %d %s at %d.%06d MHz online\n",
                num_online_cpus(), num_present_cpus(),
#else
        pr_info("CPU(s): 1 x %s at %d.%06d MHz\n",
#endif
                        boot_cpu_data.cpu_name,
                        boot_cpu_data.cpu_hz / 1000000,
                        boot_cpu_data.cpu_hz % 1000000  );

#if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
        /* Don't serialize TLB flushes if we run on one CPU only. */
        if (num_online_cpus() == 1)
                pa_serialize_tlb_flushes = 0;
#endif

        apply_alternatives_all();
        parisc_setup_cache_timing();
        return 0;
}
arch_initcall(parisc_init);

void __init start_parisc(void)
{
        int ret, cpunum;
        struct pdc_coproc_cfg coproc_cfg;

        /*
         * Check if initial kernel page mapping is sufficient.
         * Print warning if not, because we may access kernel functions and
         * variables which can't be reached yet through the initial mappings.
         * Note that the panic() and printk() functions are not functional
         * yet, so we need to use direct iodc() firmware calls instead.
         */
        const char warn1[] = "CRITICAL: Kernel may crash because "
                             "KERNEL_INITIAL_ORDER is too small.\n";
        if (__pa((unsigned long) &_end) >= KERNEL_INITIAL_SIZE)
                pdc_iodc_print(warn1, sizeof(warn1) - 1);

        /* check QEMU/SeaBIOS marker in PAGE0 */
        running_on_qemu = (memcmp(&PAGE0->pad0, "SeaBIOS", 8) == 0);

        cpunum = smp_processor_id();

        init_cpu_topology();

        set_firmware_width_unlocked();

        ret = pdc_coproc_cfg_unlocked(&coproc_cfg);
        if (ret >= 0 && coproc_cfg.ccr_functional) {
                mtctl(coproc_cfg.ccr_functional, 10);

                per_cpu(cpu_data, cpunum).fp_rev = coproc_cfg.revision;
                per_cpu(cpu_data, cpunum).fp_model = coproc_cfg.model;

                asm volatile ("fstd     %fr0,8(%sp)");
        } else {
                panic("must have an fpu to boot linux");
        }

        early_trap_init(); /* initialize checksum of fault_vector */

        start_kernel();
        // not reached
}