1*38fdb085Sthorpej /* $NetBSD: gemini_lpchc.c,v 1.5 2020/11/20 18:10:07 thorpej Exp $ */
22842dccfScliff
32842dccfScliff /*
42842dccfScliff * GEMINI LPC Host Controller
52842dccfScliff */
62842dccfScliff #include "opt_gemini.h"
72842dccfScliff #include "locators.h"
82842dccfScliff
92842dccfScliff #include <sys/cdefs.h>
10*38fdb085Sthorpej __KERNEL_RCSID(0, "$NetBSD: gemini_lpchc.c,v 1.5 2020/11/20 18:10:07 thorpej Exp $");
112842dccfScliff
122842dccfScliff #include <sys/param.h>
132842dccfScliff #include <sys/callout.h>
142842dccfScliff #include <sys/cdefs.h>
152842dccfScliff #include <sys/device.h>
162842dccfScliff #include <sys/kernel.h>
172842dccfScliff #include <sys/systm.h>
18*38fdb085Sthorpej #include <sys/kmem.h>
192842dccfScliff
20cf10107dSdyoung #include <sys/bus.h>
212842dccfScliff
222842dccfScliff #include <arm/gemini/gemini_lpchcvar.h>
232842dccfScliff #include <arm/gemini/gemini_lpcvar.h>
242842dccfScliff #include <arm/gemini/gemini_reg.h>
252842dccfScliff
262842dccfScliff static inline void
gemini_lpchc_sirq_cfg(bus_space_tag_t iot,bus_space_handle_t ioh,bus_size_t offset,uint32_t bit,boolean_t doset)272842dccfScliff gemini_lpchc_sirq_cfg(bus_space_tag_t iot, bus_space_handle_t ioh,
282842dccfScliff bus_size_t offset, uint32_t bit, boolean_t doset)
292842dccfScliff {
302842dccfScliff uint32_t r;
312842dccfScliff
322842dccfScliff r = bus_space_read_4(iot, ioh, offset);
332842dccfScliff if (doset)
342842dccfScliff r |= bit;
352842dccfScliff else
362842dccfScliff r &= ~bit;
372842dccfScliff bus_space_write_4(iot, ioh, offset, r);
382842dccfScliff }
392842dccfScliff
402842dccfScliff static inline void
gemini_lpchc_sirq_ack(bus_space_tag_t iot,bus_space_handle_t ioh,uint32_t bit)412842dccfScliff gemini_lpchc_sirq_ack(bus_space_tag_t iot, bus_space_handle_t ioh,
422842dccfScliff uint32_t bit)
432842dccfScliff {
442842dccfScliff uint32_t r;
452842dccfScliff
462842dccfScliff r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_SERIRQSTS);
472842dccfScliff r &= bit;
482842dccfScliff if (r != 0)
492842dccfScliff bus_space_write_4(iot, ioh, GEMINI_LPCHC_SERIRQSTS, r);
502842dccfScliff }
512842dccfScliff
522842dccfScliff static inline void
gemini_lpchc_sirq_disable(bus_space_tag_t iot,bus_space_handle_t ioh)532842dccfScliff gemini_lpchc_sirq_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
542842dccfScliff {
552842dccfScliff uint32_t r;
562842dccfScliff
572842dccfScliff r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_IRQCTL);
582842dccfScliff r &= ~LPCHC_IRQCTL_SIRQEN;
592842dccfScliff bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r);
602842dccfScliff }
612842dccfScliff
622842dccfScliff static inline void
gemini_lpchc_sirq_enable(bus_space_tag_t iot,bus_space_handle_t ioh)632842dccfScliff gemini_lpchc_sirq_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
642842dccfScliff {
652842dccfScliff uint32_t r;
662842dccfScliff
672842dccfScliff r = bus_space_read_4(iot, ioh, GEMINI_LPCHC_IRQCTL);
682842dccfScliff r |= LPCHC_IRQCTL_SIRQEN;
692842dccfScliff r |= LPCHC_IRQCTL_SIRQMS; /* XXX "continuous mode" */
702842dccfScliff r |= IRQCTL_SIRQFW_8; /* XXX SIRW Frame Width 8 clocks */
712842dccfScliff bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r);
722842dccfScliff #if 0
732842dccfScliff delay(10); /* wait 1 serial IRQ cycle */
742842dccfScliff r &= ~LPCHC_IRQCTL_SIRQMS; /* XXX "quiet mode" */
752842dccfScliff bus_space_write_4(iot, ioh, GEMINI_LPCHC_IRQCTL, r);
762842dccfScliff #endif
772842dccfScliff }
782842dccfScliff
792842dccfScliff static inline void
gemini_lpchc_intrq_init(gemini_lpchc_softc_t * sc)802842dccfScliff gemini_lpchc_intrq_init(gemini_lpchc_softc_t *sc)
812842dccfScliff {
822842dccfScliff SIMPLEQ_INIT(&sc->sc_intrq);
832842dccfScliff }
842842dccfScliff
852842dccfScliff static inline int
gemini_lpchc_intrq_empty(gemini_lpchc_softc_t * sc)862842dccfScliff gemini_lpchc_intrq_empty(gemini_lpchc_softc_t *sc)
872842dccfScliff {
882842dccfScliff return SIMPLEQ_EMPTY(&sc->sc_intrq);
892842dccfScliff }
902842dccfScliff
912842dccfScliff static inline void *
gemini_lpchc_intrq_insert(gemini_lpchc_softc_t * sc,int (* func)(void *),void * arg,uint32_t bit,boolean_t isedge)922842dccfScliff gemini_lpchc_intrq_insert(gemini_lpchc_softc_t *sc, int (*func)(void *),
932842dccfScliff void *arg, uint32_t bit, boolean_t isedge)
942842dccfScliff {
952842dccfScliff gemini_lpchc_intrq_t *iqp;
962842dccfScliff
97*38fdb085Sthorpej iqp = kmem_zalloc(sizeof(*iqp), KM_SLEEP);
982842dccfScliff iqp->iq_func = func;
992842dccfScliff iqp->iq_arg = arg;
1002842dccfScliff iqp->iq_bit = bit;
1012842dccfScliff iqp->iq_isedge = isedge;
1022842dccfScliff SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q);
1032842dccfScliff
1042842dccfScliff return (void *)iqp;
1052842dccfScliff }
1062842dccfScliff
1072842dccfScliff static inline void
gemini_lpchc_intrq_remove(gemini_lpchc_softc_t * sc,void * cookie)1082842dccfScliff gemini_lpchc_intrq_remove(gemini_lpchc_softc_t *sc, void *cookie)
1092842dccfScliff {
1102842dccfScliff gemini_lpchc_intrq_t *iqp;
1112842dccfScliff
1122842dccfScliff SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
1132842dccfScliff if ((void *)iqp == cookie) {
1142842dccfScliff SIMPLEQ_REMOVE(&sc->sc_intrq,
1152842dccfScliff iqp, gemini_lpchc_intrq, iq_q);
116*38fdb085Sthorpej kmem_free(iqp, sizeof(*iqp));
1172842dccfScliff return;
1182842dccfScliff }
1192842dccfScliff }
1202842dccfScliff }
1212842dccfScliff
1222842dccfScliff static inline int
gemini_lpchc_intrq_dispatch(gemini_lpchc_softc_t * sc)1232842dccfScliff gemini_lpchc_intrq_dispatch(gemini_lpchc_softc_t *sc)
1242842dccfScliff {
1252842dccfScliff gemini_lpchc_intrq_t *iqp;
1262842dccfScliff uint32_t r;
1272842dccfScliff int rv = 0;
1282842dccfScliff
1292842dccfScliff r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_SERIRQSTS);
1302842dccfScliff r &= ~LPCHC_SERIRQSTS_RESV;
1312842dccfScliff SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
1322842dccfScliff if ((r & iqp->iq_bit) != 0) {
1332842dccfScliff if (iqp->iq_isedge) {
1342842dccfScliff gemini_lpchc_sirq_ack(sc->sc_iot, sc->sc_ioh,
1352842dccfScliff iqp->iq_bit);
1362842dccfScliff }
1372842dccfScliff rv |= (*iqp->iq_func)(iqp->iq_arg);
1382842dccfScliff }
1392842dccfScliff }
1402842dccfScliff return (rv != 0);
1412842dccfScliff }
1422842dccfScliff
1432842dccfScliff void
gemini_lpchc_init(lpcintrtag_t tag)1442842dccfScliff gemini_lpchc_init(lpcintrtag_t tag)
1452842dccfScliff {
1462842dccfScliff gemini_lpchc_softc_t *sc = tag;
1472842dccfScliff uint32_t r;
1482842dccfScliff
1492842dccfScliff gemini_lpchc_intrq_init(sc);
1502842dccfScliff
1512842dccfScliff r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR);
1522842dccfScliff r |= LPCHC_CSR_BEN;
1532842dccfScliff bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_LPCHC_CSR, r);
1542842dccfScliff }
1552842dccfScliff
1562842dccfScliff void *
gemini_lpchc_intr_establish(lpcintrtag_t tag,uint irq,int ipl,int type,int (* func)(void *),void * arg)1572842dccfScliff gemini_lpchc_intr_establish(lpcintrtag_t tag, uint irq,
1582842dccfScliff int ipl, int type, int (*func)(void *), void *arg)
1592842dccfScliff {
1602842dccfScliff gemini_lpchc_softc_t *sc = tag;
1612842dccfScliff bus_space_tag_t iot = sc->sc_iot;
1622842dccfScliff bus_space_handle_t ioh = sc->sc_ioh;
1632842dccfScliff uint32_t bit;
1642842dccfScliff boolean_t isedge;
1652842dccfScliff boolean_t ishigh;
1662842dccfScliff void *ih;
1672842dccfScliff
1682842dccfScliff isedge = ((type == IST_EDGE_RISING) || (type == IST_EDGE_FALLING));
1692842dccfScliff ishigh = ((type == IST_EDGE_RISING) || (type == IST_LEVEL_HIGH));
1702842dccfScliff
1712842dccfScliff if (irq >= GEMINI_LPCHC_NSERIRQ) {
1722842dccfScliff printf("%s: bad irq %d\n", __FUNCTION__, irq);
1732842dccfScliff return NULL;
1742842dccfScliff }
1752842dccfScliff #if 0
1762842dccfScliff bit = 1 << irq;
1772842dccfScliff #else
1782842dccfScliff bit = (1 << GEMINI_LPCHC_NSERIRQ) -1; /* XXX */
1792842dccfScliff #endif
1802842dccfScliff
1812842dccfScliff /* set IRQ type */
1822842dccfScliff gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQTYP,
1832842dccfScliff bit, isedge);
1842842dccfScliff
1852842dccfScliff /* set IRQ polarity */
1862842dccfScliff gemini_lpchc_sirq_cfg(iot, ioh, GEMINI_LPCHC_SERIRQPOLARITY,
1872842dccfScliff bit, ishigh);
1882842dccfScliff
1892842dccfScliff /* ack a-priori edge status */
1902842dccfScliff if (isedge)
1912842dccfScliff gemini_lpchc_sirq_ack(iot, ioh, bit);
1922842dccfScliff
1932842dccfScliff if (gemini_lpchc_intrq_empty(sc))
1942842dccfScliff gemini_lpchc_sirq_enable(iot, ioh);
1952842dccfScliff
1962842dccfScliff ih = gemini_lpchc_intrq_insert(sc, func, arg, bit, isedge);
1972842dccfScliff return ih;
1982842dccfScliff }
1992842dccfScliff
2002842dccfScliff void
gemini_lpchc_intr_disestablish(lpcintrtag_t tag,void * ih)2012842dccfScliff gemini_lpchc_intr_disestablish(lpcintrtag_t tag, void *ih)
2022842dccfScliff {
2032842dccfScliff gemini_lpchc_softc_t *sc = tag;
2042842dccfScliff
2052842dccfScliff gemini_lpchc_intrq_remove(sc, ih);
2062842dccfScliff if (gemini_lpchc_intrq_empty(sc))
2072842dccfScliff gemini_lpchc_sirq_disable(sc->sc_iot, sc->sc_ioh);
2082842dccfScliff }
2092842dccfScliff
2102842dccfScliff int
gemini_lpchc_intr(void * arg)2112842dccfScliff gemini_lpchc_intr(void *arg)
2122842dccfScliff {
2132842dccfScliff gemini_lpchc_softc_t *sc = arg;
2142842dccfScliff int rv;
2152842dccfScliff
2162842dccfScliff printf("%s: enter\n", __FUNCTION__);
2172842dccfScliff rv = gemini_lpchc_intrq_dispatch(sc);
2182842dccfScliff printf("%s: exit\n", __FUNCTION__);
2192842dccfScliff
2202842dccfScliff return rv;
2212842dccfScliff }
222