if_re_pci.c revision 1.52
1/*	$OpenBSD: if_re_pci.c,v 1.52 2018/04/11 08:02:18 patrick Exp $	*/
2
3/*
4 * Copyright (c) 2005 Peter Valchev <pvalchev@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * PCI front-end for the Realtek 8169
21 */
22
23#include <sys/param.h>
24#include <sys/endian.h>
25#include <sys/systm.h>
26#include <sys/sockio.h>
27#include <sys/mbuf.h>
28#include <sys/malloc.h>
29#include <sys/kernel.h>
30#include <sys/device.h>
31#include <sys/timeout.h>
32#include <sys/socket.h>
33
34#include <net/if.h>
35#include <net/if_media.h>
36
37#include <netinet/in.h>
38#include <netinet/if_ether.h>
39
40#include <dev/mii/miivar.h>
41
42#include <dev/pci/pcireg.h>
43#include <dev/pci/pcivar.h>
44#include <dev/pci/pcidevs.h>
45
46#include <dev/ic/rtl81x9reg.h>
47#include <dev/ic/revar.h>
48
49struct re_pci_softc {
50	/* General */
51	struct rl_softc sc_rl;
52
53	/* PCI-specific data */
54	void *sc_ih;
55	pci_chipset_tag_t sc_pc;
56	pcitag_t sc_pcitag;
57
58	bus_size_t sc_iosize;
59};
60
61const struct pci_matchid re_pci_devices[] = {
62	{ PCI_VENDOR_COREGA, PCI_PRODUCT_COREGA_CGLAPCIGT },
63	{ PCI_VENDOR_DLINK, PCI_PRODUCT_DLINK_DGE528T },
64	{ PCI_VENDOR_DLINK, PCI_PRODUCT_DLINK_DGE530T_C1 },
65	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8101E },
66	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8168 },
67	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8169 },
68	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8169SC },
69	{ PCI_VENDOR_TTTECH, PCI_PRODUCT_TTTECH_MC322 },
70	{ PCI_VENDOR_USR2, PCI_PRODUCT_USR2_USR997902 }
71};
72
73#define RE_LINKSYS_EG1032_SUBID 0x00241737
74
75int	re_pci_probe(struct device *, void *, void *);
76void	re_pci_attach(struct device *, struct device *, void *);
77int	re_pci_detach(struct device *, int);
78int	re_pci_activate(struct device *, int);
79
80/*
81 * PCI autoconfig definitions
82 */
83struct cfattach re_pci_ca = {
84	sizeof(struct re_pci_softc),
85	re_pci_probe,
86	re_pci_attach,
87	re_pci_detach,
88	re_pci_activate
89};
90
91/*
92 * Probe for a Realtek 8169/8110 chip. Check the PCI vendor and device
93 * IDs against our list and return a device name if we find a match.
94 */
95int
96re_pci_probe(struct device *parent, void *match, void *aux)
97{
98	struct pci_attach_args *pa = aux;
99	pci_chipset_tag_t pc = pa->pa_pc;
100	pcireg_t subid;
101
102	subid = pci_conf_read(pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
103
104	/* C+ mode 8139's */
105	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_REALTEK &&
106	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_REALTEK_RT8139 &&
107	    PCI_REVISION(pa->pa_class) == 0x20)
108		return (1);
109
110	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_LINKSYS &&
111	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_LINKSYS_EG1032 &&
112	    subid == RE_LINKSYS_EG1032_SUBID)
113		return (1);
114
115	return (pci_matchbyid((struct pci_attach_args *)aux, re_pci_devices,
116	    nitems(re_pci_devices)));
117}
118
119/*
120 * PCI-specific attach routine
121 */
122void
123re_pci_attach(struct device *parent, struct device *self, void *aux)
124{
125	struct re_pci_softc	*psc = (struct re_pci_softc *)self;
126	struct rl_softc		*sc = &psc->sc_rl;
127	struct pci_attach_args	*pa = aux;
128	pci_chipset_tag_t	pc = pa->pa_pc;
129	pci_intr_handle_t	ih;
130	const char		*intrstr = NULL;
131	pcireg_t		reg;
132	int			offset;
133
134	pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
135
136#ifndef SMALL_KERNEL
137	/* Enable power management for wake on lan. */
138	pci_conf_write(pc, pa->pa_tag, RL_PCI_PMCSR, RL_PME_EN);
139#endif
140
141	/*
142	 * Map control/status registers.
143	 */
144	if (pci_mapreg_map(pa, RL_PCI_LOMEM64, PCI_MAPREG_TYPE_MEM |
145	    PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->rl_btag, &sc->rl_bhandle,
146	    NULL, &psc->sc_iosize, 0)) {
147		if (pci_mapreg_map(pa, RL_PCI_LOMEM, PCI_MAPREG_TYPE_MEM |
148		    PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->rl_btag, &sc->rl_bhandle,
149		    NULL, &psc->sc_iosize, 0)) {
150			if (pci_mapreg_map(pa, RL_PCI_LOIO, PCI_MAPREG_TYPE_IO,
151			    0, &sc->rl_btag, &sc->rl_bhandle, NULL,
152			    &psc->sc_iosize, 0)) {
153				printf(": can't map mem or i/o space\n");
154				return;
155			}
156		}
157	}
158
159	/* Allocate interrupt */
160	if (pci_intr_map_msi(pa, &ih) == 0)
161		sc->rl_flags |= RL_FLAG_MSI;
162	else if (pci_intr_map(pa, &ih) != 0) {
163		printf(": couldn't map interrupt\n");
164		return;
165	}
166	intrstr = pci_intr_string(pc, ih);
167	psc->sc_ih = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE, re_intr,
168	    sc, sc->sc_dev.dv_xname);
169	if (psc->sc_ih == NULL) {
170		printf(": couldn't establish interrupt");
171		if (intrstr != NULL)
172			printf(" at %s", intrstr);
173		return;
174	}
175
176	sc->sc_dmat = pa->pa_dmat;
177	psc->sc_pc = pc;
178
179	/*
180	 * PCI Express check.
181	 */
182	if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
183	    &offset, NULL)) {
184		/* Disable PCIe ASPM and ECPM. */
185		reg = pci_conf_read(pc, pa->pa_tag, offset + PCI_PCIE_LCSR);
186		reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1 |
187		    PCI_PCIE_LCSR_ECPM);
188		pci_conf_write(pc, pa->pa_tag, offset + PCI_PCIE_LCSR, reg);
189		sc->rl_flags |= RL_FLAG_PCIE;
190	}
191
192	if (!(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_REALTEK &&
193	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_REALTEK_RT8139)) {
194		u_int8_t	cfg;
195
196		CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
197		cfg = CSR_READ_1(sc, RL_CFG2);
198		if (sc->rl_flags & RL_FLAG_MSI) {
199			cfg |= RL_CFG2_MSI;
200			CSR_WRITE_1(sc, RL_CFG2, cfg);
201		} else {
202			if ((cfg & RL_CFG2_MSI) != 0) {
203				cfg &= ~RL_CFG2_MSI;
204				CSR_WRITE_1(sc, RL_CFG2, cfg);
205			}
206		}
207		CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
208	}
209
210	sc->sc_product = PCI_PRODUCT(pa->pa_id);
211
212	/* Call bus-independent attach routine */
213	if (re_attach(sc, intrstr)) {
214		pci_intr_disestablish(pc, psc->sc_ih);
215		bus_space_unmap(sc->rl_btag, sc->rl_bhandle, psc->sc_iosize);
216	}
217}
218
219int
220re_pci_detach(struct device *self, int flags)
221{
222	struct re_pci_softc	*psc = (struct re_pci_softc *)self;
223	struct rl_softc		*sc = &psc->sc_rl;
224	struct ifnet		*ifp = &sc->sc_arpcom.ac_if;
225
226	/* Remove timeout handler */
227	timeout_del(&sc->timer_handle);
228
229	/* Detach PHY */
230	if (LIST_FIRST(&sc->sc_mii.mii_phys) != NULL)
231		mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
232
233	/* Delete media stuff */
234	ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
235	ether_ifdetach(ifp);
236	if_detach(ifp);
237
238	/* Disable interrupts */
239	if (psc->sc_ih != NULL)
240		pci_intr_disestablish(psc->sc_pc, psc->sc_ih);
241
242	/* Free pci resources */
243	bus_space_unmap(sc->rl_btag, sc->rl_bhandle, psc->sc_iosize);
244
245	return (0);
246}
247
248int
249re_pci_activate(struct device *self, int act)
250{
251	struct re_pci_softc	*psc = (struct re_pci_softc *)self;
252	struct rl_softc		*sc = &psc->sc_rl;
253	struct ifnet 		*ifp = &sc->sc_arpcom.ac_if;
254
255	switch (act) {
256	case DVACT_SUSPEND:
257		if (ifp->if_flags & IFF_RUNNING)
258			re_stop(ifp);
259		break;
260	case DVACT_RESUME:
261		if (ifp->if_flags & IFF_UP)
262			re_init(ifp);
263		break;
264	}
265
266	return (0);
267}
268