xref: /openbsd-src/sys/dev/fdt/mvgicp.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: mvgicp.c,v 1.1 2019/02/03 14:03:36 patrick 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 
33 struct 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 
47 int	mvgicp_match(struct device *, void *, void *);
48 void	mvgicp_attach(struct device *, struct device *, void *);
49 
50 void *	mvgicp_intr_establish(void *, uint64_t *, uint64_t *,
51 	    int, int (*)(void *), void *, char *);
52 void	mvgicp_intr_disestablish(void *);
53 
54 struct cfattach mvgicp_ca = {
55 	sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach
56 };
57 
58 struct cfdriver mvgicp_cd = {
59 	NULL, "mvgicp", DV_DULL
60 };
61 
62 int
63 mvgicp_match(struct device *parent, void *match, void *aux)
64 {
65 	struct fdt_attach_args *faa = aux;
66 
67 	return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp");
68 }
69 
70 void
71 mvgicp_attach(struct device *parent, struct device *self, void *aux)
72 {
73 	struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
74 	struct fdt_attach_args *faa = aux;
75 	struct interrupt_controller *ic;
76 	uint32_t phandle;
77 
78 	if (faa->fa_nreg < 1) {
79 		printf(": no registers\n");
80 		return;
81 	}
82 
83 	OF_getpropintarray(faa->fa_node, "marvell,spi-ranges",
84 	    sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges));
85 	sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3];
86 	sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *),
87 	    M_DEVBUF, M_WAITOK | M_ZERO);
88 
89 	sc->sc_iot = faa->fa_iot;
90 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
91 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
92 		printf(": can't map registers\n");
93 		return;
94 	}
95 
96 	/* XXX: Hack to retrieve the physical address (from a CPU PoV). */
97 	if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
98 		printf(": cannot retrieve msi addr\n");
99 		return;
100 	}
101 
102 	extern uint32_t fdt_intr_get_parent(int);
103 	phandle = fdt_intr_get_parent(faa->fa_node);
104 	extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
105 	LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
106 		if (ic->ic_phandle == phandle)
107 			break;
108 	}
109 	sc->sc_parent_ic = ic;
110 
111 	sc->sc_ic.ic_node = faa->fa_node;
112 	sc->sc_ic.ic_cookie = sc;
113 	sc->sc_ic.ic_establish_msi = mvgicp_intr_establish;
114 	sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish;
115 	fdt_intr_register(&sc->sc_ic);
116 
117 	printf("\n");
118 }
119 
120 void *
121 mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data,
122     int level, int (*func)(void *), void *arg, char *name)
123 {
124 	struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
125 	struct interrupt_controller *ic = sc->sc_parent_ic;
126 	uint32_t interrupt[3];
127 	uint32_t flags;
128 	void *cookie;
129 	int i, spi;
130 
131 	if (ic == NULL)
132 		return NULL;
133 
134 	for (i = 0; i < sc->sc_nspi; i++) {
135 		if (sc->sc_spi[i] == NULL) {
136 			spi = i;
137 			break;
138 		}
139 	}
140 	if (i == sc->sc_nspi)
141 		return NULL;
142 
143 	flags = *data;
144 
145 	*addr = sc->sc_addr;
146 	*data = spi;
147 
148 	/* Convert to GIC interrupt source. */
149 	for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) {
150 		if (spi < sc->sc_spi_ranges[i + 1]) {
151 			spi += sc->sc_spi_ranges[i];
152 			break;
153 		}
154 		spi -= sc->sc_spi_ranges[i + 1];
155 	}
156 	if (i == nitems(sc->sc_spi_ranges))
157 		return NULL;
158 
159 	interrupt[0] = 0;
160 	interrupt[1] = spi - 32;
161 	interrupt[2] = flags;
162 	cookie = ic->ic_establish(ic->ic_cookie, interrupt, level,
163 	    func, arg, name);
164 	if (cookie == NULL)
165 		return NULL;
166 
167 	sc->sc_spi[*data] = cookie;
168 	return &sc->sc_spi[*data];
169 }
170 
171 void
172 mvgicp_intr_disestablish(void *cookie)
173 {
174 	fdt_intr_disestablish(*(void **)cookie);
175 	*(void **)cookie = NULL;
176 }
177