11.23Sthorpej/* $NetBSD: siisata_pci.c,v 1.23 2022/09/25 17:52:25 thorpej Exp $ */
21.1Sjnemeth
31.1Sjnemeth/*
41.1Sjnemeth * Copyright (c) 2006 Manuel Bouyer.
51.1Sjnemeth *
61.1Sjnemeth * Redistribution and use in source and binary forms, with or without
71.1Sjnemeth * modification, are permitted provided that the following conditions
81.1Sjnemeth * are met:
91.1Sjnemeth * 1. Redistributions of source code must retain the above copyright
101.1Sjnemeth *    notice, this list of conditions and the following disclaimer.
111.1Sjnemeth * 2. Redistributions in binary form must reproduce the above copyright
121.1Sjnemeth *    notice, this list of conditions and the following disclaimer in the
131.1Sjnemeth *    documentation and/or other materials provided with the distribution.
141.1Sjnemeth *
151.1Sjnemeth * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
161.1Sjnemeth * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
171.1Sjnemeth * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
181.1Sjnemeth * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
191.1Sjnemeth * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
201.1Sjnemeth * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
211.1Sjnemeth * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
221.1Sjnemeth * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
231.1Sjnemeth * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
241.1Sjnemeth * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251.1Sjnemeth *
261.1Sjnemeth */
271.1Sjnemeth
281.7Sjakllsch/*
291.4Sjakllsch * Copyright (c) 2007, 2008, 2009 Jonathan A. Kollasch.
301.1Sjnemeth * All rights reserved.
311.1Sjnemeth *
321.1Sjnemeth * Redistribution and use in source and binary forms, with or without
331.1Sjnemeth * modification, are permitted provided that the following conditions
341.1Sjnemeth * are met:
351.1Sjnemeth * 1. Redistributions of source code must retain the above copyright
361.1Sjnemeth *    notice, this list of conditions and the following disclaimer.
371.1Sjnemeth * 2. Redistributions in binary form must reproduce the above copyright
381.1Sjnemeth *    notice, this list of conditions and the following disclaimer in the
391.1Sjnemeth *    documentation and/or other materials provided with the distribution.
401.1Sjnemeth *
411.1Sjnemeth * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
421.1Sjnemeth * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
431.1Sjnemeth * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
441.1Sjnemeth * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
451.1Sjnemeth * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
461.1Sjnemeth * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
471.1Sjnemeth * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
481.1Sjnemeth * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
491.1Sjnemeth * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
501.1Sjnemeth * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
511.1Sjnemeth */
521.1Sjnemeth
531.1Sjnemeth#include <sys/cdefs.h>
541.23Sthorpej__KERNEL_RCSID(0, "$NetBSD: siisata_pci.c,v 1.23 2022/09/25 17:52:25 thorpej Exp $");
551.1Sjnemeth
561.1Sjnemeth#include <sys/types.h>
571.1Sjnemeth#include <sys/param.h>
581.1Sjnemeth#include <sys/kernel.h>
591.1Sjnemeth#include <sys/systm.h>
601.1Sjnemeth
611.1Sjnemeth#include <dev/pci/pcivar.h>
621.1Sjnemeth#include <dev/pci/pcidevs.h>
631.1Sjnemeth#include <dev/ic/siisatavar.h>
641.1Sjnemeth
651.1Sjnemethstruct siisata_pci_softc {
661.1Sjnemeth	struct siisata_softc si_sc;
671.1Sjnemeth	pci_chipset_tag_t sc_pc;
681.1Sjnemeth	pcitag_t sc_pcitag;
691.17Sjdolecek	pci_intr_handle_t *sc_pihp;
701.17Sjdolecek	void *sc_ih;
711.1Sjnemeth};
721.1Sjnemeth
731.1Sjnemethstatic int siisata_pci_match(device_t, cfdata_t, void *);
741.1Sjnemethstatic void siisata_pci_attach(device_t, device_t, void *);
751.3Sjakllschstatic int siisata_pci_detach(device_t, int);
761.19Sjdolecekstatic void siisata_pci_childdetached(device_t, device_t);
771.8Sdyoungstatic bool siisata_pci_resume(device_t, const pmf_qual_t *);
781.1Sjnemeth
791.4Sjakllschstruct siisata_pci_board {
801.4Sjakllsch	pci_vendor_id_t		spb_vend;
811.4Sjakllsch	pci_product_id_t	spb_prod;
821.4Sjakllsch	uint16_t		spb_port;
831.4Sjakllsch	uint16_t		spb_chip;
841.21Sjdolecek	uint8_t			sbp_flags;
851.4Sjakllsch};
861.1Sjnemeth
871.21Sjdolecek#define SIISATA_BROKEN_MSI		0x01
881.21Sjdolecek
891.4Sjakllschstatic const struct siisata_pci_board siisata_pci_boards[] = {
901.1Sjnemeth	{
911.4Sjakllsch		.spb_vend = PCI_VENDOR_CMDTECH,
921.4Sjakllsch		.spb_prod = PCI_PRODUCT_CMDTECH_3124,
931.4Sjakllsch		.spb_port = 4,
941.4Sjakllsch		.spb_chip = 3124,
951.21Sjdolecek		/*
961.21Sjdolecek		 * SiI3124 seems to be PCI/PCI-X chip behind PCI-e bridge,
971.22Sandvar		 * claims MSI support but interrupts don't work with MSI on.
981.21Sjdolecek		 */
991.21Sjdolecek		.sbp_flags = SIISATA_BROKEN_MSI,
1001.1Sjnemeth	},
1011.1Sjnemeth	{
1021.4Sjakllsch		.spb_vend = PCI_VENDOR_CMDTECH,
1031.4Sjakllsch		.spb_prod = PCI_PRODUCT_CMDTECH_3132,
1041.14Smsaitoh		.spb_port = 2,
1051.4Sjakllsch		.spb_chip = 3132,
1061.1Sjnemeth	},
1071.1Sjnemeth	{
1081.4Sjakllsch		.spb_vend = PCI_VENDOR_CMDTECH,
1091.15Sjdolecek		.spb_prod = PCI_PRODUCT_CMDTECH_AAR_1220SA,
1101.15Sjdolecek		.spb_port = 2,
1111.15Sjdolecek		.spb_chip = 3132,
1121.15Sjdolecek	},
1131.15Sjdolecek	{
1141.15Sjdolecek		.spb_vend = PCI_VENDOR_CMDTECH,
1151.4Sjakllsch		.spb_prod = PCI_PRODUCT_CMDTECH_3531,
1161.4Sjakllsch		.spb_port = 1,
1171.4Sjakllsch		.spb_chip = 3531,
1181.1Sjnemeth	},
1191.1Sjnemeth};
1201.1Sjnemeth
1211.19SjdolecekCFATTACH_DECL3_NEW(siisata_pci, sizeof(struct siisata_pci_softc),
1221.19Sjdolecek    siisata_pci_match, siisata_pci_attach, siisata_pci_detach, NULL,
1231.19Sjdolecek    NULL, siisata_pci_childdetached, DVF_DETACH_SHUTDOWN);
1241.1Sjnemeth
1251.4Sjakllschstatic const struct siisata_pci_board *
1261.1Sjnemethsiisata_pci_lookup(const struct pci_attach_args * pa)
1271.1Sjnemeth{
1281.4Sjakllsch	int i;
1291.1Sjnemeth
1301.4Sjakllsch	for (i = 0; i < __arraycount(siisata_pci_boards); i++) {
1311.4Sjakllsch		if (siisata_pci_boards[i].spb_vend != PCI_VENDOR(pa->pa_id))
1321.4Sjakllsch			continue;
1331.4Sjakllsch		if (siisata_pci_boards[i].spb_prod == PCI_PRODUCT(pa->pa_id))
1341.4Sjakllsch			return &siisata_pci_boards[i];
1351.1Sjnemeth	}
1361.4Sjakllsch
1371.1Sjnemeth	return NULL;
1381.1Sjnemeth}
1391.1Sjnemeth
1401.1Sjnemethstatic int
1411.1Sjnemethsiisata_pci_match(device_t parent, cfdata_t match, void *aux)
1421.1Sjnemeth{
1431.1Sjnemeth	struct pci_attach_args *pa = aux;
1441.1Sjnemeth
1451.1Sjnemeth	if (siisata_pci_lookup(pa) != NULL)
1461.1Sjnemeth		return 3;
1471.1Sjnemeth
1481.1Sjnemeth	return 0;
1491.1Sjnemeth}
1501.1Sjnemeth
1511.1Sjnemethstatic void
1521.1Sjnemethsiisata_pci_attach(device_t parent, device_t self, void *aux)
1531.1Sjnemeth{
1541.1Sjnemeth	struct pci_attach_args *pa = aux;
1551.1Sjnemeth	struct siisata_pci_softc *psc = device_private(self);
1561.1Sjnemeth	struct siisata_softc *sc = &psc->si_sc;
1571.1Sjnemeth	const char *intrstr;
1581.1Sjnemeth	pcireg_t csr, memtype;
1591.4Sjakllsch	const struct siisata_pci_board *spbp;
1601.1Sjnemeth	bus_space_tag_t memt;
1611.1Sjnemeth	bus_space_handle_t memh;
1621.1Sjnemeth	uint32_t gcreg;
1631.1Sjnemeth	int memh_valid;
1641.1Sjnemeth	bus_size_t grsize, prsize;
1651.13Schristos	char intrbuf[PCI_INTRSTR_LEN];
1661.1Sjnemeth
1671.21Sjdolecek	spbp = siisata_pci_lookup(pa);
1681.21Sjdolecek	KASSERT(spbp != NULL);
1691.21Sjdolecek
1701.1Sjnemeth	sc->sc_atac.atac_dev = self;
1711.14Smsaitoh
1721.1Sjnemeth	psc->sc_pc = pa->pa_pc;
1731.1Sjnemeth	psc->sc_pcitag = pa->pa_tag;
1741.1Sjnemeth
1751.10Sdrochner	pci_aprint_devinfo(pa, "SATA-II HBA");
1761.1Sjnemeth
1771.4Sjakllsch	/* map BAR 0, global registers */
1781.1Sjnemeth	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, SIISATA_PCI_BAR0);
1791.1Sjnemeth	switch (memtype) {
1801.1Sjnemeth	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
1811.1Sjnemeth	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
1821.1Sjnemeth		memh_valid = (pci_mapreg_map(pa, SIISATA_PCI_BAR0,
1831.1Sjnemeth			memtype, 0, &memt, &memh, NULL, &grsize) == 0);
1841.1Sjnemeth		break;
1851.1Sjnemeth	default:
1861.1Sjnemeth		memh_valid = 0;
1871.1Sjnemeth	}
1881.1Sjnemeth	if (memh_valid) {
1891.1Sjnemeth		sc->sc_grt = memt;
1901.1Sjnemeth		sc->sc_grh = memh;
1911.3Sjakllsch		sc->sc_grs = grsize;
1921.1Sjnemeth	} else {
1931.4Sjakllsch		aprint_error_dev(self, "couldn't map global registers\n");
1941.1Sjnemeth		return;
1951.1Sjnemeth	}
1961.1Sjnemeth
1971.4Sjakllsch	/* map BAR 1, port registers */
1981.1Sjnemeth	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, SIISATA_PCI_BAR1);
1991.1Sjnemeth	switch (memtype) {
2001.1Sjnemeth	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
2011.1Sjnemeth	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
2021.1Sjnemeth		memh_valid = (pci_mapreg_map(pa, SIISATA_PCI_BAR1,
2031.1Sjnemeth			memtype, 0, &memt, &memh, NULL, &prsize) == 0);
2041.1Sjnemeth		break;
2051.1Sjnemeth	default:
2061.1Sjnemeth		memh_valid = 0;
2071.1Sjnemeth	}
2081.1Sjnemeth	if (memh_valid) {
2091.1Sjnemeth		sc->sc_prt = memt;
2101.1Sjnemeth		sc->sc_prh = memh;
2111.3Sjakllsch		sc->sc_prs = prsize;
2121.1Sjnemeth	} else {
2131.1Sjnemeth		bus_space_unmap(sc->sc_grt, sc->sc_grh, grsize);
2141.4Sjakllsch		aprint_error_dev(self, "couldn't map port registers\n");
2151.1Sjnemeth		return;
2161.1Sjnemeth	}
2171.1Sjnemeth
2181.4Sjakllsch	if (pci_dma64_available(pa))
2191.1Sjnemeth		sc->sc_dmat = pa->pa_dmat64;
2201.4Sjakllsch	else
2211.1Sjnemeth		sc->sc_dmat = pa->pa_dmat;
2221.1Sjnemeth
2231.21Sjdolecek	int counts[PCI_INTR_TYPE_SIZE] = {
2241.21Sjdolecek 		[PCI_INTR_TYPE_INTX] = 1,
2251.21Sjdolecek 		[PCI_INTR_TYPE_MSI] = 1,
2261.21Sjdolecek 		[PCI_INTR_TYPE_MSIX] = 1,
2271.21Sjdolecek 	};
2281.21Sjdolecek	int max_type = PCI_INTR_TYPE_MSIX;
2291.21Sjdolecek
2301.21Sjdolecek	if (spbp->sbp_flags & SIISATA_BROKEN_MSI) {
2311.21Sjdolecek		max_type = PCI_INTR_TYPE_INTX;
2321.21Sjdolecek	}
2331.21Sjdolecek
2341.1Sjnemeth	/* map interrupt */
2351.21Sjdolecek	if (pci_intr_alloc(pa, &psc->sc_pihp, counts, max_type) != 0) {
2361.1Sjnemeth		bus_space_unmap(sc->sc_grt, sc->sc_grh, grsize);
2371.1Sjnemeth		bus_space_unmap(sc->sc_prt, sc->sc_prh, prsize);
2381.4Sjakllsch		aprint_error_dev(self, "couldn't map interrupt\n");
2391.1Sjnemeth		return;
2401.1Sjnemeth	}
2411.17Sjdolecek	intrstr = pci_intr_string(pa->pa_pc, psc->sc_pihp[0], intrbuf,
2421.14Smsaitoh	    sizeof(intrbuf));
2431.17Sjdolecek	psc->sc_ih = pci_intr_establish_xname(pa->pa_pc, psc->sc_pihp[0],
2441.16Sjdolecek	    IPL_BIO, siisata_intr, sc, device_xname(self));
2451.3Sjakllsch	if (psc->sc_ih == NULL) {
2461.20Sjdolecek		pci_intr_release(psc->sc_pc, psc->sc_pihp, 1);
2471.20Sjdolecek		psc->sc_pihp = NULL;
2481.20Sjdolecek
2491.1Sjnemeth		bus_space_unmap(sc->sc_grt, sc->sc_grh, grsize);
2501.1Sjnemeth		bus_space_unmap(sc->sc_prt, sc->sc_prh, prsize);
2511.4Sjakllsch		aprint_error_dev(self, "couldn't establish interrupt at %s\n",
2521.4Sjakllsch			intrstr);
2531.1Sjnemeth		return;
2541.1Sjnemeth	}
2551.4Sjakllsch	aprint_normal_dev(self, "interrupting at %s\n",
2561.4Sjakllsch		intrstr ? intrstr : "unknown interrupt");
2571.1Sjnemeth
2581.1Sjnemeth	/* fill in number of ports on this device */
2591.4Sjakllsch	sc->sc_atac.atac_nchannels = spbp->spb_port;
2601.1Sjnemeth
2611.2Sjakllsch	/* set the necessary bits in case the firmware didn't */
2621.2Sjakllsch	csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
2631.2Sjakllsch	csr |= PCI_COMMAND_MASTER_ENABLE;
2641.2Sjakllsch	csr |= PCI_COMMAND_MEM_ENABLE;
2651.2Sjakllsch	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr);
2661.2Sjakllsch
2671.1Sjnemeth	gcreg = GRREAD(sc, GR_GC);
2681.1Sjnemeth
2691.4Sjakllsch	aprint_verbose_dev(self, "SiI%d, %sGb/s\n",
2701.4Sjakllsch		spbp->spb_chip, (gcreg & GR_GC_3GBPS) ? "3.0" : "1.5" );
2711.4Sjakllsch	if (spbp->spb_chip == 3124) {
2721.4Sjakllsch		short width;
2731.4Sjakllsch		short speed;
2741.4Sjakllsch		char pcix = 1;
2751.4Sjakllsch
2761.4Sjakllsch		width = (gcreg & GR_GC_REQ64) ? 64 : 32;
2771.4Sjakllsch
2781.1Sjnemeth		switch (gcreg & (GR_GC_DEVSEL | GR_GC_STOP | GR_GC_TRDY)) {
2791.1Sjnemeth		case 0:
2801.4Sjakllsch			speed = (gcreg & GR_GC_M66EN) ? 66 : 33;
2811.4Sjakllsch			pcix = 0;
2821.1Sjnemeth			break;
2831.1Sjnemeth		case GR_GC_TRDY:
2841.4Sjakllsch			speed = 66;
2851.1Sjnemeth			break;
2861.1Sjnemeth		case GR_GC_STOP:
2871.4Sjakllsch			speed = 100;
2881.1Sjnemeth			break;
2891.1Sjnemeth		case GR_GC_STOP | GR_GC_TRDY:
2901.4Sjakllsch			speed = 133;
2911.1Sjnemeth			break;
2921.1Sjnemeth		default:
2931.4Sjakllsch			speed = -1;
2941.1Sjnemeth			break;
2951.1Sjnemeth		}
2961.4Sjakllsch		aprint_verbose_dev(self, "%hd-bit %hdMHz PCI%s\n",
2971.4Sjakllsch			width, speed, pcix ? "-X" : "");
2981.1Sjnemeth	}
2991.1Sjnemeth
3001.1Sjnemeth	siisata_attach(sc);
3011.1Sjnemeth
3021.1Sjnemeth	if (!pmf_device_register(self, NULL, siisata_pci_resume))
3031.1Sjnemeth		aprint_error_dev(self, "couldn't establish power handler\n");
3041.1Sjnemeth}
3051.1Sjnemeth
3061.3Sjakllschstatic int
3071.3Sjakllschsiisata_pci_detach(device_t dv, int flags)
3081.3Sjakllsch{
3091.3Sjakllsch	struct siisata_pci_softc *psc = device_private(dv);
3101.3Sjakllsch	struct siisata_softc *sc = &psc->si_sc;
3111.3Sjakllsch	int rv;
3121.3Sjakllsch
3131.3Sjakllsch	rv = siisata_detach(sc, flags);
3141.3Sjakllsch	if (rv)
3151.3Sjakllsch		return rv;
3161.3Sjakllsch
3171.18Sjdolecek	if (psc->sc_ih != NULL) {
3181.18Sjdolecek		pci_intr_disestablish(psc->sc_pc, psc->sc_ih);
3191.18Sjdolecek		psc->sc_ih = NULL;
3201.18Sjdolecek	}
3211.18Sjdolecek
3221.17Sjdolecek	if (psc->sc_pihp != NULL) {
3231.17Sjdolecek		pci_intr_release(psc->sc_pc, psc->sc_pihp, 1);
3241.17Sjdolecek		psc->sc_pihp = NULL;
3251.17Sjdolecek	}
3261.17Sjdolecek
3271.3Sjakllsch	bus_space_unmap(sc->sc_prt, sc->sc_prh, sc->sc_prs);
3281.3Sjakllsch	bus_space_unmap(sc->sc_grt, sc->sc_grh, sc->sc_grs);
3291.14Smsaitoh
3301.3Sjakllsch	return 0;
3311.3Sjakllsch}
3321.3Sjakllsch
3331.19Sjdolecekstatic void
3341.19Sjdoleceksiisata_pci_childdetached(device_t dv, device_t child)
3351.19Sjdolecek{
3361.19Sjdolecek	struct siisata_pci_softc *psc = device_private(dv);
3371.19Sjdolecek	struct siisata_softc *sc = &psc->si_sc;
3381.19Sjdolecek
3391.19Sjdolecek	siisata_childdetached(sc, child);
3401.19Sjdolecek}
3411.19Sjdolecek
3421.3Sjakllschstatic bool
3431.8Sdyoungsiisata_pci_resume(device_t dv, const pmf_qual_t *qual)
3441.3Sjakllsch{
3451.3Sjakllsch	struct siisata_pci_softc *psc = device_private(dv);
3461.3Sjakllsch	struct siisata_softc *sc = &psc->si_sc;
3471.3Sjakllsch	int s;
3481.3Sjakllsch
3491.3Sjakllsch	s = splbio();
3501.3Sjakllsch	siisata_resume(sc);
3511.3Sjakllsch	splx(s);
3521.14Smsaitoh
3531.3Sjakllsch	return true;
3541.3Sjakllsch}
355