rtsx_pci.c revision 1.9
1/*	$NetBSD: rtsx_pci.c,v 1.9 2020/04/27 23:06:34 jmcneill Exp $	*/
2/*	$OpenBSD: rtsx_pci.c,v 1.7 2014/08/19 17:55:03 phessler Exp $	*/
3
4
5/*
6 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
7 * Copyright (c) 2012 Stefan Sperling <stsp@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22#include <sys/cdefs.h>
23__KERNEL_RCSID(0, "$NetBSD: rtsx_pci.c,v 1.9 2020/04/27 23:06:34 jmcneill Exp $");
24
25#include <sys/param.h>
26#include <sys/device.h>
27#include <sys/systm.h>
28#include <sys/malloc.h>
29#include <sys/pmf.h>
30
31#include <dev/pci/pcivar.h>
32#include <dev/pci/pcidevs.h>
33
34#include <dev/ic/rtsxreg.h>
35#include <dev/ic/rtsxvar.h>
36
37#include <dev/sdmmc/sdmmcvar.h>
38
39#define RTSX_PCI_BAR		0x10
40#define RTSX_PCI_BAR_525A	0x14
41
42struct rtsx_pci_softc {
43	struct rtsx_softc sc;
44	pci_chipset_tag_t sc_pc;
45	void *sc_ih;
46
47	pci_intr_handle_t *sc_pihp;
48};
49
50static int rtsx_pci_match(device_t , cfdata_t, void *);
51static void rtsx_pci_attach(device_t, device_t, void *);
52static int rtsx_pci_detach(device_t, int);
53
54CFATTACH_DECL_NEW(rtsx_pci, sizeof(struct rtsx_pci_softc),
55    rtsx_pci_match, rtsx_pci_attach, rtsx_pci_detach, NULL);
56
57#ifdef RTSX_DEBUG
58extern int rtsxdebug;
59#define DPRINTF(n,s)	do { if ((n) <= rtsxdebug) printf s; } while (0)
60#else
61#define DPRINTF(n,s)	/**/
62#endif
63
64static int
65rtsx_pci_match(device_t parent, cfdata_t cf, void *aux)
66{
67	struct pci_attach_args *pa = aux;
68
69	/*
70	 * Explicitly match the UNDEFINED device class only. Some RTS5902
71	 * devices advertise a SYSTEM/SDHC class in addition to the UNDEFINED
72	 * device class. Let sdhc(4) handle the SYSTEM/SDHC ones.
73	 */
74	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_REALTEK ||
75	    PCI_CLASS(pa->pa_class) != PCI_CLASS_UNDEFINED)
76		return 0;
77
78	switch (PCI_PRODUCT(pa->pa_id)) {
79	case PCI_PRODUCT_REALTEK_RTS5209:
80	case PCI_PRODUCT_REALTEK_RTS5227:
81	case PCI_PRODUCT_REALTEK_RTS5229:
82	case PCI_PRODUCT_REALTEK_RTS522A:
83	case PCI_PRODUCT_REALTEK_RTS525A:
84	case PCI_PRODUCT_REALTEK_RTL8402:
85	case PCI_PRODUCT_REALTEK_RTL8411:
86	case PCI_PRODUCT_REALTEK_RTL8411B:
87		return 1;
88	}
89
90	return 0;
91}
92
93static void
94rtsx_pci_attach(device_t parent, device_t self, void *aux)
95{
96	struct rtsx_pci_softc *sc = device_private(self);
97	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
98	pci_chipset_tag_t pc = pa->pa_pc;
99	pcitag_t tag = pa->pa_tag;
100	pcireg_t reg;
101	char const *intrstr;
102	bus_space_tag_t iot;
103	bus_space_handle_t ioh;
104	bus_size_t size;
105	uint32_t flags;
106	int bar = RTSX_PCI_BAR;
107	char intrbuf[PCI_INTRSTR_LEN];
108
109	sc->sc.sc_dev = self;
110	sc->sc_pc = pc;
111
112	switch (PCI_PRODUCT(pa->pa_id)) {
113	case PCI_PRODUCT_REALTEK_RTS5209:
114		flags = RTSX_F_5209;
115		break;
116	case PCI_PRODUCT_REALTEK_RTS5227:
117		flags = RTSX_F_5227;
118		break;
119	case PCI_PRODUCT_REALTEK_RTS5229:
120		flags = RTSX_F_5229;
121		break;
122	case PCI_PRODUCT_REALTEK_RTS525A:
123		flags = RTSX_F_525A;
124		bar = RTSX_PCI_BAR_525A;
125		break;
126	case PCI_PRODUCT_REALTEK_RTL8402:
127		flags = RTSX_F_8402;
128		break;
129	case PCI_PRODUCT_REALTEK_RTL8411:
130		flags = RTSX_F_8411;
131		break;
132	case PCI_PRODUCT_REALTEK_RTL8411B:
133		flags = RTSX_F_8411B;
134		break;
135	default:
136		flags = 0;
137		break;
138	}
139
140	pci_aprint_devinfo(pa, NULL);
141
142	if ((pci_conf_read(pc, tag, RTSX_CFG_PCI) & RTSX_CFG_ASIC) != 0) {
143		aprint_error_dev(self, "no asic\n");
144		return;
145	}
146
147	if (pci_mapreg_map(pa, bar, PCI_MAPREG_TYPE_MEM, 0,
148	    &iot, &ioh, NULL, &size)) {
149		aprint_error_dev(self, "couldn't map registers\n");
150		return;
151	}
152
153	if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0)) {
154		aprint_error_dev(self, "couldn't map interrupt\n");
155		return;
156	}
157	intrstr = pci_intr_string(pc, sc->sc_pihp[0], intrbuf, sizeof(intrbuf));
158	sc->sc_ih = pci_intr_establish_xname(pc, sc->sc_pihp[0], IPL_SDMMC,
159	    rtsx_intr, &sc->sc, device_xname(self));
160	if (sc->sc_ih == NULL) {
161		aprint_error_dev(self, "couldn't establish interrupt\n");
162		return;
163	}
164	aprint_normal_dev(self, "interrupting at %s\n", intrstr);
165
166	/* Enable the device */
167	reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
168	reg |= PCI_COMMAND_MASTER_ENABLE;
169	pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, reg);
170
171	/* Power up the device */
172	pci_set_powerstate(pc, tag, PCI_PMCSR_STATE_D0);
173
174	if (rtsx_attach(&sc->sc, iot, ioh, size, pa->pa_dmat, flags) != 0) {
175		aprint_error_dev(self, "couldn't initialize chip\n");
176		return;
177	}
178
179	if (!pmf_device_register1(self, rtsx_suspend, rtsx_resume,
180	    rtsx_shutdown))
181		aprint_error_dev(self, "couldn't establish powerhook\n");
182}
183
184static int
185rtsx_pci_detach(device_t self, int flags)
186{
187	struct rtsx_pci_softc *sc = device_private(self);
188	int rv;
189
190	rv = rtsx_detach(&sc->sc, flags);
191	if (rv)
192		return rv;
193
194	pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
195	pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
196
197	return 0;
198}
199