1 | /* $NetBSD: if_ex_cardbus.c,v 1.56 2016/07/07 06:55:41 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1998 and 1999 |
5 | * HAYAKAWA Koichi. All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY HAYAKAWA KOICHI ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL TAKESHI OHASHI OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * CardBus specific routines for 3Com 3C575-family CardBus ethernet adapter |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: if_ex_cardbus.c,v 1.56 2016/07/07 06:55:41 msaitoh Exp $" ); |
35 | |
36 | /* #define EX_DEBUG 4 */ /* define to report information for debugging */ |
37 | |
38 | #include <sys/param.h> |
39 | #include <sys/systm.h> |
40 | #include <sys/mbuf.h> |
41 | #include <sys/socket.h> |
42 | #include <sys/ioctl.h> |
43 | #include <sys/errno.h> |
44 | #include <sys/syslog.h> |
45 | #include <sys/select.h> |
46 | #include <sys/device.h> |
47 | |
48 | #include <net/if.h> |
49 | #include <net/if_dl.h> |
50 | #include <net/if_ether.h> |
51 | #include <net/if_media.h> |
52 | |
53 | #include <sys/cpu.h> |
54 | #include <sys/bus.h> |
55 | |
56 | #include <dev/cardbus/cardbusvar.h> |
57 | #include <dev/pci/pcidevs.h> |
58 | |
59 | #include <dev/mii/miivar.h> |
60 | |
61 | #include <dev/ic/elink3var.h> |
62 | #include <dev/ic/elink3reg.h> |
63 | #include <dev/ic/elinkxlreg.h> |
64 | #include <dev/ic/elinkxlvar.h> |
65 | |
66 | #if defined DEBUG && !defined EX_DEBUG |
67 | #define EX_DEBUG |
68 | #endif |
69 | |
70 | #if defined EX_DEBUG |
71 | #define DPRINTF(a) printf a |
72 | #else |
73 | #define DPRINTF(a) |
74 | #endif |
75 | |
76 | #define CARDBUS_3C575BTX_FUNCSTAT_PCIREG PCI_BAR2 /* means 0x18 */ |
77 | #define EX_CB_INTR 4 /* intr acknowledge reg. CardBus only */ |
78 | #define EX_CB_INTR_ACK 0x8000 /* intr acknowledge bit */ |
79 | |
80 | int ex_cardbus_match(device_t, cfdata_t, void *); |
81 | void ex_cardbus_attach(device_t, device_t, void *); |
82 | int ex_cardbus_detach(device_t, int); |
83 | void ex_cardbus_intr_ack(struct ex_softc *); |
84 | |
85 | int ex_cardbus_enable(struct ex_softc *); |
86 | void ex_cardbus_disable(struct ex_softc *); |
87 | |
88 | struct ex_cardbus_softc { |
89 | struct ex_softc sc_softc; |
90 | |
91 | cardbus_devfunc_t sc_ct; |
92 | uint8_t sc_cardbus_flags; |
93 | #define EX_REATTACH 0x01 |
94 | #define EX_ABSENT 0x02 |
95 | uint8_t sc_cardtype; |
96 | #define EX_CB_BOOMERANG 1 |
97 | #define EX_CB_CYCLONE 2 |
98 | |
99 | /* CardBus function status space. 575B requests it. */ |
100 | bus_space_tag_t sc_funct; |
101 | bus_space_handle_t sc_funch; |
102 | bus_size_t sc_funcsize; |
103 | |
104 | bus_size_t sc_mapsize; /* the size of mapped bus space region */ |
105 | |
106 | pcitag_t sc_tag; |
107 | |
108 | pcireg_t sc_csr; |
109 | int sc_bar_reg; /* which BAR to use */ |
110 | pcireg_t sc_bar_val; /* value of the BAR */ |
111 | int sc_bar_reg1; /* which BAR to use */ |
112 | pcireg_t sc_bar_val1; /* value of the BAR */ |
113 | |
114 | }; |
115 | |
116 | CFATTACH_DECL3_NEW(ex_cardbus, sizeof(struct ex_cardbus_softc), |
117 | ex_cardbus_match, ex_cardbus_attach, ex_cardbus_detach, ex_activate, |
118 | NULL, NULL, DVF_DETACH_SHUTDOWN); |
119 | |
120 | const struct ex_cardbus_product { |
121 | uint32_t ecp_prodid; /* CardBus product ID */ |
122 | int ecp_flags; /* initial softc flags */ |
123 | pcireg_t ecp_csr; /* PCI CSR flags */ |
124 | int ecp_cardtype; /* card type */ |
125 | const char *ecp_name; /* device name */ |
126 | } ex_cardbus_products[] = { |
127 | { PCI_PRODUCT_3COM_3C575TX, |
128 | EX_CONF_MII | EX_CONF_EEPROM_OFF | EX_CONF_EEPROM_8BIT, |
129 | PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE, |
130 | EX_CB_BOOMERANG, |
131 | "3c575-TX Ethernet" }, |
132 | |
133 | { PCI_PRODUCT_3COM_3C575BTX, |
134 | EX_CONF_90XB|EX_CONF_MII|EX_CONF_INV_LED_POLARITY | |
135 | EX_CONF_EEPROM_OFF | EX_CONF_EEPROM_8BIT, |
136 | PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | |
137 | PCI_COMMAND_MASTER_ENABLE, |
138 | EX_CB_CYCLONE, |
139 | "3c575B-TX Ethernet" }, |
140 | |
141 | { PCI_PRODUCT_3COM_3C575CTX, |
142 | EX_CONF_90XB | EX_CONF_PHY_POWER | EX_CONF_EEPROM_OFF | |
143 | EX_CONF_EEPROM_8BIT, |
144 | PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | |
145 | PCI_COMMAND_MASTER_ENABLE, |
146 | EX_CB_CYCLONE, |
147 | "3c575CT Ethernet" }, |
148 | |
149 | { PCI_PRODUCT_3COM_3C656_E, |
150 | EX_CONF_90XB | EX_CONF_PHY_POWER | EX_CONF_EEPROM_OFF | |
151 | EX_CONF_EEPROM_8BIT | EX_CONF_INV_LED_POLARITY, |
152 | PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | |
153 | PCI_COMMAND_MASTER_ENABLE, |
154 | EX_CB_CYCLONE, |
155 | "3c656-TX Ethernet" }, |
156 | |
157 | { PCI_PRODUCT_3COM_3C656B_E, |
158 | EX_CONF_90XB | EX_CONF_PHY_POWER | EX_CONF_EEPROM_OFF | |
159 | EX_CONF_EEPROM_8BIT | EX_CONF_INV_LED_POLARITY, |
160 | PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | |
161 | PCI_COMMAND_MASTER_ENABLE, |
162 | EX_CB_CYCLONE, |
163 | "3c656B-TX Ethernet" }, |
164 | |
165 | { PCI_PRODUCT_3COM_3C656C_E, |
166 | EX_CONF_90XB | EX_CONF_PHY_POWER | EX_CONF_EEPROM_OFF | |
167 | EX_CONF_EEPROM_8BIT, |
168 | PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | |
169 | PCI_COMMAND_MASTER_ENABLE, |
170 | EX_CB_CYCLONE, |
171 | "3c656C-TX Ethernet" }, |
172 | |
173 | { 0, |
174 | 0, |
175 | 0, |
176 | 0, |
177 | NULL }, |
178 | }; |
179 | |
180 | |
181 | void ex_cardbus_setup(struct ex_cardbus_softc *); |
182 | |
183 | const struct ex_cardbus_product *ex_cardbus_lookup |
184 | (const struct cardbus_attach_args *); |
185 | |
186 | const struct ex_cardbus_product * |
187 | ex_cardbus_lookup(const struct cardbus_attach_args *ca) |
188 | { |
189 | const struct ex_cardbus_product *ecp; |
190 | |
191 | if (PCI_VENDOR(ca->ca_id) != PCI_VENDOR_3COM) |
192 | return (NULL); |
193 | |
194 | for (ecp = ex_cardbus_products; ecp->ecp_name != NULL; ecp++) |
195 | if (PCI_PRODUCT(ca->ca_id) == ecp->ecp_prodid) |
196 | return (ecp); |
197 | return (NULL); |
198 | } |
199 | |
200 | int |
201 | ex_cardbus_match(device_t parent, cfdata_t cf, void *aux) |
202 | { |
203 | struct cardbus_attach_args *ca = aux; |
204 | |
205 | if (ex_cardbus_lookup(ca) != NULL) |
206 | return (1); |
207 | |
208 | return (0); |
209 | } |
210 | |
211 | void |
212 | ex_cardbus_attach(device_t parent, device_t self, void *aux) |
213 | { |
214 | struct ex_cardbus_softc *csc = device_private(self); |
215 | struct ex_softc *sc = &csc->sc_softc; |
216 | struct cardbus_attach_args *ca = aux; |
217 | cardbus_devfunc_t ct = ca->ca_ct; |
218 | const struct ex_cardbus_product *ecp; |
219 | bus_addr_t adr, adr1; |
220 | |
221 | sc->sc_dev = self; |
222 | |
223 | sc->sc_dmat = ca->ca_dmat; |
224 | csc->sc_ct = ca->ca_ct; |
225 | csc->sc_tag = ca->ca_tag; |
226 | |
227 | ecp = ex_cardbus_lookup(ca); |
228 | if (ecp == NULL) { |
229 | printf("\n" ); |
230 | panic("ex_cardbus_attach: impossible" ); |
231 | } |
232 | |
233 | aprint_normal(": 3Com %s\n" , ecp->ecp_name); |
234 | |
235 | sc->ex_conf = ecp->ecp_flags; |
236 | csc->sc_cardtype = ecp->ecp_cardtype; |
237 | csc->sc_csr = ecp->ecp_csr; |
238 | |
239 | if (Cardbus_mapreg_map(ct, PCI_BAR0, PCI_MAPREG_TYPE_IO, 0, |
240 | &sc->sc_iot, &sc->sc_ioh, &adr, &csc->sc_mapsize) == 0) { |
241 | csc->sc_bar_reg = PCI_BAR0; |
242 | csc->sc_bar_val = adr | PCI_MAPREG_TYPE_IO; |
243 | |
244 | if (csc->sc_cardtype == EX_CB_CYCLONE) { |
245 | /* Map CardBus function status window. */ |
246 | if (Cardbus_mapreg_map(ct, |
247 | CARDBUS_3C575BTX_FUNCSTAT_PCIREG, |
248 | PCI_MAPREG_TYPE_MEM, 0, |
249 | &csc->sc_funct, &csc->sc_funch, |
250 | &adr1, &csc->sc_funcsize) == 0) { |
251 | |
252 | csc->sc_bar_reg1 = |
253 | CARDBUS_3C575BTX_FUNCSTAT_PCIREG; |
254 | csc->sc_bar_val1 = |
255 | adr1 | PCI_MAPREG_TYPE_MEM; |
256 | |
257 | } else { |
258 | aprint_error_dev(self, |
259 | "unable to map function status window\n" ); |
260 | return; |
261 | } |
262 | |
263 | /* Setup interrupt acknowledge hook */ |
264 | sc->intr_ack = ex_cardbus_intr_ack; |
265 | } |
266 | } else { |
267 | aprint_naive(": can't map i/o space\n" ); |
268 | return; |
269 | } |
270 | |
271 | /* Power management hooks. */ |
272 | sc->enable = ex_cardbus_enable; |
273 | sc->disable = ex_cardbus_disable; |
274 | |
275 | /* |
276 | * Handle power management nonsense and |
277 | * initialize the configuration registers. |
278 | */ |
279 | ex_cardbus_setup(csc); |
280 | |
281 | ex_config(sc); |
282 | |
283 | if (csc->sc_cardtype == EX_CB_CYCLONE) |
284 | bus_space_write_4(csc->sc_funct, csc->sc_funch, |
285 | EX_CB_INTR, EX_CB_INTR_ACK); |
286 | |
287 | Cardbus_function_disable(csc->sc_ct); |
288 | } |
289 | |
290 | void |
291 | ex_cardbus_intr_ack(struct ex_softc *sc) |
292 | { |
293 | struct ex_cardbus_softc *csc = (struct ex_cardbus_softc *)sc; |
294 | |
295 | bus_space_write_4(csc->sc_funct, csc->sc_funch, EX_CB_INTR, |
296 | EX_CB_INTR_ACK); |
297 | } |
298 | |
299 | int |
300 | ex_cardbus_detach(device_t self, int flags) |
301 | { |
302 | struct ex_cardbus_softc *csc = device_private(self); |
303 | struct ex_softc *sc = &csc->sc_softc; |
304 | struct cardbus_devfunc *ct = csc->sc_ct; |
305 | int rv; |
306 | |
307 | #if defined(DIAGNOSTIC) |
308 | if (ct == NULL) { |
309 | panic("%s: data structure lacks" , device_xname(self)); |
310 | } |
311 | #endif |
312 | |
313 | if ((rv = ex_detach(sc)) != 0) |
314 | return rv; |
315 | |
316 | /* |
317 | * Unhook the interrupt handler. |
318 | */ |
319 | Cardbus_intr_disestablish(ct, sc->sc_ih); |
320 | |
321 | if (csc->sc_cardtype == EX_CB_CYCLONE) { |
322 | Cardbus_mapreg_unmap(ct, |
323 | CARDBUS_3C575BTX_FUNCSTAT_PCIREG, |
324 | csc->sc_funct, csc->sc_funch, csc->sc_funcsize); |
325 | } |
326 | |
327 | Cardbus_mapreg_unmap(ct, PCI_BAR0, sc->sc_iot, |
328 | sc->sc_ioh, csc->sc_mapsize); |
329 | return 0; |
330 | } |
331 | |
332 | int |
333 | ex_cardbus_enable(struct ex_softc *sc) |
334 | { |
335 | struct ex_cardbus_softc *csc = (struct ex_cardbus_softc *)sc; |
336 | |
337 | Cardbus_function_enable(csc->sc_ct); |
338 | ex_cardbus_setup(csc); |
339 | |
340 | sc->sc_ih = Cardbus_intr_establish(csc->sc_ct, IPL_NET, ex_intr, sc); |
341 | if (NULL == sc->sc_ih) { |
342 | aprint_error_dev(sc->sc_dev, "couldn't establish interrupt\n" ); |
343 | return (1); |
344 | } |
345 | |
346 | return (0); |
347 | } |
348 | |
349 | void |
350 | ex_cardbus_disable(struct ex_softc *sc) |
351 | { |
352 | struct ex_cardbus_softc *csc = (struct ex_cardbus_softc *)sc; |
353 | |
354 | if (sc->sc_ih != NULL) { |
355 | Cardbus_intr_disestablish(csc->sc_ct, sc->sc_ih); |
356 | sc->sc_ih = NULL; |
357 | } |
358 | |
359 | Cardbus_function_disable(csc->sc_ct); |
360 | |
361 | } |
362 | |
363 | void |
364 | ex_cardbus_setup(struct ex_cardbus_softc *csc) |
365 | { |
366 | cardbus_devfunc_t ct = csc->sc_ct; |
367 | pcireg_t reg; |
368 | |
369 | (void)cardbus_set_powerstate(ct, csc->sc_tag, PCI_PWR_D0); |
370 | |
371 | /* Program the BAR */ |
372 | Cardbus_conf_write(ct, csc->sc_tag, csc->sc_bar_reg, csc->sc_bar_val); |
373 | /* Make sure the right access type is on the CardBus bridge. */ |
374 | if (csc->sc_cardtype == EX_CB_CYCLONE) { |
375 | /* Program the BAR */ |
376 | Cardbus_conf_write(ct, csc->sc_tag, |
377 | csc->sc_bar_reg1, csc->sc_bar_val1); |
378 | /* |
379 | * Make sure CardBus brigde can access memory space. Usually |
380 | * memory access is enabled by BIOS, but some BIOSes do not |
381 | * enable it. |
382 | */ |
383 | } |
384 | |
385 | /* Enable the appropriate bits in the CARDBUS CSR. */ |
386 | reg = Cardbus_conf_read(ct, csc->sc_tag, PCI_COMMAND_STATUS_REG); |
387 | reg |= csc->sc_csr; |
388 | Cardbus_conf_write(ct, csc->sc_tag, PCI_COMMAND_STATUS_REG, reg); |
389 | |
390 | /* |
391 | * set latency timer |
392 | */ |
393 | reg = Cardbus_conf_read(ct, csc->sc_tag, PCI_BHLC_REG); |
394 | if (PCI_LATTIMER(reg) < 0x20) { |
395 | /* at least the value of latency timer should 0x20. */ |
396 | DPRINTF(("if_ex_cardbus: lattimer 0x%x -> 0x20\n" , |
397 | PCI_LATTIMER(reg))); |
398 | reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); |
399 | reg |= (0x20 << PCI_LATTIMER_SHIFT); |
400 | Cardbus_conf_write(ct, csc->sc_tag, PCI_BHLC_REG, reg); |
401 | } |
402 | } |
403 | |