linux/arch/mips/generic/board-sead3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Imagination Technologies
   4 * Author: Paul Burton <paul.burton@mips.com>
   5 */
   6
   7#define pr_fmt(fmt) "sead3: " fmt
   8
   9#include <linux/errno.h>
  10#include <linux/libfdt.h>
  11#include <linux/printk.h>
  12#include <linux/sizes.h>
  13
  14#include <asm/fw/fw.h>
  15#include <asm/io.h>
  16#include <asm/machine.h>
  17#include <asm/yamon-dt.h>
  18
  19#define SEAD_CONFIG                     CKSEG1ADDR(0x1b100110)
  20#define SEAD_CONFIG_GIC_PRESENT         BIT(1)
  21
  22#define MIPS_REVISION                   CKSEG1ADDR(0x1fc00010)
  23#define MIPS_REVISION_MACHINE           (0xf << 4)
  24#define MIPS_REVISION_MACHINE_SEAD3     (0x4 << 4)
  25
  26/*
  27 * Maximum 384MB RAM at physical address 0, preceding any I/O.
  28 */
  29static struct yamon_mem_region mem_regions[] __initdata = {
  30        /* start        size */
  31        { 0,            SZ_256M + SZ_128M },
  32        {}
  33};
  34
  35static __init bool sead3_detect(void)
  36{
  37        uint32_t rev;
  38
  39        rev = __raw_readl((void *)MIPS_REVISION);
  40        return (rev & MIPS_REVISION_MACHINE) == MIPS_REVISION_MACHINE_SEAD3;
  41}
  42
  43static __init int append_memory(void *fdt)
  44{
  45        return yamon_dt_append_memory(fdt, mem_regions);
  46}
  47
  48static __init int remove_gic(void *fdt)
  49{
  50        const unsigned int cpu_ehci_int = 2;
  51        const unsigned int cpu_uart_int = 4;
  52        const unsigned int cpu_eth_int = 6;
  53        int gic_off, cpu_off, uart_off, eth_off, ehci_off, err;
  54        uint32_t cfg, cpu_phandle;
  55
  56        /* leave the GIC node intact if a GIC is present */
  57        cfg = __raw_readl((uint32_t *)SEAD_CONFIG);
  58        if (cfg & SEAD_CONFIG_GIC_PRESENT)
  59                return 0;
  60
  61        gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic");
  62        if (gic_off < 0) {
  63                pr_err("unable to find DT GIC node: %d\n", gic_off);
  64                return gic_off;
  65        }
  66
  67        err = fdt_nop_node(fdt, gic_off);
  68        if (err) {
  69                pr_err("unable to nop GIC node\n");
  70                return err;
  71        }
  72
  73        cpu_off = fdt_node_offset_by_compatible(fdt, -1,
  74                        "mti,cpu-interrupt-controller");
  75        if (cpu_off < 0) {
  76                pr_err("unable to find CPU intc node: %d\n", cpu_off);
  77                return cpu_off;
  78        }
  79
  80        cpu_phandle = fdt_get_phandle(fdt, cpu_off);
  81        if (!cpu_phandle) {
  82                pr_err("unable to get CPU intc phandle\n");
  83                return -EINVAL;
  84        }
  85
  86        uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a");
  87        while (uart_off >= 0) {
  88                err = fdt_setprop_u32(fdt, uart_off, "interrupt-parent",
  89                                      cpu_phandle);
  90                if (err) {
  91                        pr_warn("unable to set UART interrupt-parent: %d\n",
  92                                err);
  93                        return err;
  94                }
  95
  96                err = fdt_setprop_u32(fdt, uart_off, "interrupts",
  97                                      cpu_uart_int);
  98                if (err) {
  99                        pr_err("unable to set UART interrupts property: %d\n",
 100                               err);
 101                        return err;
 102                }
 103
 104                uart_off = fdt_node_offset_by_compatible(fdt, uart_off,
 105                                                         "ns16550a");
 106        }
 107        if (uart_off != -FDT_ERR_NOTFOUND) {
 108                pr_err("error searching for UART DT node: %d\n", uart_off);
 109                return uart_off;
 110        }
 111
 112        eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115");
 113        if (eth_off < 0) {
 114                pr_err("unable to find ethernet DT node: %d\n", eth_off);
 115                return eth_off;
 116        }
 117
 118        err = fdt_setprop_u32(fdt, eth_off, "interrupt-parent", cpu_phandle);
 119        if (err) {
 120                pr_err("unable to set ethernet interrupt-parent: %d\n", err);
 121                return err;
 122        }
 123
 124        err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int);
 125        if (err) {
 126                pr_err("unable to set ethernet interrupts property: %d\n", err);
 127                return err;
 128        }
 129
 130        ehci_off = fdt_node_offset_by_compatible(fdt, -1, "generic-ehci");
 131        if (ehci_off < 0) {
 132                pr_err("unable to find EHCI DT node: %d\n", ehci_off);
 133                return ehci_off;
 134        }
 135
 136        err = fdt_setprop_u32(fdt, ehci_off, "interrupt-parent", cpu_phandle);
 137        if (err) {
 138                pr_err("unable to set EHCI interrupt-parent: %d\n", err);
 139                return err;
 140        }
 141
 142        err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int);
 143        if (err) {
 144                pr_err("unable to set EHCI interrupts property: %d\n", err);
 145                return err;
 146        }
 147
 148        return 0;
 149}
 150
 151static const struct mips_fdt_fixup sead3_fdt_fixups[] __initconst = {
 152        { yamon_dt_append_cmdline, "append command line" },
 153        { append_memory, "append memory" },
 154        { remove_gic, "remove GIC when not present" },
 155        { yamon_dt_serial_config, "append serial configuration" },
 156        { },
 157};
 158
 159static __init const void *sead3_fixup_fdt(const void *fdt,
 160                                          const void *match_data)
 161{
 162        static unsigned char fdt_buf[16 << 10] __initdata;
 163        int err;
 164
 165        if (fdt_check_header(fdt))
 166                panic("Corrupt DT");
 167
 168        /* if this isn't SEAD3, something went wrong */
 169        BUG_ON(fdt_node_check_compatible(fdt, 0, "mti,sead-3"));
 170
 171        fw_init_cmdline();
 172
 173        err = apply_mips_fdt_fixups(fdt_buf, sizeof(fdt_buf),
 174                                    fdt, sead3_fdt_fixups);
 175        if (err)
 176                panic("Unable to fixup FDT: %d", err);
 177
 178        return fdt_buf;
 179}
 180
 181static __init unsigned int sead3_measure_hpt_freq(void)
 182{
 183        void __iomem *status_reg = (void __iomem *)0xbf000410;
 184        unsigned int freq, orig, tick = 0;
 185        unsigned long flags;
 186
 187        local_irq_save(flags);
 188
 189        orig = readl(status_reg) & 0x2;               /* get original sample */
 190        /* wait for transition */
 191        while ((readl(status_reg) & 0x2) == orig)
 192                ;
 193        orig = orig ^ 0x2;                            /* flip the bit */
 194
 195        write_c0_count(0);
 196
 197        /* wait 1 second (the sampling clock transitions every 10ms) */
 198        while (tick < 100) {
 199                /* wait for transition */
 200                while ((readl(status_reg) & 0x2) == orig)
 201                        ;
 202                orig = orig ^ 0x2;                            /* flip the bit */
 203                tick++;
 204        }
 205
 206        freq = read_c0_count();
 207
 208        local_irq_restore(flags);
 209
 210        return freq;
 211}
 212
 213extern char __dtb_sead3_begin[];
 214
 215MIPS_MACHINE(sead3) = {
 216        .fdt = __dtb_sead3_begin,
 217        .detect = sead3_detect,
 218        .fixup_fdt = sead3_fixup_fdt,
 219        .measure_hpt_freq = sead3_measure_hpt_freq,
 220};
 221