siisata_pci.c revision 1.21
1/* $NetBSD: siisata_pci.c,v 1.21 2021/05/05 19:30:51 jdolecek Exp $ */
2
3/*
4 * Copyright (c) 2006 Manuel Bouyer.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28/*
29 * Copyright (c) 2007, 2008, 2009 Jonathan A. Kollasch.
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
42 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
44 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
45 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
50 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 */
52
53#include <sys/cdefs.h>
54__KERNEL_RCSID(0, "$NetBSD: siisata_pci.c,v 1.21 2021/05/05 19:30:51 jdolecek Exp $");
55
56#include <sys/types.h>
57#include <sys/malloc.h>
58#include <sys/param.h>
59#include <sys/kernel.h>
60#include <sys/systm.h>
61
62#include <dev/pci/pcivar.h>
63#include <dev/pci/pcidevs.h>
64#include <dev/ic/siisatavar.h>
65
66struct siisata_pci_softc {
67	struct siisata_softc si_sc;
68	pci_chipset_tag_t sc_pc;
69	pcitag_t sc_pcitag;
70	pci_intr_handle_t *sc_pihp;
71	void *sc_ih;
72};
73
74static int siisata_pci_match(device_t, cfdata_t, void *);
75static void siisata_pci_attach(device_t, device_t, void *);
76static int siisata_pci_detach(device_t, int);
77static void siisata_pci_childdetached(device_t, device_t);
78static bool siisata_pci_resume(device_t, const pmf_qual_t *);
79
80struct siisata_pci_board {
81	pci_vendor_id_t		spb_vend;
82	pci_product_id_t	spb_prod;
83	uint16_t		spb_port;
84	uint16_t		spb_chip;
85	uint8_t			sbp_flags;
86};
87
88#define SIISATA_BROKEN_MSI		0x01
89
90static const struct siisata_pci_board siisata_pci_boards[] = {
91	{
92		.spb_vend = PCI_VENDOR_CMDTECH,
93		.spb_prod = PCI_PRODUCT_CMDTECH_3124,
94		.spb_port = 4,
95		.spb_chip = 3124,
96		/*
97		 * SiI3124 seems to be PCI/PCI-X chip behind PCI-e bridge,
98		 * claims MSI support but interrups don't work with MSI on.
99		 */
100		.sbp_flags = SIISATA_BROKEN_MSI,
101	},
102	{
103		.spb_vend = PCI_VENDOR_CMDTECH,
104		.spb_prod = PCI_PRODUCT_CMDTECH_3132,
105		.spb_port = 2,
106		.spb_chip = 3132,
107	},
108	{
109		.spb_vend = PCI_VENDOR_CMDTECH,
110		.spb_prod = PCI_PRODUCT_CMDTECH_AAR_1220SA,
111		.spb_port = 2,
112		.spb_chip = 3132,
113	},
114	{
115		.spb_vend = PCI_VENDOR_CMDTECH,
116		.spb_prod = PCI_PRODUCT_CMDTECH_3531,
117		.spb_port = 1,
118		.spb_chip = 3531,
119	},
120};
121
122CFATTACH_DECL3_NEW(siisata_pci, sizeof(struct siisata_pci_softc),
123    siisata_pci_match, siisata_pci_attach, siisata_pci_detach, NULL,
124    NULL, siisata_pci_childdetached, DVF_DETACH_SHUTDOWN);
125
126static const struct siisata_pci_board *
127siisata_pci_lookup(const struct pci_attach_args * pa)
128{
129	int i;
130
131	for (i = 0; i < __arraycount(siisata_pci_boards); i++) {
132		if (siisata_pci_boards[i].spb_vend != PCI_VENDOR(pa->pa_id))
133			continue;
134		if (siisata_pci_boards[i].spb_prod == PCI_PRODUCT(pa->pa_id))
135			return &siisata_pci_boards[i];
136	}
137
138	return NULL;
139}
140
141static int
142siisata_pci_match(device_t parent, cfdata_t match, void *aux)
143{
144	struct pci_attach_args *pa = aux;
145
146	if (siisata_pci_lookup(pa) != NULL)
147		return 3;
148
149	return 0;
150}
151
152static void
153siisata_pci_attach(device_t parent, device_t self, void *aux)
154{
155	struct pci_attach_args *pa = aux;
156	struct siisata_pci_softc *psc = device_private(self);
157	struct siisata_softc *sc = &psc->si_sc;
158	const char *intrstr;
159	pcireg_t csr, memtype;
160	const struct siisata_pci_board *spbp;
161	bus_space_tag_t memt;
162	bus_space_handle_t memh;
163	uint32_t gcreg;
164	int memh_valid;
165	bus_size_t grsize, prsize;
166	char intrbuf[PCI_INTRSTR_LEN];
167
168	spbp = siisata_pci_lookup(pa);
169	KASSERT(spbp != NULL);
170
171	sc->sc_atac.atac_dev = self;
172
173	psc->sc_pc = pa->pa_pc;
174	psc->sc_pcitag = pa->pa_tag;
175
176	pci_aprint_devinfo(pa, "SATA-II HBA");
177
178	/* map BAR 0, global registers */
179	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, SIISATA_PCI_BAR0);
180	switch (memtype) {
181	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
182	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
183		memh_valid = (pci_mapreg_map(pa, SIISATA_PCI_BAR0,
184			memtype, 0, &memt, &memh, NULL, &grsize) == 0);
185		break;
186	default:
187		memh_valid = 0;
188	}
189	if (memh_valid) {
190		sc->sc_grt = memt;
191		sc->sc_grh = memh;
192		sc->sc_grs = grsize;
193	} else {
194		aprint_error_dev(self, "couldn't map global registers\n");
195		return;
196	}
197
198	/* map BAR 1, port registers */
199	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, SIISATA_PCI_BAR1);
200	switch (memtype) {
201	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
202	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
203		memh_valid = (pci_mapreg_map(pa, SIISATA_PCI_BAR1,
204			memtype, 0, &memt, &memh, NULL, &prsize) == 0);
205		break;
206	default:
207		memh_valid = 0;
208	}
209	if (memh_valid) {
210		sc->sc_prt = memt;
211		sc->sc_prh = memh;
212		sc->sc_prs = prsize;
213	} else {
214		bus_space_unmap(sc->sc_grt, sc->sc_grh, grsize);
215		aprint_error_dev(self, "couldn't map port registers\n");
216		return;
217	}
218
219	if (pci_dma64_available(pa))
220		sc->sc_dmat = pa->pa_dmat64;
221	else
222		sc->sc_dmat = pa->pa_dmat;
223
224	int counts[PCI_INTR_TYPE_SIZE] = {
225 		[PCI_INTR_TYPE_INTX] = 1,
226 		[PCI_INTR_TYPE_MSI] = 1,
227 		[PCI_INTR_TYPE_MSIX] = 1,
228 	};
229	int max_type = PCI_INTR_TYPE_MSIX;
230
231	if (spbp->sbp_flags & SIISATA_BROKEN_MSI) {
232		max_type = PCI_INTR_TYPE_INTX;
233	}
234
235	/* map interrupt */
236	if (pci_intr_alloc(pa, &psc->sc_pihp, counts, max_type) != 0) {
237		bus_space_unmap(sc->sc_grt, sc->sc_grh, grsize);
238		bus_space_unmap(sc->sc_prt, sc->sc_prh, prsize);
239		aprint_error_dev(self, "couldn't map interrupt\n");
240		return;
241	}
242	intrstr = pci_intr_string(pa->pa_pc, psc->sc_pihp[0], intrbuf,
243	    sizeof(intrbuf));
244	psc->sc_ih = pci_intr_establish_xname(pa->pa_pc, psc->sc_pihp[0],
245	    IPL_BIO, siisata_intr, sc, device_xname(self));
246	if (psc->sc_ih == NULL) {
247		pci_intr_release(psc->sc_pc, psc->sc_pihp, 1);
248		psc->sc_pihp = NULL;
249
250		bus_space_unmap(sc->sc_grt, sc->sc_grh, grsize);
251		bus_space_unmap(sc->sc_prt, sc->sc_prh, prsize);
252		aprint_error_dev(self, "couldn't establish interrupt at %s\n",
253			intrstr);
254		return;
255	}
256	aprint_normal_dev(self, "interrupting at %s\n",
257		intrstr ? intrstr : "unknown interrupt");
258
259	/* fill in number of ports on this device */
260	sc->sc_atac.atac_nchannels = spbp->spb_port;
261
262	/* set the necessary bits in case the firmware didn't */
263	csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
264	csr |= PCI_COMMAND_MASTER_ENABLE;
265	csr |= PCI_COMMAND_MEM_ENABLE;
266	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr);
267
268	gcreg = GRREAD(sc, GR_GC);
269
270	aprint_verbose_dev(self, "SiI%d, %sGb/s\n",
271		spbp->spb_chip, (gcreg & GR_GC_3GBPS) ? "3.0" : "1.5" );
272	if (spbp->spb_chip == 3124) {
273		short width;
274		short speed;
275		char pcix = 1;
276
277		width = (gcreg & GR_GC_REQ64) ? 64 : 32;
278
279		switch (gcreg & (GR_GC_DEVSEL | GR_GC_STOP | GR_GC_TRDY)) {
280		case 0:
281			speed = (gcreg & GR_GC_M66EN) ? 66 : 33;
282			pcix = 0;
283			break;
284		case GR_GC_TRDY:
285			speed = 66;
286			break;
287		case GR_GC_STOP:
288			speed = 100;
289			break;
290		case GR_GC_STOP | GR_GC_TRDY:
291			speed = 133;
292			break;
293		default:
294			speed = -1;
295			break;
296		}
297		aprint_verbose_dev(self, "%hd-bit %hdMHz PCI%s\n",
298			width, speed, pcix ? "-X" : "");
299	}
300
301	siisata_attach(sc);
302
303	if (!pmf_device_register(self, NULL, siisata_pci_resume))
304		aprint_error_dev(self, "couldn't establish power handler\n");
305}
306
307static int
308siisata_pci_detach(device_t dv, int flags)
309{
310	struct siisata_pci_softc *psc = device_private(dv);
311	struct siisata_softc *sc = &psc->si_sc;
312	int rv;
313
314	rv = siisata_detach(sc, flags);
315	if (rv)
316		return rv;
317
318	if (psc->sc_ih != NULL) {
319		pci_intr_disestablish(psc->sc_pc, psc->sc_ih);
320		psc->sc_ih = NULL;
321	}
322
323	if (psc->sc_pihp != NULL) {
324		pci_intr_release(psc->sc_pc, psc->sc_pihp, 1);
325		psc->sc_pihp = NULL;
326	}
327
328	bus_space_unmap(sc->sc_prt, sc->sc_prh, sc->sc_prs);
329	bus_space_unmap(sc->sc_grt, sc->sc_grh, sc->sc_grs);
330
331	return 0;
332}
333
334static void
335siisata_pci_childdetached(device_t dv, device_t child)
336{
337	struct siisata_pci_softc *psc = device_private(dv);
338	struct siisata_softc *sc = &psc->si_sc;
339
340	siisata_childdetached(sc, child);
341}
342
343static bool
344siisata_pci_resume(device_t dv, const pmf_qual_t *qual)
345{
346	struct siisata_pci_softc *psc = device_private(dv);
347	struct siisata_softc *sc = &psc->si_sc;
348	int s;
349
350	s = splbio();
351	siisata_resume(sc);
352	splx(s);
353
354	return true;
355}
356