xref: /openbsd/sys/dev/pci/com_pci.c (revision 0f9891f1)
1*0f9891f1Sjsg /* $OpenBSD: com_pci.c,v 1.4 2024/05/24 06:02:53 jsg Exp $ */
26dedc15dSpatrick /*
36dedc15dSpatrick  * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se>
46dedc15dSpatrick  *
56dedc15dSpatrick  * Permission to use, copy, modify, and distribute this software for any
66dedc15dSpatrick  * purpose with or without fee is hereby granted, provided that the above
76dedc15dSpatrick  * copyright notice and this permission notice appear in all copies.
86dedc15dSpatrick  *
96dedc15dSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106dedc15dSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116dedc15dSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126dedc15dSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136dedc15dSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146dedc15dSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156dedc15dSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166dedc15dSpatrick  */
176dedc15dSpatrick 
186dedc15dSpatrick #include <sys/param.h>
196dedc15dSpatrick #include <sys/systm.h>
206dedc15dSpatrick #include <sys/tty.h>
216dedc15dSpatrick 
226dedc15dSpatrick #include <dev/pci/pcidevs.h>
236dedc15dSpatrick #include <dev/pci/pcireg.h>
246dedc15dSpatrick #include <dev/pci/pcivar.h>
256dedc15dSpatrick 
266dedc15dSpatrick #include <dev/ic/comreg.h>
276dedc15dSpatrick #include <dev/ic/comvar.h>
286dedc15dSpatrick 
296dedc15dSpatrick #define com_usr 31	/* Synopsys DesignWare UART */
306dedc15dSpatrick 
316dedc15dSpatrick /* Intel Low Power Subsystem */
326dedc15dSpatrick #define LPSS_CLK		0x200
336dedc15dSpatrick #define  LPSS_CLK_GATE			(1 << 0)
346dedc15dSpatrick #define  LPSS_CLK_MDIV_SHIFT		1
356dedc15dSpatrick #define  LPSS_CLK_MDIV_MASK		0x3fff
366dedc15dSpatrick #define  LPSS_CLK_NDIV_SHIFT		16
376dedc15dSpatrick #define  LPSS_CLK_NDIV_MASK		0x3fff
386dedc15dSpatrick #define  LPSS_CLK_UPDATE		(1U << 31)
396dedc15dSpatrick #define LPSS_RESETS		0x204
406dedc15dSpatrick #define  LPSS_RESETS_FUNC		(3 << 0)
416dedc15dSpatrick #define  LPSS_RESETS_IDMA		(1 << 2)
426dedc15dSpatrick #define LPSS_ACTIVELTR		0x210
436dedc15dSpatrick #define LPSS_IDLELTR		0x214
446dedc15dSpatrick #define  LPSS_LTR_VALUE_MASK		(0x3ff << 0)
456dedc15dSpatrick #define  LPSS_LTR_SCALE_MASK		(0x3 << 10)
466dedc15dSpatrick #define  LPSS_LTR_SCALE_1US		(2 << 10)
476dedc15dSpatrick #define  LPSS_LTR_SCALE_32US		(3 << 10)
486dedc15dSpatrick #define  LPSS_LTR_REQ			(1 << 15)
496dedc15dSpatrick #define LPSS_SSP		0x220
506dedc15dSpatrick #define  LPSS_SSP_DIS_DMA_FIN		(1 << 0)
516dedc15dSpatrick #define LPSS_REMAP_ADDR		0x240
526dedc15dSpatrick #define LPSS_CAPS		0x2fc
536dedc15dSpatrick #define  LPSS_CAPS_TYPE_I2C		(0 << 4)
546dedc15dSpatrick #define  LPSS_CAPS_TYPE_UART		(1 << 4)
556dedc15dSpatrick #define  LPSS_CAPS_TYPE_SPI		(2 << 4)
566dedc15dSpatrick #define  LPSS_CAPS_TYPE_MASK		(0xf << 4)
576dedc15dSpatrick #define  LPSS_CAPS_NO_IDMA		(1 << 8)
586dedc15dSpatrick 
596dedc15dSpatrick #define LPSS_REG_OFF		0x200
606dedc15dSpatrick #define LPSS_REG_SIZE		0x100
616dedc15dSpatrick #define LPSS_REG_NUM		(LPSS_REG_SIZE / sizeof(uint32_t))
626dedc15dSpatrick 
636dedc15dSpatrick #define HREAD4(sc, reg)							\
646dedc15dSpatrick 	(bus_space_read_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg)))
656dedc15dSpatrick #define HWRITE4(sc, reg, val)						\
666dedc15dSpatrick 	bus_space_write_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg), (val))
676dedc15dSpatrick #define HSET4(sc, reg, bits)						\
686dedc15dSpatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
696dedc15dSpatrick #define HCLR4(sc, reg, bits)						\
706dedc15dSpatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
716dedc15dSpatrick 
726dedc15dSpatrick int	com_pci_match(struct device *, void *, void *);
736dedc15dSpatrick void	com_pci_attach(struct device *, struct device *, void *);
746dedc15dSpatrick int	com_pci_detach(struct device *, int);
756dedc15dSpatrick int	com_pci_activate(struct device *, int);
766dedc15dSpatrick int	com_pci_intr_designware(void *);
776dedc15dSpatrick 
786dedc15dSpatrick struct com_pci_softc {
796dedc15dSpatrick 	struct com_softc	 sc;
806dedc15dSpatrick 	pci_chipset_tag_t	 sc_pc;
816dedc15dSpatrick 	pcireg_t		 sc_id;
826dedc15dSpatrick 
836dedc15dSpatrick 	bus_size_t		 sc_ios;
846dedc15dSpatrick 	void			*sc_ih;
856dedc15dSpatrick 
866dedc15dSpatrick 	uint32_t		 sc_priv[LPSS_REG_NUM];
876dedc15dSpatrick };
886dedc15dSpatrick 
89471aeecfSnaddy const struct cfattach com_pci_ca = {
906dedc15dSpatrick 	sizeof(struct com_pci_softc), com_pci_match,
916dedc15dSpatrick 	com_pci_attach, com_pci_detach, com_pci_activate,
926dedc15dSpatrick };
936dedc15dSpatrick 
946dedc15dSpatrick const struct pci_matchid com_pci_ids[] = {
956dedc15dSpatrick 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_1 },
966dedc15dSpatrick 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_2 },
976dedc15dSpatrick 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_3 },
986dedc15dSpatrick 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_4 },
996dedc15dSpatrick };
1006dedc15dSpatrick 
1016dedc15dSpatrick int
com_pci_match(struct device * parent,void * match,void * aux)1026dedc15dSpatrick com_pci_match(struct device *parent, void *match, void *aux)
1036dedc15dSpatrick {
1046dedc15dSpatrick 	return (pci_matchbyid(aux, com_pci_ids, nitems(com_pci_ids)));
1056dedc15dSpatrick }
1066dedc15dSpatrick 
1076dedc15dSpatrick void
com_pci_attach(struct device * parent,struct device * self,void * aux)1086dedc15dSpatrick com_pci_attach(struct device *parent, struct device *self, void *aux)
1096dedc15dSpatrick {
1106dedc15dSpatrick 	struct com_pci_softc *sc = (struct com_pci_softc *)self;
1116dedc15dSpatrick 	struct pci_attach_args *pa = aux;
1126dedc15dSpatrick 	pci_intr_handle_t ih;
1136dedc15dSpatrick 	const char *intrstr;
1146dedc15dSpatrick 	uint64_t freq, m, n;
1156dedc15dSpatrick 	uint32_t caps;
1166dedc15dSpatrick 
1176dedc15dSpatrick 	sc->sc_pc = pa->pa_pc;
1186dedc15dSpatrick 	sc->sc_id = pa->pa_id;
1196dedc15dSpatrick 	sc->sc.sc_frequency = COM_FREQ;
1206dedc15dSpatrick 	sc->sc.sc_uarttype = COM_UART_16550;
1216dedc15dSpatrick 	sc->sc.sc_reg_width = 4;
1226dedc15dSpatrick 	sc->sc.sc_reg_shift = 2;
1236dedc15dSpatrick 
1246dedc15dSpatrick 	pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
1256dedc15dSpatrick 
1266dedc15dSpatrick 	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT, 0,
1276dedc15dSpatrick 	    &sc->sc.sc_iot, &sc->sc.sc_ioh, &sc->sc.sc_iobase, &sc->sc_ios, 0)) {
1286dedc15dSpatrick 		printf(": can't map mem space\n");
1296dedc15dSpatrick 		return;
1306dedc15dSpatrick 	}
1316dedc15dSpatrick 
1326dedc15dSpatrick 	/*
1336dedc15dSpatrick 	 * Once we are adding non-Intel and non-LPSS device it will make
1346dedc15dSpatrick 	 * sense to add a second table and use pci_matchbyid().
1356dedc15dSpatrick 	 */
1366dedc15dSpatrick 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
1376dedc15dSpatrick 		caps = HREAD4(sc, LPSS_CAPS);
1386dedc15dSpatrick 		if ((caps & LPSS_CAPS_TYPE_MASK) != LPSS_CAPS_TYPE_UART) {
1396dedc15dSpatrick 			bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh,
1406dedc15dSpatrick 			    sc->sc_ios);
1416dedc15dSpatrick 			printf(": not a UART\n");
1426dedc15dSpatrick 			return;
1436dedc15dSpatrick 		}
1446dedc15dSpatrick 
1456dedc15dSpatrick 		HWRITE4(sc, LPSS_RESETS, 0);
1466dedc15dSpatrick 		HWRITE4(sc, LPSS_RESETS, LPSS_RESETS_FUNC | LPSS_RESETS_IDMA);
1476dedc15dSpatrick 		HWRITE4(sc, LPSS_REMAP_ADDR, sc->sc.sc_iobase);
1486dedc15dSpatrick 
1496dedc15dSpatrick 		/* 100 MHz base clock */
1506dedc15dSpatrick 		freq = 100 * 1000 * 1000;
1516dedc15dSpatrick 		m = n = HREAD4(sc, LPSS_CLK);
1526dedc15dSpatrick 		m = (m >> LPSS_CLK_MDIV_SHIFT) & LPSS_CLK_MDIV_MASK;
1536dedc15dSpatrick 		n = (n >> LPSS_CLK_NDIV_SHIFT) & LPSS_CLK_NDIV_MASK;
1546dedc15dSpatrick 		if (m && n) {
1556dedc15dSpatrick 			freq *= m;
1566dedc15dSpatrick 			freq /= n;
1576dedc15dSpatrick 		}
1586dedc15dSpatrick 		sc->sc.sc_frequency = freq;
1596dedc15dSpatrick 	}
1606dedc15dSpatrick 
1616dedc15dSpatrick 	if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) {
1626dedc15dSpatrick 		bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios);
1636dedc15dSpatrick 		printf(": unable to map interrupt\n");
1646dedc15dSpatrick 		return;
1656dedc15dSpatrick 	}
1666dedc15dSpatrick 
1676dedc15dSpatrick 	intrstr = pci_intr_string(pa->pa_pc, ih);
1686dedc15dSpatrick 	sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY,
1696dedc15dSpatrick 	    com_pci_intr_designware, &sc->sc, sc->sc.sc_dev.dv_xname);
1706dedc15dSpatrick 	if (sc->sc_ih == NULL) {
1716dedc15dSpatrick 		bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios);
1726dedc15dSpatrick 		printf(": can't establish interrupt");
1736dedc15dSpatrick 		if (intrstr != NULL)
1746dedc15dSpatrick 			printf(" at %s", intrstr);
1756dedc15dSpatrick 		printf("\n");
1766dedc15dSpatrick 		return;
1776dedc15dSpatrick 	}
1786dedc15dSpatrick 
1796dedc15dSpatrick 	com_attach_subr(&sc->sc);
1806dedc15dSpatrick }
1816dedc15dSpatrick 
1826dedc15dSpatrick int
com_pci_detach(struct device * self,int flags)1836dedc15dSpatrick com_pci_detach(struct device *self, int flags)
1846dedc15dSpatrick {
1856dedc15dSpatrick 	struct com_pci_softc *sc = (struct com_pci_softc *)self;
1866dedc15dSpatrick 	int rv;
1876dedc15dSpatrick 
1886dedc15dSpatrick 	rv = com_detach(self, flags);
1896dedc15dSpatrick 	if (rv != 0)
1906dedc15dSpatrick 		return (rv);
1916dedc15dSpatrick 	if (sc->sc_ih != NULL) {
1926dedc15dSpatrick 		pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
1936dedc15dSpatrick 		sc->sc_ih = NULL;
1946dedc15dSpatrick 	}
1956dedc15dSpatrick 	if (sc->sc_ios != 0) {
1966dedc15dSpatrick 		bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios);
1976dedc15dSpatrick 		sc->sc_ios = 0;
1986dedc15dSpatrick 	}
1996dedc15dSpatrick 
2006dedc15dSpatrick 	return (rv);
2016dedc15dSpatrick }
2026dedc15dSpatrick 
2036dedc15dSpatrick int
com_pci_activate(struct device * self,int act)2046dedc15dSpatrick com_pci_activate(struct device *self, int act)
2056dedc15dSpatrick {
2066dedc15dSpatrick 	struct com_pci_softc *sc = (struct com_pci_softc *)self;
2076dedc15dSpatrick 	int i, rv = 0;
2086dedc15dSpatrick 
2096dedc15dSpatrick 	if (PCI_VENDOR(sc->sc_id) != PCI_VENDOR_INTEL)
2106dedc15dSpatrick 		return com_activate(self, act);
2116dedc15dSpatrick 
2126dedc15dSpatrick 	switch (act) {
2136dedc15dSpatrick 	case DVACT_RESUME:
2146dedc15dSpatrick 		for (i = 0; i < LPSS_REG_NUM; i++)
2156dedc15dSpatrick 			HWRITE4(sc, i * sizeof(uint32_t), sc->sc_priv[i]);
2166dedc15dSpatrick 		rv = com_activate(self, act);
2176dedc15dSpatrick 		break;
2186dedc15dSpatrick 	case DVACT_SUSPEND:
2196dedc15dSpatrick 		rv = com_activate(self, act);
2206dedc15dSpatrick 		for (i = 0; i < LPSS_REG_NUM; i++)
2216dedc15dSpatrick 			sc->sc_priv[i] = HREAD4(sc, i * sizeof(uint32_t));
2226dedc15dSpatrick 		break;
2236dedc15dSpatrick 	default:
2246dedc15dSpatrick 		rv = com_activate(self, act);
2256dedc15dSpatrick 		break;
2266dedc15dSpatrick 	}
2276dedc15dSpatrick 
2286dedc15dSpatrick 	return (rv);
2296dedc15dSpatrick }
2306dedc15dSpatrick 
2316dedc15dSpatrick int
com_pci_intr_designware(void * cookie)2326dedc15dSpatrick com_pci_intr_designware(void *cookie)
2336dedc15dSpatrick {
2346dedc15dSpatrick 	struct com_softc *sc = cookie;
2356dedc15dSpatrick 
2366dedc15dSpatrick 	com_read_reg(sc, com_usr);
2376dedc15dSpatrick 
2386dedc15dSpatrick 	return comintr(sc);
2396dedc15dSpatrick }
240