1168404Spjd// SPDX-License-Identifier: GPL-2.0-or-later
2168404Spjd/*
3168404Spjd * Copyright (C) 2016 Imagination Technologies
4168404Spjd * Author: Paul Burton <paul.burton@mips.com>
5168404Spjd */
6168404Spjd
7168404Spjd#define pr_fmt(fmt) "sead3: " fmt
8168404Spjd
9168404Spjd#include <linux/errno.h>
10168404Spjd#include <linux/libfdt.h>
11168404Spjd#include <linux/printk.h>
12168404Spjd#include <linux/sizes.h>
13168404Spjd
14168404Spjd#include <asm/fw/fw.h>
15168404Spjd#include <asm/io.h>
16168404Spjd#include <asm/machine.h>
17168404Spjd#include <asm/yamon-dt.h>
18168404Spjd
19168404Spjd#define SEAD_CONFIG			CKSEG1ADDR(0x1b100110)
20168404Spjd#define SEAD_CONFIG_GIC_PRESENT		BIT(1)
21168404Spjd
22219089Spjd#define MIPS_REVISION			CKSEG1ADDR(0x1fc00010)
23249195Smm#define MIPS_REVISION_MACHINE		(0xf << 4)
24254012Sdelphij#define MIPS_REVISION_MACHINE_SEAD3	(0x4 << 4)
25255750Sdelphij
26168404Spjd/*
27168404Spjd * Maximum 384MB RAM at physical address 0, preceding any I/O.
28168404Spjd */
29219089Spjdstatic struct yamon_mem_region mem_regions[] __initdata = {
30185029Spjd	/* start	size */
31168404Spjd	{ 0,		SZ_256M + SZ_128M },
32168404Spjd	{}
33168404Spjd};
34168404Spjd
35168404Spjdstatic __init bool sead3_detect(void)
36236155Smm{
37185029Spjd	uint32_t rev;
38168404Spjd
39168404Spjd	rev = __raw_readl((void *)MIPS_REVISION);
40168404Spjd	return (rev & MIPS_REVISION_MACHINE) == MIPS_REVISION_MACHINE_SEAD3;
41168404Spjd}
42168404Spjd
43168404Spjdstatic __init int append_memory(void *fdt)
44168404Spjd{
45168404Spjd	return yamon_dt_append_memory(fdt, mem_regions);
46168404Spjd}
47168404Spjd
48168404Spjdstatic __init int remove_gic(void *fdt)
49168404Spjd{
50219089Spjd	const unsigned int cpu_ehci_int = 2;
51219089Spjd	const unsigned int cpu_uart_int = 4;
52219089Spjd	const unsigned int cpu_eth_int = 6;
53219089Spjd	int gic_off, cpu_off, uart_off, eth_off, ehci_off, err;
54219089Spjd	uint32_t cfg, cpu_phandle;
55219089Spjd
56219089Spjd	/* leave the GIC node intact if a GIC is present */
57219089Spjd	cfg = __raw_readl((uint32_t *)SEAD_CONFIG);
58219089Spjd	if (cfg & SEAD_CONFIG_GIC_PRESENT)
59219089Spjd		return 0;
60219089Spjd
61219089Spjd	gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic");
62219089Spjd	if (gic_off < 0) {
63219089Spjd		pr_err("unable to find DT GIC node: %d\n", gic_off);
64219089Spjd		return gic_off;
65219089Spjd	}
66219089Spjd
67219089Spjd	err = fdt_nop_node(fdt, gic_off);
68219089Spjd	if (err) {
69219089Spjd		pr_err("unable to nop GIC node\n");
70219089Spjd		return err;
71219089Spjd	}
72219089Spjd
73219089Spjd	cpu_off = fdt_node_offset_by_compatible(fdt, -1,
74219089Spjd			"mti,cpu-interrupt-controller");
75219089Spjd	if (cpu_off < 0) {
76219089Spjd		pr_err("unable to find CPU intc node: %d\n", cpu_off);
77219089Spjd		return cpu_off;
78219089Spjd	}
79219089Spjd
80219089Spjd	cpu_phandle = fdt_get_phandle(fdt, cpu_off);
81219089Spjd	if (!cpu_phandle) {
82219089Spjd		pr_err("unable to get CPU intc phandle\n");
83219089Spjd		return -EINVAL;
84219089Spjd	}
85219089Spjd
86219089Spjd	uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a");
87219089Spjd	while (uart_off >= 0) {
88219089Spjd		err = fdt_setprop_u32(fdt, uart_off, "interrupt-parent",
89219089Spjd				      cpu_phandle);
90219089Spjd		if (err) {
91219089Spjd			pr_warn("unable to set UART interrupt-parent: %d\n",
92219089Spjd				err);
93219089Spjd			return err;
94219089Spjd		}
95219089Spjd
96219089Spjd		err = fdt_setprop_u32(fdt, uart_off, "interrupts",
97219089Spjd				      cpu_uart_int);
98219089Spjd		if (err) {
99219089Spjd			pr_err("unable to set UART interrupts property: %d\n",
100219089Spjd			       err);
101219089Spjd			return err;
102219089Spjd		}
103219089Spjd
104219089Spjd		uart_off = fdt_node_offset_by_compatible(fdt, uart_off,
105219089Spjd							 "ns16550a");
106219089Spjd	}
107219089Spjd	if (uart_off != -FDT_ERR_NOTFOUND) {
108219089Spjd		pr_err("error searching for UART DT node: %d\n", uart_off);
109236155Smm		return uart_off;
110236155Smm	}
111236155Smm
112236155Smm	eth_off = fdt_node_offset_by_compatible(fdt, -1, "smsc,lan9115");
113236155Smm	if (eth_off < 0) {
114236155Smm		pr_err("unable to find ethernet DT node: %d\n", eth_off);
115236155Smm		return eth_off;
116236155Smm	}
117236155Smm
118236155Smm	err = fdt_setprop_u32(fdt, eth_off, "interrupt-parent", cpu_phandle);
119236155Smm	if (err) {
120236155Smm		pr_err("unable to set ethernet interrupt-parent: %d\n", err);
121236155Smm		return err;
122236155Smm	}
123236155Smm
124236155Smm	err = fdt_setprop_u32(fdt, eth_off, "interrupts", cpu_eth_int);
125236155Smm	if (err) {
126236155Smm		pr_err("unable to set ethernet interrupts property: %d\n", err);
127236155Smm		return err;
128236155Smm	}
129236155Smm
130236155Smm	ehci_off = fdt_node_offset_by_compatible(fdt, -1, "generic-ehci");
131236155Smm	if (ehci_off < 0) {
132236155Smm		pr_err("unable to find EHCI DT node: %d\n", ehci_off);
133236155Smm		return ehci_off;
134236155Smm	}
135236155Smm
136236155Smm	err = fdt_setprop_u32(fdt, ehci_off, "interrupt-parent", cpu_phandle);
137236155Smm	if (err) {
138236155Smm		pr_err("unable to set EHCI interrupt-parent: %d\n", err);
139254012Sdelphij		return err;
140254012Sdelphij	}
141254012Sdelphij
142254012Sdelphij	err = fdt_setprop_u32(fdt, ehci_off, "interrupts", cpu_ehci_int);
143254012Sdelphij	if (err) {
144254012Sdelphij		pr_err("unable to set EHCI interrupts property: %d\n", err);
145254012Sdelphij		return err;
146254012Sdelphij	}
147254012Sdelphij
148254012Sdelphij	return 0;
149168404Spjd}
150236155Smm
151236155Smmstatic const struct mips_fdt_fixup sead3_fdt_fixups[] __initconst = {
152168404Spjd	{ yamon_dt_append_cmdline, "append command line" },
153209962Smm	{ append_memory, "append memory" },
154168404Spjd	{ remove_gic, "remove GIC when not present" },
155254012Sdelphij	{ yamon_dt_serial_config, "append serial configuration" },
156254012Sdelphij	{ },
157254012Sdelphij};
158254012Sdelphij
159254012Sdelphijstatic __init const void *sead3_fixup_fdt(const void *fdt,
160254012Sdelphij					  const void *match_data)
161168404Spjd{
162185029Spjd	static unsigned char fdt_buf[16 << 10] __initdata;
163185029Spjd	int err;
164249209Smm
165249209Smm	if (fdt_check_header(fdt))
166254012Sdelphij		panic("Corrupt DT");
167168404Spjd
168168404Spjd	/* if this isn't SEAD3, something went wrong */
169168404Spjd	BUG_ON(fdt_node_check_compatible(fdt, 0, "mti,sead-3"));
170168404Spjd
171168404Spjd	fw_init_cmdline();
172168404Spjd
173249195Smm	err = apply_mips_fdt_fixups(fdt_buf, sizeof(fdt_buf),
174168404Spjd				    fdt, sead3_fdt_fixups);
175168404Spjd	if (err)
176219089Spjd		panic("Unable to fixup FDT: %d", err);
177219089Spjd
178219089Spjd	return fdt_buf;
179219089Spjd}
180219089Spjd
181219089Spjdstatic __init unsigned int sead3_measure_hpt_freq(void)
182219089Spjd{
183219089Spjd	void __iomem *status_reg = (void __iomem *)0xbf000410;
184219089Spjd	unsigned int freq, orig, tick = 0;
185219089Spjd	unsigned long flags;
186168404Spjd
187168404Spjd	local_irq_save(flags);
188168404Spjd
189168404Spjd	orig = readl(status_reg) & 0x2;		      /* get original sample */
190168404Spjd	/* wait for transition */
191168404Spjd	while ((readl(status_reg) & 0x2) == orig)
192168404Spjd		;
193168404Spjd	orig = orig ^ 0x2;			      /* flip the bit */
194168404Spjd
195168404Spjd	write_c0_count(0);
196168404Spjd
197168404Spjd	/* wait 1 second (the sampling clock transitions every 10ms) */
198168404Spjd	while (tick < 100) {
199168404Spjd		/* wait for transition */
200168404Spjd		while ((readl(status_reg) & 0x2) == orig)
201168404Spjd			;
202168404Spjd		orig = orig ^ 0x2;			      /* flip the bit */
203168404Spjd		tick++;
204168404Spjd	}
205168404Spjd
206168404Spjd	freq = read_c0_count();
207168404Spjd
208249195Smm	local_irq_restore(flags);
209168404Spjd
210168404Spjd	return freq;
211168404Spjd}
212168404Spjd
213168404Spjdextern char __dtb_sead3_begin[];
214219089Spjd
215168404SpjdMIPS_MACHINE(sead3) = {
216168404Spjd	.fdt = __dtb_sead3_begin,
217168404Spjd	.detect = sead3_detect,
218168404Spjd	.fixup_fdt = sead3_fixup_fdt,
219168404Spjd	.measure_hpt_freq = sead3_measure_hpt_freq,
220168404Spjd};
221168404Spjd