xref: /netbsd-src/sys/arch/arm/gemini/gemini_lpchc.c (revision 38fdb0855d7c611ea7875ffde7af0ddd65823fc4)
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