/* $NetBSD: rtsx_pci.c,v 1.8.4.1 2020/04/28 16:26:44 martin Exp $ */ /* $OpenBSD: rtsx_pci.c,v 1.7 2014/08/19 17:55:03 phessler Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler * Copyright (c) 2012 Stefan Sperling * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __KERNEL_RCSID(0, "$NetBSD: rtsx_pci.c,v 1.8.4.1 2020/04/28 16:26:44 martin Exp $"); #include #include #include #include #include #include #include #include #include #include #define RTSX_PCI_BAR 0x10 #define RTSX_PCI_BAR_525A 0x14 struct rtsx_pci_softc { struct rtsx_softc sc; pci_chipset_tag_t sc_pc; void *sc_ih; pci_intr_handle_t *sc_pihp; }; static int rtsx_pci_match(device_t , cfdata_t, void *); static void rtsx_pci_attach(device_t, device_t, void *); static int rtsx_pci_detach(device_t, int); CFATTACH_DECL_NEW(rtsx_pci, sizeof(struct rtsx_pci_softc), rtsx_pci_match, rtsx_pci_attach, rtsx_pci_detach, NULL); #ifdef RTSX_DEBUG extern int rtsxdebug; #define DPRINTF(n,s) do { if ((n) <= rtsxdebug) printf s; } while (0) #else #define DPRINTF(n,s) /**/ #endif static int rtsx_pci_match(device_t parent, cfdata_t cf, void *aux) { struct pci_attach_args *pa = aux; /* * Explicitly match the UNDEFINED device class only. Some RTS5902 * devices advertise a SYSTEM/SDHC class in addition to the UNDEFINED * device class. Let sdhc(4) handle the SYSTEM/SDHC ones. */ if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_REALTEK || PCI_CLASS(pa->pa_class) != PCI_CLASS_UNDEFINED) return 0; switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_REALTEK_RTS5209: case PCI_PRODUCT_REALTEK_RTS5227: case PCI_PRODUCT_REALTEK_RTS5229: case PCI_PRODUCT_REALTEK_RTS522A: case PCI_PRODUCT_REALTEK_RTS525A: case PCI_PRODUCT_REALTEK_RTL8402: case PCI_PRODUCT_REALTEK_RTL8411: case PCI_PRODUCT_REALTEK_RTL8411B: return 1; } return 0; } static void rtsx_pci_attach(device_t parent, device_t self, void *aux) { struct rtsx_pci_softc *sc = device_private(self); struct pci_attach_args *pa = (struct pci_attach_args *)aux; pci_chipset_tag_t pc = pa->pa_pc; pcitag_t tag = pa->pa_tag; pcireg_t reg; char const *intrstr; bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t size; uint32_t flags; int bar = RTSX_PCI_BAR; char intrbuf[PCI_INTRSTR_LEN]; sc->sc.sc_dev = self; sc->sc_pc = pc; switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_REALTEK_RTS5209: flags = RTSX_F_5209; break; case PCI_PRODUCT_REALTEK_RTS5227: flags = RTSX_F_5227; break; case PCI_PRODUCT_REALTEK_RTS5229: flags = RTSX_F_5229; break; case PCI_PRODUCT_REALTEK_RTS525A: flags = RTSX_F_525A; bar = RTSX_PCI_BAR_525A; break; case PCI_PRODUCT_REALTEK_RTL8402: flags = RTSX_F_8402; break; case PCI_PRODUCT_REALTEK_RTL8411: flags = RTSX_F_8411; break; case PCI_PRODUCT_REALTEK_RTL8411B: flags = RTSX_F_8411B; break; default: flags = 0; break; } pci_aprint_devinfo(pa, NULL); if ((pci_conf_read(pc, tag, RTSX_CFG_PCI) & RTSX_CFG_ASIC) != 0) { aprint_error_dev(self, "no asic\n"); return; } if (pci_mapreg_map(pa, bar, PCI_MAPREG_TYPE_MEM, 0, &iot, &ioh, NULL, &size)) { aprint_error_dev(self, "couldn't map registers\n"); return; } if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) { aprint_error_dev(self, "couldn't map interrupt\n"); return; } intrstr = pci_intr_string(pc, sc->sc_pihp[0], intrbuf, sizeof(intrbuf)); sc->sc_ih = pci_intr_establish_xname(pc, sc->sc_pihp[0], IPL_SDMMC, rtsx_intr, &sc->sc, device_xname(self)); if (sc->sc_ih == NULL) { aprint_error_dev(self, "couldn't establish interrupt\n"); return; } aprint_normal_dev(self, "interrupting at %s\n", intrstr); /* Enable the device */ reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); reg |= PCI_COMMAND_MASTER_ENABLE; pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, reg); /* Power up the device */ pci_set_powerstate(pc, tag, PCI_PMCSR_STATE_D0); if (rtsx_attach(&sc->sc, iot, ioh, size, pa->pa_dmat, flags) != 0) { aprint_error_dev(self, "couldn't initialize chip\n"); return; } if (!pmf_device_register1(self, rtsx_suspend, rtsx_resume, rtsx_shutdown)) aprint_error_dev(self, "couldn't establish powerhook\n"); } static int rtsx_pci_detach(device_t self, int flags) { struct rtsx_pci_softc *sc = device_private(self); int rv; rv = rtsx_detach(&sc->sc, flags); if (rv) return rv; pci_intr_disestablish(sc->sc_pc, sc->sc_ih); pci_intr_release(sc->sc_pc, sc->sc_pihp, 1); return 0; }