1/*	$OpenBSD: mvgicp.c,v 1.5 2021/10/24 17:52:26 mpi Exp $	*/
2/*
3 * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21#include <sys/malloc.h>
22
23#include <uvm/uvm_extern.h>
24
25#include <machine/intr.h>
26#include <machine/bus.h>
27#include <machine/fdt.h>
28
29#include <dev/ofw/openfirm.h>
30#include <dev/ofw/ofw_misc.h>
31#include <dev/ofw/fdt.h>
32
33struct mvgicp_softc {
34	struct device		  sc_dev;
35	bus_space_tag_t		  sc_iot;
36	bus_space_handle_t	  sc_ioh;
37	paddr_t			  sc_addr;
38
39	uint32_t		  sc_spi_ranges[4];
40	void			**sc_spi;
41	uint32_t		  sc_nspi;
42
43	struct interrupt_controller sc_ic;
44	struct interrupt_controller *sc_parent_ic;
45};
46
47int	mvgicp_match(struct device *, void *, void *);
48void	mvgicp_attach(struct device *, struct device *, void *);
49
50void *	mvgicp_intr_establish(void *, uint64_t *, uint64_t *,
51	    int, struct cpu_info *, int (*)(void *), void *, char *);
52void	mvgicp_intr_disestablish(void *);
53void	mvgicp_intr_barrier(void *);
54
55const struct cfattach mvgicp_ca = {
56	sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach
57};
58
59struct cfdriver mvgicp_cd = {
60	NULL, "mvgicp", DV_DULL
61};
62
63int
64mvgicp_match(struct device *parent, void *match, void *aux)
65{
66	struct fdt_attach_args *faa = aux;
67
68	return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp");
69}
70
71void
72mvgicp_attach(struct device *parent, struct device *self, void *aux)
73{
74	struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
75	struct fdt_attach_args *faa = aux;
76	struct interrupt_controller *ic;
77	uint32_t phandle;
78
79	if (faa->fa_nreg < 1) {
80		printf(": no registers\n");
81		return;
82	}
83
84	OF_getpropintarray(faa->fa_node, "marvell,spi-ranges",
85	    sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges));
86	sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3];
87	sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *),
88	    M_DEVBUF, M_WAITOK | M_ZERO);
89
90	sc->sc_iot = faa->fa_iot;
91	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
92	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
93		printf(": can't map registers\n");
94		return;
95	}
96
97	/* XXX: Hack to retrieve the physical address (from a CPU PoV). */
98	if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
99		printf(": cannot retrieve msi addr\n");
100		return;
101	}
102
103	extern uint32_t fdt_intr_get_parent(int);
104	phandle = fdt_intr_get_parent(faa->fa_node);
105	extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
106	LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
107		if (ic->ic_phandle == phandle)
108			break;
109	}
110	sc->sc_parent_ic = ic;
111
112	sc->sc_ic.ic_node = faa->fa_node;
113	sc->sc_ic.ic_cookie = sc;
114	sc->sc_ic.ic_establish_msi = mvgicp_intr_establish;
115	sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish;
116	sc->sc_ic.ic_barrier = mvgicp_intr_barrier;
117	fdt_intr_register(&sc->sc_ic);
118
119	printf("\n");
120}
121
122void *
123mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data,
124    int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
125{
126	struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
127	struct interrupt_controller *ic = sc->sc_parent_ic;
128	struct machine_intr_handle *ih;
129	uint32_t interrupt[3];
130	uint32_t flags;
131	void *cookie;
132	int i, spi;
133
134	if (ic == NULL)
135		return NULL;
136
137	for (i = 0; i < sc->sc_nspi; i++) {
138		if (sc->sc_spi[i] == NULL) {
139			spi = i;
140			break;
141		}
142	}
143	if (i == sc->sc_nspi)
144		return NULL;
145
146	flags = *data;
147
148	*addr = sc->sc_addr;
149	*data = spi;
150
151	/* Convert to GIC interrupt source. */
152	for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) {
153		if (spi < sc->sc_spi_ranges[i + 1]) {
154			spi += sc->sc_spi_ranges[i];
155			break;
156		}
157		spi -= sc->sc_spi_ranges[i + 1];
158	}
159	if (i == nitems(sc->sc_spi_ranges))
160		return NULL;
161
162	interrupt[0] = 0;
163	interrupt[1] = spi - 32;
164	interrupt[2] = flags;
165	cookie = ic->ic_establish(ic->ic_cookie, interrupt, level,
166	    ci, func, arg, name);
167	if (cookie == NULL)
168		return NULL;
169
170	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
171	ih->ih_ic = ic;
172	ih->ih_ih = cookie;
173
174	sc->sc_spi[*data] = ih;
175	return &sc->sc_spi[*data];
176}
177
178void
179mvgicp_intr_disestablish(void *cookie)
180{
181	struct machine_intr_handle *ih = *(void **)cookie;
182	struct interrupt_controller *ic = ih->ih_ic;
183
184	ic->ic_disestablish(ih->ih_ih);
185	free(ih, M_DEVBUF, sizeof(*ih));
186	*(void **)cookie = NULL;
187}
188
189void
190mvgicp_intr_barrier(void *cookie)
191{
192	struct machine_intr_handle *ih = *(void **)cookie;
193	struct interrupt_controller *ic = ih->ih_ic;
194
195	ic->ic_barrier(ih->ih_ih);
196}
197