1/*	$NetBSD: kirkwood.c,v 1.11 2021/08/30 00:04:30 rin Exp $	*/
2/*
3 * Copyright (c) 2010 KIYOHARA Takashi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: kirkwood.c,v 1.11 2021/08/30 00:04:30 rin Exp $");
30
31#define _INTR_PRIVATE
32
33#include "mvsocgpp.h"
34
35#include <sys/param.h>
36#include <sys/bus.h>
37
38#include <machine/intr.h>
39
40#include <arm/pic/picvar.h>
41#include <arm/pic/picvar.h>
42
43#include <arm/marvell/mvsocreg.h>
44#include <arm/marvell/mvsocvar.h>
45#include <arm/marvell/kirkwoodreg.h>
46
47#include <dev/marvell/marvellreg.h>
48
49
50static void kirkwood_intr_init(void);
51
52static void kirkwood_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
53static void kirkwood_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
54static void kirkwood_pic_establish_irq(struct pic_softc *, struct intrsource *);
55static void kirkwood_pic_source_name(struct pic_softc *, int, char *, size_t);
56
57static int kirkwood_find_pending_irqs(void);
58
59static void kirkwood_getclks(vaddr_t);
60static int kirkwood_clkgating(struct marvell_attach_args *);
61
62static const char * const sources[64] = {
63    "MainHighSum(0)",  "Bridge(1)",       "Host2CPU DB(2)",  "CPU2Host DB(3)",
64    "Reserved_4(4)",   "Xor0Chan0(5)",    "Xor0Chan1(6)",    "Xor1Chan0(7)",
65    "Xor1Chan1(8)",    "PEX0INT(9)",      "Reserved(10)",    "GbE0Sum(11)",
66    "GbE0Rx(12)",      "GbE0Tx(13)",      "GbE0Misc(14)",    "GbE1Sum(15)",
67    "GbE1Rx(16)",      "GbE1Tx(17)",      "GbE1Misc(18)",    "USB0Cnt(19)",
68    "Reserved(20)",    "Sata(21)",        "SecurityInt(22)", "SPIInt(23)",
69    "AudioINT(24)",    "Reserved(25)",    "TS0Int(26)",      "Reserved(27)",
70    "SDIOInt(28)",     "TWSI(29)",        "AVBInt(30)",      "TDMInt(31)",
71
72    "Reserved(32)",    "Uart0Int(33)",    "Uart1Int(34)",    "GPIOLo7_0(35)",
73    "GPIOLo8_15(36)",  "GPIOLo16_23(37)", "GPIOLo24_31(38)", "GPIOHi7_0(39)",
74    "GPIOHi8_15(40)",  "GPIOHi16_23(41)", "XOR0Err(42)",     "XOR1Err(43)",
75    "PEX0Err(44)",     "Reserved(45)",    "GbE0Err(46)",     "GbE1Err(47)",
76    "USBErr(48)",      "SecurityErr(49)", "AudioErr(50)",    "Reserved(51)",
77    "Reserved(52)",    "RTCInt(53)",      "Reserved(54)",    "Reserved(55)",
78    "Reserved(56)",    "Reserved(57)",    "Reserved(58)",    "Reserved(59)",
79    "Reserved(60)",    "Reserved(61)",    "Reserved(62)",    "Reserved(63)"
80};
81
82static struct pic_ops kirkwood_picops = {
83	.pic_unblock_irqs = kirkwood_pic_unblock_irqs,
84	.pic_block_irqs = kirkwood_pic_block_irqs,
85	.pic_establish_irq = kirkwood_pic_establish_irq,
86	.pic_source_name = kirkwood_pic_source_name,
87};
88static struct pic_softc kirkwood_pic = {
89	.pic_ops = &kirkwood_picops,
90	.pic_maxsources = 64,
91	.pic_name = "kirkwood",
92};
93
94static struct {
95	bus_size_t offset;
96	uint32_t bits;
97} clkgatings[]= {
98	{ KIRKWOOD_GBE0_BASE,	(1 << 0) },
99	{ MVSOC_PEX_BASE,	(1 << 2) },
100	{ KIRKWOOD_USB_BASE,	(1 << 3) },
101	{ KIRKWOOD_SDIO_BASE,	(1 << 4) },
102	{ KIRKWOOD_MTS_BASE,	(1 << 5) },
103#if 0
104	{ Dunit, (1 << 6) },	/* SDRAM Unit Clock */
105	{ Runit, (1 << 7) },	/* Runit Clock */
106#endif
107	{ KIRKWOOD_IDMAC_BASE,	(1 << 8) | (1 << 16) },
108	{ KIRKWOOD_AUDIO_BASE,	(1 << 9) },
109	{ KIRKWOOD_SATAHC_BASE, (1 << 14) | (1 << 15) },
110	{ KIRKWOOD_CESA_BASE,	(1 << 17) },
111	{ KIRKWOOD_GBE1_BASE,	(1 << 19) },
112	{ KIRKWOOD_TDM_BASE,	(1 << 20) },
113};
114
115
116/*
117 * kirkwood_bootstrap:
118 *
119 *	Initialize the rest of the Kirkwood dependencies, making it
120 *	ready to handle interrupts from devices.
121 */
122void
123kirkwood_bootstrap(vaddr_t iobase)
124{
125
126	/* disable all interrupts */
127	write_mlmbreg(KIRKWOOD_MLMB_MIRQIMLR, 0);
128	write_mlmbreg(KIRKWOOD_MLMB_MIRQIMHR, 0);
129
130	/* disable all bridge interrupts */
131	write_mlmbreg(MVSOC_MLMB_MLMBIMR, 0);
132
133	mvsoc_intr_init = kirkwood_intr_init;
134
135#if NMVSOCGPP > 0
136	switch (mvsoc_model()) {
137	case MARVELL_KIRKWOOD_88F6180: gpp_npins = 30; break;
138	case MARVELL_KIRKWOOD_88F6192: gpp_npins = 36; break;
139	case MARVELL_KIRKWOOD_88F6281: gpp_npins = 50; break;
140	case MARVELL_KIRKWOOD_88F6282: gpp_npins = 50; break;
141	}
142	gpp_irqbase = 96;	/* Main Low(32) + High(32) + Bridge(32) */
143#endif
144
145	kirkwood_getclks(iobase);
146	mvsoc_clkgating = kirkwood_clkgating;
147}
148
149static void
150kirkwood_intr_init(void)
151{
152	extern struct pic_softc mvsoc_bridge_pic;
153	void *ih __diagused;
154
155	pic_add(&kirkwood_pic, 0);
156
157	pic_add(&mvsoc_bridge_pic, 64);
158	ih = intr_establish(KIRKWOOD_IRQ_BRIDGE, IPL_HIGH, IST_LEVEL_HIGH,
159	    pic_handle_intr, &mvsoc_bridge_pic);
160	KASSERT(ih != NULL);
161
162	find_pending_irqs = kirkwood_find_pending_irqs;
163}
164
165/* ARGSUSED */
166static void
167kirkwood_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
168			  uint32_t irq_mask)
169{
170	const size_t reg = KIRKWOOD_MLMB_MIRQIMLR
171	   + irqbase * (KIRKWOOD_MLMB_MIRQIMHR - KIRKWOOD_MLMB_MIRQIMLR) / 32;
172
173	KASSERT(irqbase < 64);
174	write_mlmbreg(reg, read_mlmbreg(reg) | irq_mask);
175}
176
177/* ARGSUSED */
178static void
179kirkwood_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
180			uint32_t irq_mask)
181{
182	const size_t reg = KIRKWOOD_MLMB_MIRQIMLR
183	   + irqbase * (KIRKWOOD_MLMB_MIRQIMHR - KIRKWOOD_MLMB_MIRQIMLR) / 32;
184
185	KASSERT(irqbase < 64);
186	write_mlmbreg(reg, read_mlmbreg(reg) & ~irq_mask);
187}
188
189/* ARGSUSED */
190static void
191kirkwood_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
192{
193	/* Nothing */
194}
195
196static void
197kirkwood_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
198{
199
200	strlcpy(buf, sources[pic->pic_irqbase + irq], len);
201}
202
203/*
204 * Called with interrupts disabled
205 */
206static int
207kirkwood_find_pending_irqs(void)
208{
209	int ipl = 0;
210
211	uint32_t causelow = read_mlmbreg(KIRKWOOD_MLMB_MICLR);
212	uint32_t pendinglow = read_mlmbreg(KIRKWOOD_MLMB_MIRQIMLR);
213
214	pendinglow &= causelow;
215	if (pendinglow != 0)
216		ipl |= pic_mark_pending_sources(&kirkwood_pic, 0, pendinglow);
217
218	if ((causelow & (1 << KIRKWOOD_IRQ_HIGH)) == (1 << KIRKWOOD_IRQ_HIGH)) {
219		uint32_t causehigh = read_mlmbreg(KIRKWOOD_MLMB_MICHR);
220		uint32_t pendinghigh = read_mlmbreg(KIRKWOOD_MLMB_MIRQIMHR);
221		pendinghigh &= causehigh;
222		ipl |= pic_mark_pending_sources(&kirkwood_pic, 32, pendinghigh);
223	}
224
225	return ipl;
226}
227
228/*
229 * Clock functions
230 */
231
232static void
233kirkwood_getclks(vaddr_t iobase)
234{
235	uint32_t reg;
236	uint16_t model;
237
238#define MHz	* 1000 * 1000
239
240	model = mvsoc_model();
241	if (model == MARVELL_KIRKWOOD_88F6281 ||
242	    model == MARVELL_KIRKWOOD_88F6282)
243		mvTclk = 200 MHz;
244	else		/* 166MHz */
245		mvTclk = 166666667;
246
247	reg = le32toh(*(volatile uint32_t *)(iobase + KIRKWOOD_MPP_BASE +
248	    KIRKWOOD_MPP_SAMPLE_AT_RESET));
249	if (model == MARVELL_KIRKWOOD_88F6180) {
250		switch (reg & 0x0000001c) {
251		case 0x00000014: mvPclk =  600 MHz; break;
252		case 0x00000018: mvPclk =  800 MHz; break;
253		default:
254			panic("unknown mvPclk\n");
255		}
256		mvSysclk = 200 MHz;
257	} else {
258		switch (reg & 0x0040001a) {
259		case 0x00000002: mvPclk =  400 MHz; break;
260		case 0x00000008: mvPclk =  600 MHz; break;
261		case 0x00400008: mvPclk =  800 MHz; break;
262		case 0x0040000a: mvPclk = 1000 MHz; break;
263		case 0x00000012: mvPclk = 1200 MHz; break;
264		case 0x00000018: mvPclk = 1500 MHz; break;
265		case 0x0000001a: mvPclk = 1600 MHz; break;
266		case 0x00400018: mvPclk = 1800 MHz; break;
267		case 0x0040001a: mvPclk = 2000 MHz; break;
268		default:
269			panic("unknown mvPclk\n");
270		}
271
272		switch (reg & 0x000001e0) {
273		case 0x00000000: mvSysclk = mvPclk * 1 / 1; break;
274		case 0x00000040: mvSysclk = mvPclk * 1 / 2; break;
275		case 0x00000060: mvSysclk = mvPclk * 2 / 5; break;
276		case 0x00000080: mvSysclk = mvPclk * 1 / 3; break;
277		case 0x000000c0: mvSysclk = mvPclk * 1 / 4; break;
278		case 0x000000e0: mvSysclk = mvPclk * 2 / 9; break;
279		case 0x00000100: mvSysclk = mvPclk * 1 / 5; break;
280		case 0x00000120: mvSysclk = mvPclk * 1 / 6; break;
281		default:
282			panic("unknown mvSysclk\n");
283		}
284	}
285
286#undef MHz
287
288}
289
290static int
291kirkwood_clkgating(struct marvell_attach_args *mva)
292{
293	uint32_t val;
294	int i;
295
296	for (i = 0; i < __arraycount(clkgatings); i++) {
297		if (clkgatings[i].offset == mva->mva_offset) {
298			val = read_mlmbreg(MVSOC_MLMB_CLKGATING);
299			if ((val & clkgatings[i].bits) == clkgatings[i].bits)
300				/* Clock enabled */
301				return 0;
302			return 1;
303		}
304	}
305	/* Clock Gating not support */
306	return 0;
307}
308