1*9067f6d4Ssimonb /* $NetBSD: octeon_cib.c,v 1.7 2021/05/05 06:47:29 simonb Exp $ */
2571c5712Sjmcneill
3571c5712Sjmcneill /*-
4571c5712Sjmcneill * Copyright (c) 2020 Jared D. McNeill <jmcneill@invisible.ca>
5571c5712Sjmcneill * All rights reserved.
6571c5712Sjmcneill *
7571c5712Sjmcneill * Redistribution and use in source and binary forms, with or without
8571c5712Sjmcneill * modification, are permitted provided that the following conditions
9571c5712Sjmcneill * are met:
10571c5712Sjmcneill * 1. Redistributions of source code must retain the above copyright
11571c5712Sjmcneill * notice, this list of conditions and the following disclaimer.
12571c5712Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13571c5712Sjmcneill * notice, this list of conditions and the following disclaimer in the
14571c5712Sjmcneill * documentation and/or other materials provided with the distribution.
15571c5712Sjmcneill *
16571c5712Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17571c5712Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18571c5712Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19571c5712Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20571c5712Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21571c5712Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22571c5712Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23571c5712Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24571c5712Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25571c5712Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26571c5712Sjmcneill * SUCH DAMAGE.
27571c5712Sjmcneill */
28571c5712Sjmcneill
29571c5712Sjmcneill #include "opt_multiprocessor.h"
30571c5712Sjmcneill
31571c5712Sjmcneill #include <sys/cdefs.h>
32*9067f6d4Ssimonb __KERNEL_RCSID(0, "$NetBSD: octeon_cib.c,v 1.7 2021/05/05 06:47:29 simonb Exp $");
33571c5712Sjmcneill
34571c5712Sjmcneill #include <sys/param.h>
35571c5712Sjmcneill #include <sys/bus.h>
36571c5712Sjmcneill #include <sys/device.h>
37571c5712Sjmcneill #include <sys/intr.h>
38571c5712Sjmcneill #include <sys/systm.h>
39571c5712Sjmcneill #include <sys/kernel.h>
40571c5712Sjmcneill #include <sys/kmem.h>
41571c5712Sjmcneill
42571c5712Sjmcneill #include <dev/fdt/fdtvar.h>
43571c5712Sjmcneill
44571c5712Sjmcneill #include <arch/mips/cavium/octeonvar.h>
45571c5712Sjmcneill
46571c5712Sjmcneill static int octeon_cib_match(device_t, cfdata_t, void *);
47571c5712Sjmcneill static void octeon_cib_attach(device_t, device_t, void *);
48571c5712Sjmcneill
49571c5712Sjmcneill static void * octeon_cib_establish(device_t, u_int *, int, int,
5059ad346dSjmcneill int (*)(void *), void *, const char *);
51571c5712Sjmcneill static void octeon_cib_disestablish(device_t, void *);
52571c5712Sjmcneill static bool octeon_cib_intrstr(device_t, u_int *, char *, size_t);
53571c5712Sjmcneill
54571c5712Sjmcneill static int octeon_cib_intr(void *);
55571c5712Sjmcneill
56*9067f6d4Ssimonb static struct fdtbus_interrupt_controller_func octeon_cib_funcs = {
57571c5712Sjmcneill .establish = octeon_cib_establish,
58571c5712Sjmcneill .disestablish = octeon_cib_disestablish,
59571c5712Sjmcneill .intrstr = octeon_cib_intrstr
60571c5712Sjmcneill };
61571c5712Sjmcneill
62571c5712Sjmcneill struct octeon_cib_intr {
63571c5712Sjmcneill bool ih_mpsafe;
64571c5712Sjmcneill int ih_type;
65571c5712Sjmcneill int (*ih_func)(void *);
66571c5712Sjmcneill void *ih_arg;
67571c5712Sjmcneill uint64_t ih_mask;
68571c5712Sjmcneill };
69571c5712Sjmcneill
70571c5712Sjmcneill struct octeon_cib_softc {
71571c5712Sjmcneill device_t sc_dev;
72571c5712Sjmcneill int sc_phandle;
73571c5712Sjmcneill bus_space_tag_t sc_bst;
74571c5712Sjmcneill bus_space_handle_t sc_bsh_raw;
75571c5712Sjmcneill bus_space_handle_t sc_bsh_en;
76571c5712Sjmcneill
77571c5712Sjmcneill struct octeon_cib_intr *sc_intr;
78571c5712Sjmcneill u_int sc_nintr;
79571c5712Sjmcneill };
80571c5712Sjmcneill
81571c5712Sjmcneill #define CIB_READ_RAW(sc) \
82571c5712Sjmcneill bus_space_read_8((sc)->sc_bst, (sc)->sc_bsh_raw, 0)
83571c5712Sjmcneill #define CIB_WRITE_RAW(sc, val) \
84571c5712Sjmcneill bus_space_write_8((sc)->sc_bst, (sc)->sc_bsh_raw, 0, (val))
85571c5712Sjmcneill #define CIB_READ_EN(sc) \
86571c5712Sjmcneill bus_space_read_8((sc)->sc_bst, (sc)->sc_bsh_en, 0)
87571c5712Sjmcneill #define CIB_WRITE_EN(sc, val) \
88571c5712Sjmcneill bus_space_write_8((sc)->sc_bst, (sc)->sc_bsh_en, 0, (val))
89571c5712Sjmcneill
90571c5712Sjmcneill CFATTACH_DECL_NEW(octcib, sizeof(struct octeon_cib_softc),
91571c5712Sjmcneill octeon_cib_match, octeon_cib_attach, NULL, NULL);
92571c5712Sjmcneill
93646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
94646c0f59Sthorpej { .compat = "cavium,octeon-7130-cib" },
95ec189949Sthorpej DEVICE_COMPAT_EOL
96571c5712Sjmcneill };
97571c5712Sjmcneill
98571c5712Sjmcneill static int
octeon_cib_match(device_t parent,cfdata_t cf,void * aux)99571c5712Sjmcneill octeon_cib_match(device_t parent, cfdata_t cf, void *aux)
100571c5712Sjmcneill {
101571c5712Sjmcneill struct fdt_attach_args * const faa = aux;
102571c5712Sjmcneill
1036e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
104571c5712Sjmcneill }
105571c5712Sjmcneill
106571c5712Sjmcneill static void
octeon_cib_attach(device_t parent,device_t self,void * aux)107571c5712Sjmcneill octeon_cib_attach(device_t parent, device_t self, void *aux)
108571c5712Sjmcneill {
109571c5712Sjmcneill struct octeon_cib_softc * const sc = device_private(self);
110571c5712Sjmcneill struct fdt_attach_args * const faa = aux;
111571c5712Sjmcneill const int phandle = faa->faa_phandle;
112571c5712Sjmcneill char intrstr[128];
113571c5712Sjmcneill bus_addr_t addr;
114571c5712Sjmcneill bus_size_t size;
115571c5712Sjmcneill u_int max_bits;
116571c5712Sjmcneill int error;
117571c5712Sjmcneill void *ih;
118571c5712Sjmcneill
119571c5712Sjmcneill sc->sc_dev = self;
120571c5712Sjmcneill sc->sc_phandle = phandle;
121571c5712Sjmcneill sc->sc_bst = faa->faa_bst;
122571c5712Sjmcneill
123571c5712Sjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0 ||
124571c5712Sjmcneill bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_raw) != 0) {
125571c5712Sjmcneill aprint_error(": couldn't map RAW register\n");
126571c5712Sjmcneill return;
127571c5712Sjmcneill }
128571c5712Sjmcneill if (fdtbus_get_reg(phandle, 1, &addr, &size) != 0 ||
129571c5712Sjmcneill bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_en) != 0) {
130571c5712Sjmcneill aprint_error(": couldn't map EN register\n");
131571c5712Sjmcneill return;
132571c5712Sjmcneill }
133571c5712Sjmcneill
134571c5712Sjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
135571c5712Sjmcneill aprint_error(": failed to decode interrupt\n");
136571c5712Sjmcneill return;
137571c5712Sjmcneill }
138571c5712Sjmcneill
139571c5712Sjmcneill if (of_getprop_uint32(phandle, "cavium,max-bits", &max_bits) != 0) {
140571c5712Sjmcneill aprint_error(": missing 'cavium,max-bits' property\n");
141571c5712Sjmcneill return;
142571c5712Sjmcneill }
143571c5712Sjmcneill if (max_bits == 0 || max_bits > 64) {
144571c5712Sjmcneill aprint_error(": 'cavium,max-bits' value out of range\n");
145571c5712Sjmcneill return;
146571c5712Sjmcneill }
147571c5712Sjmcneill
148571c5712Sjmcneill sc->sc_intr = kmem_zalloc(sizeof(*sc->sc_intr) * max_bits, KM_SLEEP);
149571c5712Sjmcneill sc->sc_nintr = max_bits;
150571c5712Sjmcneill
151571c5712Sjmcneill error = fdtbus_register_interrupt_controller(self, phandle,
152571c5712Sjmcneill &octeon_cib_funcs);
153571c5712Sjmcneill if (error != 0) {
154571c5712Sjmcneill aprint_error(": couldn't register with fdtbus: %d\n", error);
155571c5712Sjmcneill return;
156571c5712Sjmcneill }
157571c5712Sjmcneill
158571c5712Sjmcneill aprint_naive("\n");
159571c5712Sjmcneill aprint_normal(": CIB\n");
160571c5712Sjmcneill
161571c5712Sjmcneill CIB_WRITE_EN(sc, 0);
162571c5712Sjmcneill CIB_WRITE_RAW(sc, ~0ULL);
163571c5712Sjmcneill
164571c5712Sjmcneill ih = fdtbus_intr_establish(phandle, 0, IPL_SCHED, FDT_INTR_MPSAFE,
165571c5712Sjmcneill octeon_cib_intr, sc);
166571c5712Sjmcneill if (ih == NULL) {
167571c5712Sjmcneill aprint_error_dev(self, "couldn't establish interrupt on %s\n",
168571c5712Sjmcneill intrstr);
169571c5712Sjmcneill return;
170571c5712Sjmcneill }
171571c5712Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
172571c5712Sjmcneill }
173571c5712Sjmcneill
174571c5712Sjmcneill static void *
octeon_cib_establish(device_t dev,u_int * specifier,int ipl,int flags,int (* func)(void *),void * arg,const char * xname)175571c5712Sjmcneill octeon_cib_establish(device_t dev, u_int *specifier, int ipl, int flags,
17659ad346dSjmcneill int (*func)(void *), void *arg, const char *xname)
177571c5712Sjmcneill {
178571c5712Sjmcneill struct octeon_cib_softc * const sc = device_private(dev);
179571c5712Sjmcneill struct octeon_cib_intr *ih;
180571c5712Sjmcneill uint64_t val;
181571c5712Sjmcneill
182571c5712Sjmcneill /* 1st cell is the bit number in the CIB* registers */
183571c5712Sjmcneill /* 2nd cell is the triggering setting */
184571c5712Sjmcneill const int bit = be32toh(specifier[0]);
185571c5712Sjmcneill const int type = (be32toh(specifier[1]) & 0x3) ? IST_EDGE : IST_LEVEL;
186571c5712Sjmcneill
187571c5712Sjmcneill if (bit > sc->sc_nintr) {
188571c5712Sjmcneill aprint_error_dev(dev, "bit %d out of range\n", bit);
189571c5712Sjmcneill return NULL;
190571c5712Sjmcneill }
191571c5712Sjmcneill
192571c5712Sjmcneill ih = &sc->sc_intr[bit];
193571c5712Sjmcneill ih->ih_mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
194571c5712Sjmcneill ih->ih_type = type;
195571c5712Sjmcneill ih->ih_func = func;
196571c5712Sjmcneill ih->ih_arg = arg;
197571c5712Sjmcneill ih->ih_mask = __BIT(bit);
198571c5712Sjmcneill
199571c5712Sjmcneill val = CIB_READ_EN(sc);
200571c5712Sjmcneill val |= ih->ih_mask;
201571c5712Sjmcneill CIB_WRITE_EN(sc, val);
202571c5712Sjmcneill
203571c5712Sjmcneill return ih;
204571c5712Sjmcneill }
205571c5712Sjmcneill
206571c5712Sjmcneill static void
octeon_cib_disestablish(device_t dev,void * ih_cookie)207571c5712Sjmcneill octeon_cib_disestablish(device_t dev, void *ih_cookie)
208571c5712Sjmcneill {
209571c5712Sjmcneill struct octeon_cib_softc * const sc = device_private(dev);
210571c5712Sjmcneill struct octeon_cib_intr *ih = ih_cookie;
211571c5712Sjmcneill uint64_t val;
212571c5712Sjmcneill
213571c5712Sjmcneill val = CIB_READ_EN(sc);
214571c5712Sjmcneill val &= ~ih->ih_mask;
215571c5712Sjmcneill CIB_WRITE_EN(sc, val);
216571c5712Sjmcneill }
217571c5712Sjmcneill
218571c5712Sjmcneill static bool
octeon_cib_intrstr(device_t dev,u_int * specifier,char * buf,size_t buflen)219571c5712Sjmcneill octeon_cib_intrstr(device_t dev, u_int *specifier, char *buf,
220571c5712Sjmcneill size_t buflen)
221571c5712Sjmcneill {
222571c5712Sjmcneill /* 1st cell is the bit number in the CIB* registers */
223571c5712Sjmcneill const int bit = be32toh(specifier[0]);
224571c5712Sjmcneill
225571c5712Sjmcneill snprintf(buf, buflen, "%s intr %d", device_xname(dev), bit);
226571c5712Sjmcneill
227571c5712Sjmcneill return true;
228571c5712Sjmcneill }
229571c5712Sjmcneill
230571c5712Sjmcneill static int
octeon_cib_intr(void * priv)231571c5712Sjmcneill octeon_cib_intr(void *priv)
232571c5712Sjmcneill {
233571c5712Sjmcneill struct octeon_cib_softc * const sc = priv;
234571c5712Sjmcneill struct octeon_cib_intr *ih;
235571c5712Sjmcneill uint64_t pend;
236571c5712Sjmcneill int n, rv = 0;
237571c5712Sjmcneill
238571c5712Sjmcneill pend = CIB_READ_RAW(sc);
239571c5712Sjmcneill pend &= CIB_READ_EN(sc);
240571c5712Sjmcneill
241571c5712Sjmcneill while ((n = ffs64(pend)) != 0) {
242571c5712Sjmcneill ih = &sc->sc_intr[n - 1];
243571c5712Sjmcneill KASSERT(ih->ih_mask == __BIT(n - 1));
244571c5712Sjmcneill
245571c5712Sjmcneill if (ih->ih_type == IST_EDGE)
246571c5712Sjmcneill CIB_WRITE_RAW(sc, ih->ih_mask); /* ack */
247571c5712Sjmcneill
248571c5712Sjmcneill #ifdef MULTIPROCESSOR
249571c5712Sjmcneill if (!ih->ih_mpsafe) {
250571c5712Sjmcneill KERNEL_LOCK(1, NULL);
251571c5712Sjmcneill rv |= ih->ih_func(ih->ih_arg);
252571c5712Sjmcneill KERNEL_UNLOCK_ONE(NULL);
253571c5712Sjmcneill } else
254571c5712Sjmcneill #endif
255571c5712Sjmcneill rv |= ih->ih_func(ih->ih_arg);
256571c5712Sjmcneill
257571c5712Sjmcneill pend &= ~ih->ih_mask;
258571c5712Sjmcneill }
259571c5712Sjmcneill
260571c5712Sjmcneill return rv;
261571c5712Sjmcneill }
262