xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2836.c (revision 6ae1554a5d9b318f8ad53ccc39fa5a961403da73)
1 /*
2  * Copyright 2015 Andrew Turner.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
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 AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/rman.h>
37 
38 #include <machine/bus.h>
39 #include <machine/resource.h>
40 
41 #include <dev/ofw/ofw_bus_subr.h>
42 #include <dev/ofw/ofw_bus.h>
43 
44 #include <arm/broadcom/bcm2835/bcm2836.h>
45 
46 #define	ARM_LOCAL_BASE	0x40000000
47 #define	ARM_LOCAL_SIZE	0x00001000
48 
49 #define	ARM_LOCAL_CONTROL		0x00
50 #define	ARM_LOCAL_PRESCALER		0x08
51 #define	 PRESCALER_19_2			0x80000000 /* 19.2 MHz */
52 #define	ARM_LOCAL_INT_TIMER(n)		(0x40 + (n) * 4)
53 #define	ARM_LOCAL_INT_MAILBOX(n)	(0x50 + (n) * 4)
54 #define	ARM_LOCAL_INT_PENDING(n)	(0x60 + (n) * 4)
55 #define	 INT_PENDING_MASK		0x01f
56 #define	MAILBOX0_IRQ			4
57 #define	MAILBOX0_IRQEN			(1 << 0)
58 
59 /*
60  * A driver for features of the bcm2836.
61  */
62 
63 struct bcm2836_softc {
64 	device_t	 sc_dev;
65 	struct resource *sc_mem;
66 };
67 
68 static device_identify_t bcm2836_identify;
69 static device_probe_t bcm2836_probe;
70 static device_attach_t bcm2836_attach;
71 
72 struct bcm2836_softc *softc;
73 
74 static void
75 bcm2836_identify(driver_t *driver, device_t parent)
76 {
77 
78 	if (BUS_ADD_CHILD(parent, 0, "bcm2836", -1) == NULL)
79 		device_printf(parent, "add child failed\n");
80 }
81 
82 static int
83 bcm2836_probe(device_t dev)
84 {
85 
86 	if (softc != NULL)
87 		return (ENXIO);
88 
89 	device_set_desc(dev, "Broadcom bcm2836");
90 
91 	return (BUS_PROBE_DEFAULT);
92 }
93 
94 static int
95 bcm2836_attach(device_t dev)
96 {
97 	int i, rid;
98 
99 	softc = device_get_softc(dev);
100 	softc->sc_dev = dev;
101 
102 	rid = 0;
103 	softc->sc_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
104 	    ARM_LOCAL_BASE, ARM_LOCAL_BASE + ARM_LOCAL_SIZE, ARM_LOCAL_SIZE,
105 	    RF_ACTIVE);
106 	if (softc->sc_mem == NULL) {
107 		device_printf(dev, "could not allocate memory resource\n");
108 		return (ENXIO);
109 	}
110 
111 	bus_write_4(softc->sc_mem, ARM_LOCAL_CONTROL, 0);
112 	bus_write_4(softc->sc_mem, ARM_LOCAL_PRESCALER, PRESCALER_19_2);
113 
114 	for (i = 0; i < 4; i++)
115 		bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), 0);
116 
117 	for (i = 0; i < 4; i++)
118 		bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(i), 1);
119 
120 	return (0);
121 }
122 
123 int
124 bcm2836_get_next_irq(int last_irq)
125 {
126 	uint32_t reg;
127 	int cpu;
128 	int irq;
129 
130 	cpu = PCPU_GET(cpuid);
131 
132 	reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_PENDING(cpu));
133 	reg &= INT_PENDING_MASK;
134 	if (reg == 0)
135 		return (-1);
136 
137 	irq = ffs(reg) - 1;
138 
139 	return (irq);
140 }
141 
142 void
143 bcm2836_mask_irq(uintptr_t irq)
144 {
145 	uint32_t reg;
146 #ifdef SMP
147 	int cpu;
148 #endif
149 	int i;
150 
151 	if (irq < MAILBOX0_IRQ) {
152 		for (i = 0; i < 4; i++) {
153 			reg = bus_read_4(softc->sc_mem,
154 			    ARM_LOCAL_INT_TIMER(i));
155 			reg &= ~(1 << irq);
156 			bus_write_4(softc->sc_mem,
157 			    ARM_LOCAL_INT_TIMER(i), reg);
158 		}
159 #ifdef SMP
160 	} else if (irq == MAILBOX0_IRQ) {
161 		/* Mailbox 0 for IPI */
162 		cpu = PCPU_GET(cpuid);
163 		reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu));
164 		reg &= ~MAILBOX0_IRQEN;
165 		bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg);
166 #endif
167 	}
168 }
169 
170 void
171 bcm2836_unmask_irq(uintptr_t irq)
172 {
173 	uint32_t reg;
174 #ifdef SMP
175 	int cpu;
176 #endif
177 	int i;
178 
179 	if (irq < MAILBOX0_IRQ) {
180 		for (i = 0; i < 4; i++) {
181 			reg = bus_read_4(softc->sc_mem,
182 			    ARM_LOCAL_INT_TIMER(i));
183 			reg |= (1 << irq);
184 			bus_write_4(softc->sc_mem,
185 			    ARM_LOCAL_INT_TIMER(i), reg);
186 		}
187 #ifdef SMP
188 	} else if (irq == MAILBOX0_IRQ) {
189 		/* Mailbox 0 for IPI */
190 		cpu = PCPU_GET(cpuid);
191 		reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu));
192 		reg |= MAILBOX0_IRQEN;
193 		bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg);
194 #endif
195 	}
196 }
197 
198 static device_method_t bcm2836_methods[] = {
199 	/* Device interface */
200 	DEVMETHOD(device_identify,	bcm2836_identify),
201 	DEVMETHOD(device_probe,		bcm2836_probe),
202 	DEVMETHOD(device_attach,	bcm2836_attach),
203 
204 	DEVMETHOD_END
205 };
206 
207 static devclass_t bcm2836_devclass;
208 
209 static driver_t bcm2836_driver = {
210 	"bcm2836",
211 	bcm2836_methods,
212 	sizeof(struct bcm2836_softc),
213 };
214 
215 EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0,
216     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
217