xref: /openbsd/sys/dev/pci/com_pci.c (revision e5dd7070)
1 /* $OpenBSD: com_pci.c,v 1.1 2020/03/06 08:39:34 patrick Exp $ */
2 /*
3  * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/kernel.h>
21 #include <sys/selinfo.h>
22 #include <sys/tty.h>
23 
24 #include <dev/pci/pcidevs.h>
25 #include <dev/pci/pcireg.h>
26 #include <dev/pci/pcivar.h>
27 
28 #include <dev/ic/comreg.h>
29 #include <dev/ic/comvar.h>
30 #include <dev/ic/ns16550reg.h>
31 
32 #define com_usr 31	/* Synopsys DesignWare UART */
33 
34 /* Intel Low Power Subsystem */
35 #define LPSS_CLK		0x200
36 #define  LPSS_CLK_GATE			(1 << 0)
37 #define  LPSS_CLK_MDIV_SHIFT		1
38 #define  LPSS_CLK_MDIV_MASK		0x3fff
39 #define  LPSS_CLK_NDIV_SHIFT		16
40 #define  LPSS_CLK_NDIV_MASK		0x3fff
41 #define  LPSS_CLK_UPDATE		(1U << 31)
42 #define LPSS_RESETS		0x204
43 #define  LPSS_RESETS_FUNC		(3 << 0)
44 #define  LPSS_RESETS_IDMA		(1 << 2)
45 #define LPSS_ACTIVELTR		0x210
46 #define LPSS_IDLELTR		0x214
47 #define  LPSS_LTR_VALUE_MASK		(0x3ff << 0)
48 #define  LPSS_LTR_SCALE_MASK		(0x3 << 10)
49 #define  LPSS_LTR_SCALE_1US		(2 << 10)
50 #define  LPSS_LTR_SCALE_32US		(3 << 10)
51 #define  LPSS_LTR_REQ			(1 << 15)
52 #define LPSS_SSP		0x220
53 #define  LPSS_SSP_DIS_DMA_FIN		(1 << 0)
54 #define LPSS_REMAP_ADDR		0x240
55 #define LPSS_CAPS		0x2fc
56 #define  LPSS_CAPS_TYPE_I2C		(0 << 4)
57 #define  LPSS_CAPS_TYPE_UART		(1 << 4)
58 #define  LPSS_CAPS_TYPE_SPI		(2 << 4)
59 #define  LPSS_CAPS_TYPE_MASK		(0xf << 4)
60 #define  LPSS_CAPS_NO_IDMA		(1 << 8)
61 
62 #define LPSS_REG_OFF		0x200
63 #define LPSS_REG_SIZE		0x100
64 #define LPSS_REG_NUM		(LPSS_REG_SIZE / sizeof(uint32_t))
65 
66 #define HREAD4(sc, reg)							\
67 	(bus_space_read_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg)))
68 #define HWRITE4(sc, reg, val)						\
69 	bus_space_write_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg), (val))
70 #define HSET4(sc, reg, bits)						\
71 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
72 #define HCLR4(sc, reg, bits)						\
73 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
74 
75 int	com_pci_match(struct device *, void *, void *);
76 void	com_pci_attach(struct device *, struct device *, void *);
77 int	com_pci_detach(struct device *, int);
78 int	com_pci_activate(struct device *, int);
79 int	com_pci_intr_designware(void *);
80 
81 struct com_pci_softc {
82 	struct com_softc	 sc;
83 	pci_chipset_tag_t	 sc_pc;
84 	pcireg_t		 sc_id;
85 
86 	bus_size_t		 sc_ios;
87 	void			*sc_ih;
88 
89 	uint32_t		 sc_priv[LPSS_REG_NUM];
90 };
91 
92 struct cfattach com_pci_ca = {
93 	sizeof(struct com_pci_softc), com_pci_match,
94 	com_pci_attach, com_pci_detach, com_pci_activate,
95 };
96 
97 const struct pci_matchid com_pci_ids[] = {
98 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_1 },
99 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_2 },
100 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_3 },
101 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_APOLLOLAKE_UART_4 },
102 };
103 
104 int
105 com_pci_match(struct device *parent, void *match, void *aux)
106 {
107 	return (pci_matchbyid(aux, com_pci_ids, nitems(com_pci_ids)));
108 }
109 
110 void
111 com_pci_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct com_pci_softc *sc = (struct com_pci_softc *)self;
114 	struct pci_attach_args *pa = aux;
115 	pci_intr_handle_t ih;
116 	const char *intrstr;
117 	uint64_t freq, m, n;
118 	uint32_t caps;
119 
120 	sc->sc_pc = pa->pa_pc;
121 	sc->sc_id = pa->pa_id;
122 	sc->sc.sc_frequency = COM_FREQ;
123 	sc->sc.sc_uarttype = COM_UART_16550;
124 	sc->sc.sc_reg_width = 4;
125 	sc->sc.sc_reg_shift = 2;
126 
127 	pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
128 
129 	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT, 0,
130 	    &sc->sc.sc_iot, &sc->sc.sc_ioh, &sc->sc.sc_iobase, &sc->sc_ios, 0)) {
131 		printf(": can't map mem space\n");
132 		return;
133 	}
134 
135 	/*
136 	 * Once we are adding non-Intel and non-LPSS device it will make
137 	 * sense to add a second table and use pci_matchbyid().
138 	 */
139 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
140 		caps = HREAD4(sc, LPSS_CAPS);
141 		if ((caps & LPSS_CAPS_TYPE_MASK) != LPSS_CAPS_TYPE_UART) {
142 			bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh,
143 			    sc->sc_ios);
144 			printf(": not a UART\n");
145 			return;
146 		}
147 
148 		HWRITE4(sc, LPSS_RESETS, 0);
149 		HWRITE4(sc, LPSS_RESETS, LPSS_RESETS_FUNC | LPSS_RESETS_IDMA);
150 		HWRITE4(sc, LPSS_REMAP_ADDR, sc->sc.sc_iobase);
151 
152 		/* 100 MHz base clock */
153 		freq = 100 * 1000 * 1000;
154 		m = n = HREAD4(sc, LPSS_CLK);
155 		m = (m >> LPSS_CLK_MDIV_SHIFT) & LPSS_CLK_MDIV_MASK;
156 		n = (n >> LPSS_CLK_NDIV_SHIFT) & LPSS_CLK_NDIV_MASK;
157 		if (m && n) {
158 			freq *= m;
159 			freq /= n;
160 		}
161 		sc->sc.sc_frequency = freq;
162 	}
163 
164 	if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) {
165 		bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios);
166 		printf(": unable to map interrupt\n");
167 		return;
168 	}
169 
170 	intrstr = pci_intr_string(pa->pa_pc, ih);
171 	sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY,
172 	    com_pci_intr_designware, &sc->sc, sc->sc.sc_dev.dv_xname);
173 	if (sc->sc_ih == NULL) {
174 		bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios);
175 		printf(": can't establish interrupt");
176 		if (intrstr != NULL)
177 			printf(" at %s", intrstr);
178 		printf("\n");
179 		return;
180 	}
181 
182 	com_attach_subr(&sc->sc);
183 }
184 
185 int
186 com_pci_detach(struct device *self, int flags)
187 {
188 	struct com_pci_softc *sc = (struct com_pci_softc *)self;
189 	int rv;
190 
191 	rv = com_detach(self, flags);
192 	if (rv != 0)
193 		return (rv);
194 	if (sc->sc_ih != NULL) {
195 		pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
196 		sc->sc_ih = NULL;
197 	}
198 	if (sc->sc_ios != 0) {
199 		bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh, sc->sc_ios);
200 		sc->sc_ios = 0;
201 	}
202 
203 	return (rv);
204 }
205 
206 int
207 com_pci_activate(struct device *self, int act)
208 {
209 	struct com_pci_softc *sc = (struct com_pci_softc *)self;
210 	int i, rv = 0;
211 
212 	if (PCI_VENDOR(sc->sc_id) != PCI_VENDOR_INTEL)
213 		return com_activate(self, act);
214 
215 	switch (act) {
216 	case DVACT_RESUME:
217 		for (i = 0; i < LPSS_REG_NUM; i++)
218 			HWRITE4(sc, i * sizeof(uint32_t), sc->sc_priv[i]);
219 		rv = com_activate(self, act);
220 		break;
221 	case DVACT_SUSPEND:
222 		rv = com_activate(self, act);
223 		for (i = 0; i < LPSS_REG_NUM; i++)
224 			sc->sc_priv[i] = HREAD4(sc, i * sizeof(uint32_t));
225 		break;
226 	default:
227 		rv = com_activate(self, act);
228 		break;
229 	}
230 
231 	return (rv);
232 }
233 
234 int
235 com_pci_intr_designware(void *cookie)
236 {
237 	struct com_softc *sc = cookie;
238 
239 	com_read_reg(sc, com_usr);
240 
241 	return comintr(sc);
242 }
243