Home | History | Annotate | Line # | Download | only in gemini
gemini_lpchc.c revision 1.2
      1 /*	$NetBSD: gemini_lpchc.c,v 1.2 2011/07/01 19:32:28 dyoung Exp $	*/
      2 
      3 /*
      4  * GEMINI LPC Host Controller
      5  */
      6 #include "opt_gemini.h"
      7 #include "locators.h"
      8 
      9 #include <sys/cdefs.h>
     10 __KERNEL_RCSID(0, "$NetBSD: gemini_lpchc.c,v 1.2 2011/07/01 19:32:28 dyoung Exp $");
     11 
     12 #include <sys/param.h>
     13 #include <sys/callout.h>
     14 #include <sys/cdefs.h>
     15 #include <sys/device.h>
     16 #include <sys/kernel.h>
     17 #include <sys/systm.h>
     18 #include <sys/malloc.h>
     19 
     20 #include <machine/param.h>
     21 #include <sys/bus.h>
     22 
     23 #include <arm/gemini/gemini_lpchcvar.h>
     24 #include <arm/gemini/gemini_lpcvar.h>
     25 #include <arm/gemini/gemini_reg.h>
     26 
     27 static inline void
     28 gemini_lpchc_sirq_cfg(bus_space_tag_t iot, bus_space_handle_t ioh,
     29 	bus_size_t offset, uint32_t bit, boolean_t doset)
     30 {
     31 	uint32_t r;
     32 
     33 	r = bus_space_read_4(iot, ioh, offset);
     34 	if (doset)
     35 		r |= bit;
     36 	else
     37 		r &= ~bit;
     38 	bus_space_write_4(iot, ioh, offset, r);
     39 }
     40 
     41 static inline void
     42 gemini_lpchc_sirq_ack(bus_space_tag_t iot, bus_space_handle_t ioh,
     43 	uint32_t bit)
     44 {
     45 	uint32_t r;
     46 
     47 	r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_SERIRQSTS);
     48 	r &= bit;
     49 	if (r != 0)
     50 		bus_space_write_4(iot, ioh, GEMINI_LPCHC_SERIRQSTS, r);
     51 }
     52 
     53 static inline void
     54 gemini_lpchc_sirq_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
     55 {
     56 	uint32_t r;
     57 
     58 	r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_IRQCTL);
     59 	r &= ~LPCHC_IRQCTL_SIRQEN;
     60 	bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r);
     61 }
     62 
     63 static inline void
     64 gemini_lpchc_sirq_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
     65 {
     66 	uint32_t r;
     67 
     68 	r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_IRQCTL);
     69 	r |= LPCHC_IRQCTL_SIRQEN;
     70 	r |= LPCHC_IRQCTL_SIRQMS;	/* XXX "continuous mode" */
     71 	r |= IRQCTL_SIRQFW_8;		/* XXX SIRW Frame Width 8 clocks */
     72 	bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r);
     73 #if 0
     74 	delay(10);			/* wait 1 serial IRQ cycle */
     75 	r &= ~LPCHC_IRQCTL_SIRQMS;	/* XXX "quiet mode" */
     76 	bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r);
     77 #endif
     78 }
     79 
     80 static inline void
     81 gemini_lpchc_intrq_init(gemini_lpchc_softc_t *sc)
     82 {
     83 	SIMPLEQ_INIT(&sc->sc_intrq);
     84 }
     85 
     86 static inline int
     87 gemini_lpchc_intrq_empty(gemini_lpchc_softc_t *sc)
     88 {
     89 	return SIMPLEQ_EMPTY(&sc->sc_intrq);
     90 }
     91 
     92 static inline void *
     93 gemini_lpchc_intrq_insert(gemini_lpchc_softc_t *sc, int (*func)(void *),
     94 	void *arg, uint32_t bit, boolean_t isedge)
     95 {
     96 	gemini_lpchc_intrq_t *iqp;
     97 
     98         iqp = malloc(sizeof(*iqp), M_DEVBUF, M_NOWAIT|M_ZERO);
     99         if (iqp == NULL) {
    100 		printf("gemini_lpchc_intrq_insert: malloc failed\n");
    101 		return NULL;
    102 	}
    103 
    104         iqp->iq_func = func;
    105         iqp->iq_arg = arg;
    106         iqp->iq_bit = bit;
    107         iqp->iq_isedge = isedge;
    108         SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q);
    109 
    110 	return (void *)iqp;
    111 }
    112 
    113 static inline void
    114 gemini_lpchc_intrq_remove(gemini_lpchc_softc_t *sc, void *cookie)
    115 {
    116 	gemini_lpchc_intrq_t *iqp;
    117 
    118 	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
    119 		if ((void *)iqp == cookie) {
    120 			SIMPLEQ_REMOVE(&sc->sc_intrq,
    121 				iqp, gemini_lpchc_intrq, iq_q);
    122 			free(iqp, M_DEVBUF);
    123 			return;
    124 		}
    125 	}
    126 }
    127 
    128 static inline int
    129 gemini_lpchc_intrq_dispatch(gemini_lpchc_softc_t *sc)
    130 {
    131 	gemini_lpchc_intrq_t *iqp;
    132 	uint32_t r;
    133 	int rv = 0;
    134 
    135 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_SERIRQSTS);
    136 	r &= ~LPCHC_SERIRQSTS_RESV;
    137 	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
    138 		if ((r & iqp->iq_bit) != 0) {
    139 			if (iqp->iq_isedge) {
    140 				gemini_lpchc_sirq_ack(sc->sc_iot, sc->sc_ioh,
    141 					iqp->iq_bit);
    142 			}
    143 			rv |= (*iqp->iq_func)(iqp->iq_arg);
    144 		}
    145 	}
    146 	return (rv != 0);
    147 }
    148 
    149 void
    150 gemini_lpchc_init(lpcintrtag_t tag)
    151 {
    152 	gemini_lpchc_softc_t *sc = tag;
    153 	uint32_t r;
    154 
    155 	gemini_lpchc_intrq_init(sc);
    156 
    157 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR);
    158 	r |= LPCHC_CSR_BEN;
    159 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR, r);
    160 }
    161 
    162 void *
    163 gemini_lpchc_intr_establish(lpcintrtag_t tag, uint irq,
    164 	int ipl, int type, int (*func)(void *), void *arg)
    165 {
    166 	gemini_lpchc_softc_t *sc = tag;
    167 	bus_space_tag_t iot = sc->sc_iot;
    168 	bus_space_handle_t ioh = sc->sc_ioh;
    169 	uint32_t bit;
    170 	boolean_t isedge;
    171 	boolean_t ishigh;
    172 	void *ih;
    173 
    174 	isedge = ((type == IST_EDGE_RISING) || (type == IST_EDGE_FALLING));
    175 	ishigh = ((type == IST_EDGE_RISING) || (type == IST_LEVEL_HIGH));
    176 
    177 	if (irq >= GEMINI_LPCHC_NSERIRQ) {
    178 		printf("%s: bad irq %d\n", __FUNCTION__, irq);
    179 		return NULL;
    180 	}
    181 #if 0
    182 	bit = 1 << irq;
    183 #else
    184 	bit = (1 << GEMINI_LPCHC_NSERIRQ) -1;	/* XXX */
    185 #endif
    186 
    187 	/* set IRQ type */
    188 	gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQTYP,
    189 		bit, isedge);
    190 
    191 	/* set IRQ polarity */
    192 	gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQPOLARITY,
    193 		bit, ishigh);
    194 
    195 	/* ack a-priori edge status */
    196 	if (isedge)
    197 		gemini_lpchc_sirq_ack(iot, ioh, bit);
    198 
    199 	if (gemini_lpchc_intrq_empty(sc))
    200 		gemini_lpchc_sirq_enable(iot, ioh);
    201 
    202 	ih = gemini_lpchc_intrq_insert(sc, func, arg, bit, isedge);
    203 	if (ih == NULL)
    204 		if (gemini_lpchc_intrq_empty(sc))
    205 			gemini_lpchc_sirq_disable(iot, ioh);
    206 
    207 	return ih;
    208 }
    209 
    210 void
    211 gemini_lpchc_intr_disestablish(lpcintrtag_t tag, void *ih)
    212 {
    213 	gemini_lpchc_softc_t *sc = tag;
    214 
    215 	gemini_lpchc_intrq_remove(sc, ih);
    216 	if (gemini_lpchc_intrq_empty(sc))
    217 		gemini_lpchc_sirq_disable(sc->sc_iot, sc->sc_ioh);
    218 }
    219 
    220 int
    221 gemini_lpchc_intr(void *arg)
    222 {
    223 	gemini_lpchc_softc_t *sc = arg;
    224 	int rv;
    225 
    226 printf("%s: enter\n", __FUNCTION__);
    227 	rv = gemini_lpchc_intrq_dispatch(sc);
    228 printf("%s: exit\n", __FUNCTION__);
    229 
    230 	return rv;
    231 }
    232 
    233