Line data Source code
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 :
49 : struct 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 :
61 : const 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 :
75 : int re_pci_probe(struct device *, void *, void *);
76 : void re_pci_attach(struct device *, struct device *, void *);
77 : int re_pci_detach(struct device *, int);
78 : int re_pci_activate(struct device *, int);
79 :
80 : /*
81 : * PCI autoconfig definitions
82 : */
83 : struct 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 : */
95 : int
96 0 : re_pci_probe(struct device *parent, void *match, void *aux)
97 : {
98 0 : struct pci_attach_args *pa = aux;
99 0 : pci_chipset_tag_t pc = pa->pa_pc;
100 : pcireg_t subid;
101 :
102 0 : subid = pci_conf_read(pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
103 :
104 : /* C+ mode 8139's */
105 0 : if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_REALTEK &&
106 0 : PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_REALTEK_RT8139 &&
107 0 : PCI_REVISION(pa->pa_class) == 0x20)
108 0 : return (1);
109 :
110 0 : if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_LINKSYS &&
111 0 : PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_LINKSYS_EG1032 &&
112 0 : subid == RE_LINKSYS_EG1032_SUBID)
113 0 : return (1);
114 :
115 0 : return (pci_matchbyid((struct pci_attach_args *)aux, re_pci_devices,
116 : nitems(re_pci_devices)));
117 0 : }
118 :
119 : /*
120 : * PCI-specific attach routine
121 : */
122 : void
123 0 : re_pci_attach(struct device *parent, struct device *self, void *aux)
124 : {
125 0 : struct re_pci_softc *psc = (struct re_pci_softc *)self;
126 0 : struct rl_softc *sc = &psc->sc_rl;
127 0 : struct pci_attach_args *pa = aux;
128 0 : pci_chipset_tag_t pc = pa->pa_pc;
129 0 : pci_intr_handle_t ih;
130 : const char *intrstr = NULL;
131 : pcireg_t reg;
132 0 : int offset;
133 :
134 0 : 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 0 : pci_conf_write(pc, pa->pa_tag, RL_PCI_PMCSR, RL_PME_EN);
139 : #endif
140 :
141 : /*
142 : * Map control/status registers.
143 : */
144 0 : if (pci_mapreg_map(pa, RL_PCI_LOMEM64, PCI_MAPREG_TYPE_MEM |
145 0 : PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->rl_btag, &sc->rl_bhandle,
146 0 : NULL, &psc->sc_iosize, 0)) {
147 0 : 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 0 : 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 0 : printf(": can't map mem or i/o space\n");
154 0 : return;
155 : }
156 : }
157 : }
158 :
159 : /* Allocate interrupt */
160 0 : if (pci_intr_map_msi(pa, &ih) == 0)
161 0 : sc->rl_flags |= RL_FLAG_MSI;
162 0 : else if (pci_intr_map(pa, &ih) != 0) {
163 0 : printf(": couldn't map interrupt\n");
164 0 : return;
165 : }
166 0 : intrstr = pci_intr_string(pc, ih);
167 0 : psc->sc_ih = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE, re_intr,
168 0 : sc, sc->sc_dev.dv_xname);
169 0 : if (psc->sc_ih == NULL) {
170 0 : printf(": couldn't establish interrupt");
171 0 : if (intrstr != NULL)
172 0 : printf(" at %s", intrstr);
173 0 : return;
174 : }
175 :
176 0 : sc->sc_dmat = pa->pa_dmat;
177 0 : psc->sc_pc = pc;
178 :
179 : /*
180 : * PCI Express check.
181 : */
182 0 : if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
183 : &offset, NULL)) {
184 : /* Disable PCIe ASPM and ECPM. */
185 0 : reg = pci_conf_read(pc, pa->pa_tag, offset + PCI_PCIE_LCSR);
186 0 : reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1 |
187 : PCI_PCIE_LCSR_ECPM);
188 0 : pci_conf_write(pc, pa->pa_tag, offset + PCI_PCIE_LCSR, reg);
189 0 : sc->rl_flags |= RL_FLAG_PCIE;
190 0 : }
191 :
192 0 : if (!(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_REALTEK &&
193 0 : PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_REALTEK_RT8139)) {
194 : u_int8_t cfg;
195 :
196 0 : CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
197 0 : cfg = CSR_READ_1(sc, RL_CFG2);
198 0 : if (sc->rl_flags & RL_FLAG_MSI) {
199 0 : cfg |= RL_CFG2_MSI;
200 0 : CSR_WRITE_1(sc, RL_CFG2, cfg);
201 0 : } else {
202 0 : if ((cfg & RL_CFG2_MSI) != 0) {
203 0 : cfg &= ~RL_CFG2_MSI;
204 0 : CSR_WRITE_1(sc, RL_CFG2, cfg);
205 0 : }
206 : }
207 0 : CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
208 0 : }
209 :
210 0 : sc->sc_product = PCI_PRODUCT(pa->pa_id);
211 :
212 : /* Call bus-independent attach routine */
213 0 : if (re_attach(sc, intrstr)) {
214 0 : pci_intr_disestablish(pc, psc->sc_ih);
215 0 : bus_space_unmap(sc->rl_btag, sc->rl_bhandle, psc->sc_iosize);
216 0 : }
217 0 : }
218 :
219 : int
220 0 : re_pci_detach(struct device *self, int flags)
221 : {
222 0 : struct re_pci_softc *psc = (struct re_pci_softc *)self;
223 0 : struct rl_softc *sc = &psc->sc_rl;
224 0 : struct ifnet *ifp = &sc->sc_arpcom.ac_if;
225 :
226 : /* Remove timeout handler */
227 0 : timeout_del(&sc->timer_handle);
228 :
229 : /* Detach PHY */
230 0 : if (LIST_FIRST(&sc->sc_mii.mii_phys) != NULL)
231 0 : mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
232 :
233 : /* Delete media stuff */
234 0 : ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
235 0 : ether_ifdetach(ifp);
236 0 : if_detach(ifp);
237 :
238 : /* Disable interrupts */
239 0 : if (psc->sc_ih != NULL)
240 0 : pci_intr_disestablish(psc->sc_pc, psc->sc_ih);
241 :
242 : /* Free pci resources */
243 0 : bus_space_unmap(sc->rl_btag, sc->rl_bhandle, psc->sc_iosize);
244 :
245 0 : return (0);
246 : }
247 :
248 : int
249 0 : re_pci_activate(struct device *self, int act)
250 : {
251 0 : struct re_pci_softc *psc = (struct re_pci_softc *)self;
252 0 : struct rl_softc *sc = &psc->sc_rl;
253 0 : struct ifnet *ifp = &sc->sc_arpcom.ac_if;
254 :
255 0 : switch (act) {
256 : case DVACT_SUSPEND:
257 0 : if (ifp->if_flags & IFF_RUNNING)
258 0 : re_stop(ifp);
259 : break;
260 : case DVACT_RESUME:
261 0 : if (ifp->if_flags & IFF_UP)
262 0 : re_init(ifp);
263 : break;
264 : }
265 :
266 0 : return (0);
267 : }
|