1/*	$NetBSD: mv78xx0.c,v 1.3 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: mv78xx0.c,v 1.3 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#include <sys/cpu.h>
38
39#include <machine/intr.h>
40
41#include <arm/pic/picvar.h>
42#include <arm/pic/picvar.h>
43
44#include <arm/marvell/mvsocreg.h>
45#include <arm/marvell/mvsocvar.h>
46#include <arm/marvell/mv78xx0reg.h>
47
48#include <dev/marvell/marvellreg.h>
49
50#define MV78XX0_ICI_MICR(g)	(MV78XX0_ICI_MICLR + ((g) << 2))
51#define MV78XX0_ICI_IRQIMR(g)	(MV78XX0_ICI_IRQIMLR + ((g) << 2))
52
53static void mv78xx0_intr_init(void);
54
55static void mv78xx0_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
56static void mv78xx0_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
57static void mv78xx0_pic_establish_irq(struct pic_softc *, struct intrsource *);
58static void mv78xx0_pic_source_name(struct pic_softc *, int, char *, size_t);
59
60static int mv78xx0_find_pending_irqs(void);
61
62static void mv78xx0_getclks(vaddr_t);
63
64static const char * const sources[64] = {
65    "ErrSum(0)",       "SPI(1)",          "TWSI0(2)",        "TWSI1(3)",
66    "IDMA0(4)",        "IDMA1(5)",        "IDMA2(6)",        "IDMA3(7)",
67    "Timer0(8)",       "Timer1(9)",       "Timer2(10)",      "Timer3(11)",
68    "UART0(12)",       "UART1(13)",       "UART2(14)",       "UART3(15)",
69    "USB0(16)",        "USB1(17)",        "USB2(18)",        "Crypto(19)",
70    "Reserved(20)",    "Reserved(21)",    "XOR0(22)",        "XOR1(23)",
71    "Reserved(24)",    "Reserved(25)",    "SATA(26)",        "TDMI_INT(27)",
72    "Reserved(28)",    "Reserved(29)",    "Reserved(30)",    "Reserved(31)"
73
74    "PEX00INTA(32)",   "PEX01INTA(33)",   "PEX02INTA(34)",   "PEX03INTA(35)"
75    "PEX10INTA(36)",   "PEX11INTA(37)",   "PEX12INTA(38)",   "PEX13INTA(39)"
76    "GE00Sum(40)",     "GE00Rx(41)",      "GE00Tx(42)",      "GE00Misc(43)"
77    "GE01Sum(44)",     "GE01Rx(45)",      "GE01Tx(46)",      "GE01Misc(47)"
78    "GE10Sum(48)",     "GE10Rx(49)",      "GE10Tx(50)",      "GE10Misc(51)"
79    "GE11Sum(52)",     "GE11Rx(53)",      "GE11Tx(54)",      "GE11Misc(55)"
80    "GPIO0_7(56)",     "GPIO8_15(57)",    "GPIO16_23(58)",   "GPIO24_31(59)"
81    "DB_INT(60)",      "DB_OUT(61)",      "Reserved(62)",    "Reserved(63)"
82};
83
84static struct pic_ops mv78xx0_picops = {
85	.pic_unblock_irqs = mv78xx0_pic_unblock_irqs,
86	.pic_block_irqs = mv78xx0_pic_block_irqs,
87	.pic_establish_irq = mv78xx0_pic_establish_irq,
88	.pic_source_name = mv78xx0_pic_source_name,
89};
90static struct pic_softc mv78xx0_pic = {
91	.pic_ops = &mv78xx0_picops,
92	.pic_maxsources = 64,
93	.pic_name = "mv78xx0_pic",
94};
95
96
97/*
98 * mv78xx0_bootstrap:
99 *
100 *	Initialize the rest of the Discovery Innovation dependencies, making it
101 *	ready to handle interrupts from devices.
102 */
103void
104mv78xx0_bootstrap(vaddr_t iobase)
105{
106
107	/* disable all interrupts */
108	write_mlmbreg(MV78XX0_ICI_IRQIMER, 0);
109	write_mlmbreg(MV78XX0_ICI_IRQIMLR, 0);
110	write_mlmbreg(MV78XX0_ICI_IRQIMHR, 0);
111
112	/* disable all bridge interrupts */
113	write_mlmbreg(MVSOC_MLMB_MLMBIMR, 0);
114
115	mvsoc_intr_init = mv78xx0_intr_init;
116
117#if NMVSOCGPP > 0
118	gpp_npins = 32;
119	gpp_irqbase = 64;	/* Main Low(32) + High(32) */
120#endif
121
122	mv78xx0_getclks(iobase);
123}
124
125static void
126mv78xx0_intr_init(void)
127{
128
129	pic_add(&mv78xx0_pic, 0);
130
131	find_pending_irqs = mv78xx0_find_pending_irqs;
132}
133
134/* ARGSUSED */
135static void
136mv78xx0_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
137			 uint32_t irq_mask)
138{
139	const size_t group = irqbase / 32;
140
141	write_mlmbreg(MV78XX0_ICI_IRQIMR(group),
142	    read_mlmbreg(MV78XX0_ICI_IRQIMR(group)) | irq_mask);
143}
144
145/* ARGSUSED */
146static void
147mv78xx0_pic_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
148{
149	const size_t group = irqbase / 32;
150
151	write_mlmbreg(MV78XX0_ICI_IRQIMR(group),
152	    read_mlmbreg(MV78XX0_ICI_IRQIMR(group)) & ~irq_mask);
153}
154
155/* ARGSUSED */
156static void
157mv78xx0_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
158{
159	uint32_t mlmbim;
160
161	/* Also enable MbusL-Mbus Bridge Interrupt Mask, if irq is TimerX. */
162	if (is->is_irq == MV78XX0_IRQ_TIMER0 ||
163	    is->is_irq == MV78XX0_IRQ_TIMER1 ||
164	    is->is_irq == MV78XX0_IRQ_TIMER2 ||
165	    is->is_irq == MV78XX0_IRQ_TIMER3) {
166		mlmbim = read_mlmbreg(MVSOC_MLMB_MLMBIMR);
167		mlmbim |= TIMER_IRQ2MLMBIMR(is->is_irq);
168		write_mlmbreg(MVSOC_MLMB_MLMBIMR, mlmbim);
169	}
170}
171
172static void
173mv78xx0_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
174{
175
176	strlcpy(buf, sources[pic->pic_irqbase + irq], len);
177}
178
179/*
180 * Called with interrupts disabled
181 */
182static int
183mv78xx0_find_pending_irqs(void)
184{
185	uint32_t pending;
186	int ipl = 0;
187
188	pending = read_mlmbreg(MV78XX0_ICI_MICR(0)) &
189	    read_mlmbreg(MV78XX0_ICI_IRQIMR(0));
190	if (pending != 0)
191		ipl = pic_mark_pending_sources(&mv78xx0_pic, 0, pending);
192
193	pending = read_mlmbreg(MV78XX0_ICI_MICR(1)) &
194	    read_mlmbreg(MV78XX0_ICI_IRQIMR(1));
195	if (pending != 0)
196		ipl |= pic_mark_pending_sources(&mv78xx0_pic, 32, pending);
197
198	return ipl;
199}
200
201/*
202 * Clock functions
203 */
204
205static void
206mv78xx0_getclks(vaddr_t iobase)
207{
208	const static int sys2cpu_clk_ratio_m[] =	/* Mul constant */
209	    { 1, 3, 2, 5, 3, 7, 4, 9, 5, 1, 6 };
210	const static int sys2cpu_clk_ratio_n[] =	/* Div constant */
211	    { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 };
212	uint32_t reg;
213	int x;
214
215#define MHz	* 1000 * 1000
216
217	reg = le32toh(*(volatile uint32_t *)(iobase +
218	    MV78XX0_SAMPLE_AT_RESET_HIGH));
219	switch (reg & 0x180) {
220	case 0x000: mvTclk = 166666667; break;
221	case 0x080: mvTclk =   200 MHz; break;
222	default:    mvTclk =   200 MHz; break;
223	}
224
225	reg = le32toh(*(volatile uint32_t *)(iobase +
226	    MV78XX0_SAMPLE_AT_RESET_LOW));
227
228	switch (reg & 0x0e0) {
229	case 0x020: mvSysclk =   200 MHz; break;
230	case 0x040: mvSysclk = 266666667; break;
231	case 0x060: mvSysclk = 333333334; break;
232	case 0x080: mvSysclk =   400 MHz; break;
233	case 0x0a0: mvSysclk =   250 MHz; break;
234	case 0x0c0: mvSysclk =   300 MHz; break;
235	default:    mvSysclk = 266666667; break;
236	}
237
238	x = (reg & 0xf00) >> 8;
239	mvPclk = sys2cpu_clk_ratio_m[x] * mvSysclk / sys2cpu_clk_ratio_n[x];
240
241#undef MHz
242
243}
244