1*8a1e8ccaSmiod /* $OpenBSD: fhc.c,v 1.22 2024/03/29 21:29:33 miod Exp $ */
2f63ee831Sjason
3f63ee831Sjason /*
4f63ee831Sjason * Copyright (c) 2004 Jason L. Wright (jason@thought.net)
5f63ee831Sjason * All rights reserved.
6f63ee831Sjason *
7f63ee831Sjason * Redistribution and use in source and binary forms, with or without
8f63ee831Sjason * modification, are permitted provided that the following conditions
9f63ee831Sjason * are met:
10f63ee831Sjason * 1. Redistributions of source code must retain the above copyright
11f63ee831Sjason * notice, this list of conditions and the following disclaimer.
12f63ee831Sjason * 2. Redistributions in binary form must reproduce the above copyright
13f63ee831Sjason * notice, this list of conditions and the following disclaimer in the
14f63ee831Sjason * documentation and/or other materials provided with the distribution.
15f63ee831Sjason *
16f63ee831Sjason * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17f63ee831Sjason * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18f63ee831Sjason * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19f63ee831Sjason * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20f63ee831Sjason * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21f63ee831Sjason * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22f63ee831Sjason * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f63ee831Sjason * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24f63ee831Sjason * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25f63ee831Sjason * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26f63ee831Sjason * POSSIBILITY OF SUCH DAMAGE.
27f63ee831Sjason */
28f63ee831Sjason
29f63ee831Sjason #include <sys/param.h>
30f63ee831Sjason #include <sys/systm.h>
31f63ee831Sjason #include <sys/kernel.h>
32f63ee831Sjason #include <sys/device.h>
33f63ee831Sjason #include <sys/conf.h>
34f63ee831Sjason #include <sys/timeout.h>
35f63ee831Sjason #include <sys/malloc.h>
36f63ee831Sjason
37f63ee831Sjason #include <machine/bus.h>
38f63ee831Sjason #include <machine/autoconf.h>
39f63ee831Sjason #include <machine/openfirm.h>
40f63ee831Sjason
4109338432Sjason #include <sparc64/dev/fhcreg.h>
42f63ee831Sjason #include <sparc64/dev/fhcvar.h>
4309338432Sjason #include <sparc64/dev/iommureg.h>
44f63ee831Sjason
45f63ee831Sjason struct cfdriver fhc_cd = {
46f63ee831Sjason NULL, "fhc", DV_DULL
47f63ee831Sjason };
48f63ee831Sjason
49f63ee831Sjason int fhc_print(void *, const char *);
50f63ee831Sjason
51f63ee831Sjason bus_space_tag_t fhc_alloc_bus_tag(struct fhc_softc *);
52f63ee831Sjason int _fhc_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t, bus_size_t,
53f63ee831Sjason int, bus_space_handle_t *);
5409338432Sjason void *fhc_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
5509338432Sjason int (*)(void *), void *, const char *);
5609338432Sjason bus_space_handle_t *fhc_find_intr_handle(struct fhc_softc *, int);
579f31f4f7Sjason void fhc_led_blink(void *, int);
58f63ee831Sjason
59f63ee831Sjason void
fhc_attach(struct fhc_softc * sc)60f63ee831Sjason fhc_attach(struct fhc_softc *sc)
61f63ee831Sjason {
62f63ee831Sjason int node0, node;
63c7a321c7Sjason u_int32_t ctrl;
64f63ee831Sjason
6511618695Sjason printf(" board %d: %s\n", sc->sc_board,
66d435f11cSjason getpropstring(sc->sc_node, "board-model"));
67f63ee831Sjason
68f63ee831Sjason sc->sc_cbt = fhc_alloc_bus_tag(sc);
69f63ee831Sjason
702af5f99fSjason sc->sc_ign = sc->sc_board << 1;
712af5f99fSjason bus_space_write_4(sc->sc_bt, sc->sc_ireg, FHC_I_IGN, sc->sc_ign);
722af5f99fSjason sc->sc_ign = bus_space_read_4(sc->sc_bt, sc->sc_ireg, FHC_I_IGN);
732af5f99fSjason
74c7a321c7Sjason ctrl = bus_space_read_4(sc->sc_bt, sc->sc_preg, FHC_P_CTRL);
75c7a321c7Sjason if (!sc->sc_is_central)
76c7a321c7Sjason ctrl |= FHC_P_CTRL_IXIST;
77c7a321c7Sjason ctrl &= ~(FHC_P_CTRL_AOFF | FHC_P_CTRL_BOFF | FHC_P_CTRL_SLINE);
78c7a321c7Sjason bus_space_write_4(sc->sc_bt, sc->sc_preg, FHC_P_CTRL, ctrl);
79c7a321c7Sjason bus_space_read_4(sc->sc_bt, sc->sc_preg, FHC_P_CTRL);
80c7a321c7Sjason
81c7a321c7Sjason /* clear interrupts */
82c7a321c7Sjason bus_space_write_4(sc->sc_bt, sc->sc_freg, FHC_F_ICLR, 0);
83c7a321c7Sjason bus_space_read_4(sc->sc_bt, sc->sc_freg, FHC_F_ICLR);
84c7a321c7Sjason bus_space_write_4(sc->sc_bt, sc->sc_sreg, FHC_S_ICLR, 0);
85c7a321c7Sjason bus_space_read_4(sc->sc_bt, sc->sc_sreg, FHC_S_ICLR);
86c7a321c7Sjason bus_space_write_4(sc->sc_bt, sc->sc_ureg, FHC_U_ICLR, 0);
87c7a321c7Sjason bus_space_read_4(sc->sc_bt, sc->sc_ureg, FHC_U_ICLR);
88c7a321c7Sjason bus_space_write_4(sc->sc_bt, sc->sc_treg, FHC_T_ICLR, 0);
89c7a321c7Sjason bus_space_read_4(sc->sc_bt, sc->sc_treg, FHC_T_ICLR);
90c7a321c7Sjason
91f63ee831Sjason getprop(sc->sc_node, "ranges", sizeof(struct fhc_range),
92f63ee831Sjason &sc->sc_nrange, (void **)&sc->sc_range);
93f63ee831Sjason
94f63ee831Sjason node0 = firstchild(sc->sc_node);
95f63ee831Sjason for (node = node0; node; node = nextsibling(node)) {
96f63ee831Sjason struct fhc_attach_args fa;
97f63ee831Sjason
98b4088691Skettenis if (!checkstatus(node))
99b4088691Skettenis continue;
100b4088691Skettenis
101f63ee831Sjason bzero(&fa, sizeof(fa));
102f63ee831Sjason
103f63ee831Sjason fa.fa_node = node;
104f63ee831Sjason fa.fa_bustag = sc->sc_cbt;
105f63ee831Sjason
106f63ee831Sjason if (fhc_get_string(fa.fa_node, "name", &fa.fa_name)) {
107f63ee831Sjason printf("can't fetch name for node 0x%x\n", node);
108f63ee831Sjason continue;
109f63ee831Sjason }
110f63ee831Sjason getprop(node, "reg", sizeof(struct fhc_reg),
111f63ee831Sjason &fa.fa_nreg, (void **)&fa.fa_reg);
11209338432Sjason getprop(node, "interrupts", sizeof(int),
11309338432Sjason &fa.fa_nintr, (void **)&fa.fa_intr);
114b150d549Sjason getprop(node, "address", sizeof(*fa.fa_promvaddrs),
115b150d549Sjason &fa.fa_npromvaddrs, (void **)&fa.fa_promvaddrs);
116f63ee831Sjason
117f63ee831Sjason (void)config_found(&sc->sc_dv, (void *)&fa, fhc_print);
118f63ee831Sjason
119f8e6c425Stedu free(fa.fa_name, M_DEVBUF, 0);
120f8e6c425Stedu free(fa.fa_reg, M_DEVBUF, 0);
121f8e6c425Stedu free(fa.fa_intr, M_DEVBUF, 0);
122f8e6c425Stedu free(fa.fa_promvaddrs, M_DEVBUF, 0);
123f63ee831Sjason }
124cbf7153bSjason
1259f31f4f7Sjason sc->sc_blink.bl_func = fhc_led_blink;
1269f31f4f7Sjason sc->sc_blink.bl_arg = sc;
1279f31f4f7Sjason blink_led_register(&sc->sc_blink);
128f63ee831Sjason }
129f63ee831Sjason
130f63ee831Sjason int
fhc_print(void * args,const char * busname)131f63ee831Sjason fhc_print(void *args, const char *busname)
132f63ee831Sjason {
133f63ee831Sjason struct fhc_attach_args *fa = args;
134f63ee831Sjason char *class;
135f63ee831Sjason
136f63ee831Sjason if (busname != NULL) {
137909f848aSkettenis printf("\"%s\" at %s", fa->fa_name, busname);
138f63ee831Sjason class = getpropstring(fa->fa_node, "device_type");
139f63ee831Sjason if (*class != '\0')
140f63ee831Sjason printf(" class %s", class);
141f63ee831Sjason }
142f63ee831Sjason return (UNCONF);
143f63ee831Sjason }
144f63ee831Sjason
145f63ee831Sjason int
fhc_get_string(int node,char * name,char ** buf)146f63ee831Sjason fhc_get_string(int node, char *name, char **buf)
147f63ee831Sjason {
148f63ee831Sjason int len;
149f63ee831Sjason
150f63ee831Sjason len = getproplen(node, name);
151f63ee831Sjason if (len < 0)
152f63ee831Sjason return (len);
153f63ee831Sjason *buf = (char *)malloc(len + 1, M_DEVBUF, M_NOWAIT);
154f1cc154aSjason if (*buf == NULL)
155f63ee831Sjason return (-1);
156f63ee831Sjason
157f63ee831Sjason if (len != 0)
158f63ee831Sjason getpropstringA(node, name, *buf);
159f63ee831Sjason (*buf)[len] = '\0';
160f63ee831Sjason return (0);
161f63ee831Sjason }
162f63ee831Sjason
163f63ee831Sjason bus_space_tag_t
fhc_alloc_bus_tag(struct fhc_softc * sc)164f63ee831Sjason fhc_alloc_bus_tag(struct fhc_softc *sc)
165f63ee831Sjason {
166f63ee831Sjason struct sparc_bus_space_tag *bt;
167f63ee831Sjason
168bc9397c2Skrw bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
169f63ee831Sjason if (bt == NULL)
170f63ee831Sjason panic("fhc: couldn't alloc bus tag");
171f63ee831Sjason
172ec13f893Smiod strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name));
173f63ee831Sjason bt->cookie = sc;
174f63ee831Sjason bt->parent = sc->sc_bt;
175f63ee831Sjason bt->asi = bt->parent->asi;
176f63ee831Sjason bt->sasi = bt->parent->sasi;
177f63ee831Sjason bt->sparc_bus_map = _fhc_bus_map;
178f63ee831Sjason /* XXX bt->sparc_bus_mmap = fhc_bus_mmap; */
17909338432Sjason bt->sparc_intr_establish = fhc_intr_establish;
180f63ee831Sjason return (bt);
181f63ee831Sjason }
182f63ee831Sjason
183f63ee831Sjason int
_fhc_bus_map(bus_space_tag_t t,bus_space_tag_t t0,bus_addr_t addr,bus_size_t size,int flags,bus_space_handle_t * hp)184f63ee831Sjason _fhc_bus_map(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t addr,
185f63ee831Sjason bus_size_t size, int flags, bus_space_handle_t *hp)
186f63ee831Sjason {
187f63ee831Sjason struct fhc_softc *sc = t->cookie;
188f63ee831Sjason int64_t slot = BUS_ADDR_IOSPACE(addr);
189f63ee831Sjason int64_t offset = BUS_ADDR_PADDR(addr);
190f63ee831Sjason int i;
191f63ee831Sjason
192f63ee831Sjason if (t->parent == NULL || t->parent->sparc_bus_map == NULL) {
193f63ee831Sjason printf("\n_fhc_bus_map: invalid parent");
194f63ee831Sjason return (EINVAL);
195f63ee831Sjason }
196f63ee831Sjason
197f63ee831Sjason if (flags & BUS_SPACE_MAP_PROMADDRESS)
198f63ee831Sjason return ((*t->parent->sparc_bus_map)(t, t0, addr,
199f63ee831Sjason size, flags, hp));
200f63ee831Sjason
201f63ee831Sjason for (i = 0; i < sc->sc_nrange; i++) {
202f63ee831Sjason bus_addr_t paddr;
203f63ee831Sjason
204f63ee831Sjason if (sc->sc_range[i].cspace != slot)
205f63ee831Sjason continue;
206f63ee831Sjason
207f63ee831Sjason paddr = offset - sc->sc_range[i].coffset;
208f63ee831Sjason paddr += sc->sc_range[i].poffset;
209f63ee831Sjason paddr |= ((bus_addr_t)sc->sc_range[i].pspace << 32);
210f63ee831Sjason
2114e302ebeSjason return ((*t->parent->sparc_bus_map)(t->parent, t0, paddr,
212f63ee831Sjason size, flags, hp));
213f63ee831Sjason }
214f63ee831Sjason
215f63ee831Sjason return (EINVAL);
216f63ee831Sjason }
21709338432Sjason
21809338432Sjason bus_space_handle_t *
fhc_find_intr_handle(struct fhc_softc * sc,int ino)219fa5ef511Skettenis fhc_find_intr_handle(struct fhc_softc *sc, int ino)
22009338432Sjason {
221fa5ef511Skettenis switch (FHC_INO(ino)) {
222fa5ef511Skettenis case FHC_F_INO:
223fa5ef511Skettenis return &sc->sc_freg;
224fa5ef511Skettenis case FHC_S_INO:
225fa5ef511Skettenis return &sc->sc_sreg;
226fa5ef511Skettenis case FHC_U_INO:
227fa5ef511Skettenis return &sc->sc_ureg;
228fa5ef511Skettenis case FHC_T_INO:
229fa5ef511Skettenis return &sc->sc_treg;
230fa5ef511Skettenis default:
231fa5ef511Skettenis break;
23209338432Sjason }
23309338432Sjason
23409338432Sjason return (NULL);
23509338432Sjason }
23609338432Sjason
23709338432Sjason void *
fhc_intr_establish(bus_space_tag_t t,bus_space_tag_t t0,int ihandle,int level,int flags,int (* handler)(void *),void * arg,const char * what)23809338432Sjason fhc_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
23909338432Sjason int level, int flags, int (*handler)(void *), void *arg, const char *what)
24009338432Sjason {
24109338432Sjason struct fhc_softc *sc = t->cookie;
24209338432Sjason volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
24309338432Sjason struct intrhand *ih;
24441cccc39Sjason long vec;
24509338432Sjason
24609338432Sjason if (level == IPL_NONE)
24709338432Sjason level = INTLEV(ihandle);
24809338432Sjason if (level == IPL_NONE) {
24909338432Sjason printf(": no IPL, setting IPL 2.\n");
25009338432Sjason level = 2;
25109338432Sjason }
25209338432Sjason
25309338432Sjason if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) {
25409338432Sjason bus_space_handle_t *hp;
25509338432Sjason struct fhc_intr_reg *intrregs;
25609338432Sjason
25709338432Sjason hp = fhc_find_intr_handle(sc, ihandle);
25809338432Sjason if (hp == NULL) {
25909338432Sjason printf(": can't find intr handle\n");
26009338432Sjason return (NULL);
26109338432Sjason }
26209338432Sjason
26309338432Sjason intrregs = bus_space_vaddr(sc->sc_bt, *hp);
26409338432Sjason intrmapptr = &intrregs->imap;
26509338432Sjason intrclrptr = &intrregs->iclr;
2662af5f99fSjason vec = ((sc->sc_ign << INTMAP_IGN_SHIFT) & INTMAP_IGN) |
2672af5f99fSjason INTINO(ihandle);
26841cccc39Sjason } else
26941cccc39Sjason vec = INTVEC(ihandle);
27009338432Sjason
27141cccc39Sjason ih = bus_intr_allocate(t0, handler, arg, vec, level, intrmapptr,
27241cccc39Sjason intrclrptr, what);
27309338432Sjason if (ih == NULL)
27409338432Sjason return (NULL);
27509338432Sjason
276*8a1e8ccaSmiod intr_establish(ih);
27709338432Sjason
27809338432Sjason if (intrmapptr != NULL) {
27909338432Sjason u_int64_t r;
28009338432Sjason
28109338432Sjason r = *intrmapptr;
28209338432Sjason r |= INTMAP_V;
28309338432Sjason *intrmapptr = r;
28409338432Sjason r = *intrmapptr;
28509338432Sjason ih->ih_number |= r & INTMAP_INR;
28609338432Sjason }
28709338432Sjason
28809338432Sjason return (ih);
28909338432Sjason }
290cbf7153bSjason
291cbf7153bSjason void
fhc_led_blink(void * vsc,int on)2929f31f4f7Sjason fhc_led_blink(void *vsc, int on)
293cbf7153bSjason {
294cbf7153bSjason struct fhc_softc *sc = vsc;
2959f31f4f7Sjason int s;
296cbf7153bSjason u_int32_t r;
297cbf7153bSjason
298cbf7153bSjason s = splhigh();
299cbf7153bSjason r = bus_space_read_4(sc->sc_bt, sc->sc_preg, FHC_P_CTRL);
3009f31f4f7Sjason if (on)
3019f31f4f7Sjason r |= FHC_P_CTRL_RLED;
3029f31f4f7Sjason else
3039f31f4f7Sjason r &= ~FHC_P_CTRL_RLED;
304cbf7153bSjason r &= ~(FHC_P_CTRL_AOFF | FHC_P_CTRL_BOFF | FHC_P_CTRL_SLINE);
305cbf7153bSjason bus_space_write_4(sc->sc_bt, sc->sc_preg, FHC_P_CTRL, r);
306cbf7153bSjason bus_space_read_4(sc->sc_bt, sc->sc_preg, FHC_P_CTRL);
307cbf7153bSjason splx(s);
308cbf7153bSjason }
309