Home | History | Annotate | Line # | Download | only in gemini
gemini_ipi.c revision 1.1
      1 #include "opt_gemini.h"
      2 #if !defined(GEMINI_MASTER)  && !defined(GEMINI_SLAVE)
      3 # error IPI needs GEMINI_MASTER or GEMINI_SLAVE
      4 #endif
      5 #include "locators.h"
      6 
      7 #include <sys/cdefs.h>
      8 
      9 __KERNEL_RCSID(0, "$NetBSD: gemini_ipi.c,v 1.1 2008/11/20 08:02:48 cliff Exp $");
     10 
     11 #include <sys/param.h>
     12 #include <sys/systm.h>
     13 #include <sys/device.h>
     14 #include <sys/intr.h>
     15 #include <sys/malloc.h>
     16 #include <arch/arm/gemini/gemini_obiovar.h>
     17 #include <arch/arm/gemini/gemini_ipivar.h>
     18 #include <arch/arm/gemini/gemini_reg.h>
     19 
     20 static int  gemini_ipi_match(struct device *, struct cfdata *, void *);
     21 static void gemini_ipi_attach(struct device *, struct device *, void *);
     22 static int  gemini_ipiintr(void *);
     23 
     24 CFATTACH_DECL_NEW(geminiipi, sizeof(struct gemini_ipi_softc),
     25 	gemini_ipi_match, gemini_ipi_attach, NULL, NULL);
     26 
     27 static gemini_ipi_softc_t *gemini_ipi_sc;
     28 
     29 
     30 static int
     31 gemini_ipi_match(struct device *parent, struct cfdata *cf, void *aux)
     32 {
     33         struct obio_attach_args *obio = aux;
     34 
     35         if (obio->obio_intr == LPCCF_INTR_DEFAULT)
     36                 panic("ipi must specify intr in config.");
     37 
     38         return 1;
     39 }
     40 
     41 static void
     42 gemini_ipi_attach(struct device *parent, struct device *self, void *aux)
     43 {
     44         gemini_ipi_softc_t *sc = device_private(self);
     45         struct obio_attach_args *obio = aux;
     46 	bus_space_tag_t iot;
     47 	bus_space_handle_t ioh;
     48 	bus_size_t size;
     49 	bus_addr_t addr;
     50 	void *ih;
     51 
     52 	iot = obio->obio_iot;
     53 	addr = GEMINI_GLOBAL_BASE;
     54 	size = 4096;		/* XXX */
     55 
     56 	if (bus_space_map(iot, addr, size, 0, &ioh))
     57                 panic("%s: Cannot map registers", device_xname(self));
     58 
     59 	/*
     60 	 * NOTE we are using IPL_NET, not IPL_IPI a.k.a. IPL_HIGH
     61 	 * use of IPI on this system is (mainly) networking
     62 	 * keep simple (for now) and force all IPIs to same level
     63 	 * so splnet() can block them as any other NIC.
     64 	 */
     65 #if 0
     66 	ih = intr_establish(obio->obio_intr, IPL_NET, IST_LEVEL_HIGH,
     67 		gemini_ipiintr, sc);
     68 #else
     69 	ih = intr_establish(obio->obio_intr, IPL_NET, IST_EDGE_RISING,
     70 		gemini_ipiintr, sc);
     71 #endif
     72 	if (ih == NULL)
     73 		panic("%s: Cannot establish interrupt %d\n",
     74 			device_xname(self), obio->obio_intr);
     75 
     76 	SIMPLEQ_INIT(&sc->sc_intrq);
     77 
     78 	sc->sc_iot = iot;
     79 	sc->sc_ioh = ioh;
     80 	sc->sc_addr = addr;
     81 	sc->sc_size = size;
     82 	sc->sc_intr = obio->obio_intr;
     83 	sc->sc_ih = ih;
     84 
     85 	gemini_ipi_sc = sc;
     86 
     87 	aprint_normal("\n");
     88 	aprint_naive("\n");
     89 }
     90 
     91 static inline int
     92 gemini_ipi_intrq_empty(gemini_ipi_softc_t *sc)
     93 {
     94 	return SIMPLEQ_EMPTY(&sc->sc_intrq);
     95 }
     96 
     97 static inline void *
     98 gemini_ipi_intrq_insert(gemini_ipi_softc_t *sc, int (*func)(void *), void *arg)
     99 {
    100 	gemini_ipi_intrq_t *iqp;
    101 
    102         iqp = malloc(sizeof(*iqp), M_DEVBUF, M_NOWAIT|M_ZERO);
    103         if (iqp == NULL) {
    104 		printf("gemini_ipi_intrq_insert: malloc failed\n");
    105 		return NULL;
    106 	}
    107 
    108         iqp->iq_func = func;
    109         iqp->iq_arg = arg;
    110         SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q);
    111 
    112 	return (void *)iqp;
    113 }
    114 
    115 static inline void
    116 gemini_ipi_intrq_remove(gemini_ipi_softc_t *sc, void *cookie)
    117 {
    118 	gemini_ipi_intrq_t *iqp;
    119 
    120 	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
    121 		if ((void *)iqp == cookie) {
    122 			SIMPLEQ_REMOVE(&sc->sc_intrq,
    123 				iqp, gemini_ipi_intrq, iq_q);
    124 			free(iqp, M_DEVBUF);
    125 			return;
    126 		}
    127 	}
    128 }
    129 
    130 static inline int
    131 gemini_ipi_intrq_dispatch(gemini_ipi_softc_t *sc)
    132 {
    133 	gemini_ipi_intrq_t *iqp;
    134 	int rv = 0;
    135 
    136 	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q)
    137 		rv |= (*iqp->iq_func)(iqp->iq_arg);
    138 
    139 	return (rv != 0);
    140 }
    141 
    142 
    143 void *
    144 ipi_intr_establish(int (*func)(void *), void *arg)
    145 {
    146         gemini_ipi_softc_t *sc = gemini_ipi_sc;
    147 	void *ih;
    148 
    149 	if (sc == NULL)
    150 		return NULL;
    151 
    152 	ih = gemini_ipi_intrq_insert(sc, func, arg);
    153 #ifdef DEBUG
    154         if (ih == NULL)
    155 		panic("%s: gemini_ipi_intrq_insert failed",
    156 			device_xname(&sc->sc_dev));
    157 #endif
    158 
    159 	return ih;
    160 }
    161 
    162 void
    163 ipi_intr_disestablish(void *ih)
    164 {
    165         gemini_ipi_softc_t *sc = gemini_ipi_sc;
    166 
    167 	if (sc == NULL)
    168 		panic("%s: NULL gemini_ipi_sc", device_xname(&sc->sc_dev));
    169 
    170         gemini_ipi_intrq_remove(sc, ih);
    171 }
    172 
    173 int
    174 ipi_send(void)
    175 {
    176         gemini_ipi_softc_t *sc = gemini_ipi_sc;
    177 	uint32_t r;
    178 	uint32_t bit;
    179 	bus_addr_t off;
    180 
    181 	if (sc == NULL)
    182 		return -1;
    183 
    184 #if defined(GEMINI_MASTER)
    185 	off = GEMINI_GLOBAL_CPU0;
    186 	bit = GLOBAL_CPU0_IPICPU1;
    187 #elif defined(GEMINI_SLAVE)
    188 	off = GEMINI_GLOBAL_CPU1;
    189 	bit = GLOBAL_CPU1_IPICPU0;
    190 #endif
    191 
    192 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
    193 	r |= bit;
    194 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
    195 
    196 	return 0;
    197 }
    198 
    199 static inline void
    200 ipi_ack(gemini_ipi_softc_t *sc)
    201 {
    202 	uint32_t r;
    203 	uint32_t bit;
    204 	bus_addr_t off;
    205 
    206 #if defined(GEMINI_MASTER)
    207 	off = GEMINI_GLOBAL_CPU1;
    208 	bit = GLOBAL_CPU1_IPICPU0;
    209 #elif defined(GEMINI_SLAVE)
    210 	off = GEMINI_GLOBAL_CPU0;
    211 	bit = GLOBAL_CPU0_IPICPU1;
    212 #endif
    213 
    214 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
    215 	r &= ~bit;
    216 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
    217 }
    218 
    219 static int
    220 gemini_ipiintr(void *arg)
    221 {
    222 	gemini_ipi_softc_t *sc = arg;
    223 	int rv;
    224 
    225 	if (sc == NULL)
    226 		return -1;
    227 
    228 	ipi_ack(sc);
    229 
    230 	rv = gemini_ipi_intrq_dispatch(sc);
    231 
    232 	return rv;
    233 }
    234