Deleted Added
sdiff udiff text old ( 154895 ) new ( 154924 )
full compact
1/*-
2 * Copyright (c) 2005, M. Warner Losh
3 * All rights reserved.
4 * Copyright (c) 1995, David Greenman
5 * 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 unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/ed/if_ed_hpp.c 154895 2006-01-27 08:25:47Z imp $");
32
33#include "opt_ed.h"
34
35#ifdef ED_HPP
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/sockio.h>
40#include <sys/mbuf.h>
41#include <sys/kernel.h>
42#include <sys/socket.h>
43#include <sys/syslog.h>
44
45#include <sys/bus.h>
46
47#include <machine/bus.h>
48#include <sys/rman.h>
49#include <machine/resource.h>
50
51#include <net/ethernet.h>
52#include <net/if.h>
53#include <net/if_arp.h>
54#include <net/if_dl.h>
55#include <net/if_mib.h>
56#include <net/if_media.h>
57
58#include <net/bpf.h>
59
60#include <dev/ed/if_edreg.h>
61#include <dev/ed/if_edvar.h>
62
63static void ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
64 uint16_t);
65static void ed_hpp_set_physical_link(struct ed_softc *sc);
66
67/*
68 * Interrupt conversion table for the HP PC LAN+
69 */
70static uint16_t ed_hpp_intr_val[] = {
71 0, /* 0 */
72 0, /* 1 */
73 0, /* 2 */
74 3, /* 3 */
75 4, /* 4 */
76 5, /* 5 */
77 6, /* 6 */
78 7, /* 7 */
79 0, /* 8 */
80 9, /* 9 */
81 10, /* 10 */
82 11, /* 11 */
83 12, /* 12 */
84 0, /* 13 */
85 0, /* 14 */
86 15 /* 15 */
87};
88
89#define ED_HPP_TEST_SIZE 16
90
91/*
92 * Probe and vendor specific initialization for the HP PC Lan+ Cards.
93 * (HP Part nos: 27247B and 27252A).
94 *
95 * The card has an asic wrapper around a DS8390 core. The asic handles
96 * host accesses and offers both standard register IO and memory mapped
97 * IO. Memory mapped I/O allows better performance at the expense of greater
98 * chance of an incompatibility with existing ISA cards.
99 *
100 * The card has a few caveats: it isn't tolerant of byte wide accesses, only
101 * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions
102 * don't allow 32 bit accesses; these are indicated by a bit in the software
103 * ID register (see if_edreg.h).
104 *
105 * Other caveats are: we should read the MAC address only when the card
106 * is inactive.
107 *
108 * For more information; please consult the CRYNWR packet driver.
109 *
110 * The AUI port is turned on using the "link2" option on the ifconfig
111 * command line.
112 */
113int
114ed_probe_HP_pclanp(device_t dev, int port_rid, int flags)
115{
116 struct ed_softc *sc = device_get_softc(dev);
117 int error;
118 int n; /* temp var */
119 int memsize; /* mem on board */
120 u_char checksum; /* checksum of board address */
121 u_char irq; /* board configured IRQ */
122 uint8_t test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */
123 uint8_t test_buffer[ED_HPP_TEST_SIZE]; /* probing card */
124 u_long conf_maddr, conf_msize, conf_irq, junk;
125
126 error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
127 if (error)
128 return (error);
129
130 /* Fill in basic information */
131 sc->asic_offset = ED_HPP_ASIC_OFFSET;
132 sc->nic_offset = ED_HPP_NIC_OFFSET;
133
134 sc->chip_type = ED_CHIP_TYPE_DP8390;
135 sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */
136
137 /*
138 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
139 */
140
141 if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
142 (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
143 ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
144 (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
145 return (ENXIO);
146
147 /*
148 * Read the MAC address and verify checksum on the address.
149 */
150
151 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
152 for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
153 checksum += (sc->enaddr[n] =
154 ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
155
156 checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
157
158 if (checksum != 0xFF)
159 return (ENXIO);
160
161 /*
162 * Verify that the software model number is 0.
163 */
164
165 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
166 if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
167 ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
168 return (ENXIO);
169
170 /*
171 * Read in and save the current options configured on card.
172 */
173
174 sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
175
176 sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
177 ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
178
179 /*
180 * Reset the chip. This requires writing to the option register
181 * so take care to preserve the other bits.
182 */
183
184 ed_asic_outw(sc, ED_HPP_OPTION,
185 (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET |
186 ED_HPP_OPTION_CHIP_RESET)));
187
188 DELAY(5000); /* wait for chip reset to complete */
189
190 ed_asic_outw(sc, ED_HPP_OPTION,
191 (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
192 ED_HPP_OPTION_CHIP_RESET |
193 ED_HPP_OPTION_ENABLE_IRQ)));
194
195 DELAY(5000);
196
197 if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
198 return (ENXIO); /* reset did not complete */
199
200 /*
201 * Read out configuration information.
202 */
203
204 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
205
206 irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
207
208 /*
209 * Check for impossible IRQ.
210 */
211
212 if (irq >= (sizeof(ed_hpp_intr_val) / sizeof(ed_hpp_intr_val[0])))
213 return (ENXIO);
214
215 /*
216 * If the kernel IRQ was specified with a '?' use the cards idea
217 * of the IRQ. If the kernel IRQ was explicitly specified, it
218 * should match that of the hardware.
219 */
220 error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
221 if (error)
222 bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
223 else {
224 if (conf_irq != ed_hpp_intr_val[irq])
225 return (ENXIO);
226 }
227
228 /*
229 * Fill in softconfig info.
230 */
231
232 sc->vendor = ED_VENDOR_HP;
233 sc->type = ED_TYPE_HP_PCLANPLUS;
234 sc->type_str = "HP-PCLAN+";
235
236 sc->mem_shared = 0; /* we DON'T have dual ported RAM */
237 sc->mem_start = 0; /* we use offsets inside the card RAM */
238
239 sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
240
241 /*
242 * The board has 32KB of memory. Is there a way to determine
243 * this programmatically?
244 */
245
246 memsize = 32768;
247
248 /*
249 * Check if memory mapping of the I/O registers possible.
250 */
251 if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
252 u_long mem_addr;
253
254 /*
255 * determine the memory address from the board.
256 */
257
258 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
259 mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
260
261 /*
262 * Check that the kernel specified start of memory and
263 * hardware's idea of it match.
264 */
265 error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
266 &conf_maddr, &conf_msize);
267 if (error)
268 return (error);
269
270 if (mem_addr != conf_maddr)
271 return (ENXIO);
272
273 error = ed_alloc_memory(dev, 0, memsize);
274 if (error)
275 return (error);
276
277 sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
278 }
279
280 /*
281 * Fill in the rest of the soft config structure.
282 */
283
284 /*
285 * The transmit page index.
286 */
287
288 sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
289
290 if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
291 sc->txb_cnt = 1;
292 else
293 sc->txb_cnt = 2;
294
295 /*
296 * Memory description
297 */
298
299 sc->mem_size = memsize;
300 sc->mem_ring = sc->mem_start +
301 (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
302 sc->mem_end = sc->mem_start + sc->mem_size;
303
304 /*
305 * Receive area starts after the transmit area and
306 * continues till the end of memory.
307 */
308
309 sc->rec_page_start = sc->tx_page_start +
310 (sc->txb_cnt * ED_TXBUF_SIZE);
311 sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
312
313
314 sc->cr_proto = 0; /* value works */
315
316 /*
317 * Set the wrap registers for string I/O reads.
318 */
319
320 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
321 ed_asic_outw(sc, ED_HPP_HW_WRAP,
322 ((sc->rec_page_start / ED_PAGE_SIZE) |
323 (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
324
325 /*
326 * Reset the register page to normal operation.
327 */
328
329 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
330
331 /*
332 * Verify that we can read/write from adapter memory.
333 * Create test pattern.
334 */
335
336 for (n = 0; n < ED_HPP_TEST_SIZE; n++)
337 test_pattern[n] = (n*n) ^ ~n;
338
339#undef ED_HPP_TEST_SIZE
340
341 /*
342 * Check that the memory is accessible thru the I/O ports.
343 * Write out the contents of "test_pattern", read back
344 * into "test_buffer" and compare the two for any
345 * mismatch.
346 */
347
348 for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
349 ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
350 sizeof(test_pattern));
351 ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
352 test_buffer, sizeof(test_pattern));
353
354 if (bcmp(test_pattern, test_buffer,
355 sizeof(test_pattern)))
356 return (ENXIO);
357 }
358
359 sc->sc_mediachg = ed_hpp_set_physical_link;
360 return (0);
361}
362
363/*
364 * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
365 */
366
367static void
368ed_hpp_set_physical_link(struct ed_softc *sc)
369{
370 struct ifnet *ifp = sc->ifp;
371 int lan_page;
372
373 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
374 lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
375
376 if (ifp->if_flags & IFF_LINK2) {
377 /*
378 * Use the AUI port.
379 */
380
381 lan_page |= ED_HPP_LAN_AUI;
382 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
383 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
384 } else {
385 /*
386 * Use the ThinLan interface
387 */
388
389 lan_page &= ~ED_HPP_LAN_AUI;
390 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
391 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
392 }
393
394 /*
395 * Wait for the lan card to re-initialize itself
396 */
397 DELAY(150000); /* wait 150 ms */
398
399 /*
400 * Restore normal pages.
401 */
402 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
403}
404
405/*
406 * Support routines to handle the HP PC Lan+ card.
407 */
408
409/*
410 * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
411 * IO.
412 */
413
414void
415ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
416 uint16_t amount)
417{
418 int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
419
420 /* Program the source address in RAM */
421 ed_asic_outw(sc, ED_HPP_PAGE_2, src);
422
423 /*
424 * The HP PC Lan+ card supports word reads as well as
425 * a memory mapped i/o port that is aliased to every
426 * even address on the board.
427 */
428 if (sc->hpp_mem_start) {
429 /* Enable memory mapped access. */
430 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
431 ~(ED_HPP_OPTION_MEM_DISABLE |
432 ED_HPP_OPTION_BOOT_ROM_ENB));
433
434 if (use_32bit_access && (amount > 3)) {
435 uint32_t *dl = (uint32_t *) dst;
436 volatile uint32_t *const sl =
437 (uint32_t *) sc->hpp_mem_start;
438 uint32_t *const fence = dl + (amount >> 2);
439
440 /*
441 * Copy out NIC data. We could probably write this
442 * as a `movsl'. The currently generated code is lousy.
443 */
444 while (dl < fence)
445 *dl++ = *sl;
446
447 dst += (amount & ~3);
448 amount &= 3;
449
450 }
451
452 /* Finish off any words left, as a series of short reads */
453 if (amount > 1) {
454 u_short *d = (u_short *) dst;
455 volatile u_short *const s =
456 (u_short *) sc->hpp_mem_start;
457 u_short *const fence = d + (amount >> 1);
458
459 /* Copy out NIC data. */
460 while (d < fence)
461 *d++ = *s;
462
463 dst += (amount & ~1);
464 amount &= 1;
465 }
466
467 /*
468 * read in a byte; however we need to always read 16 bits
469 * at a time or the hardware gets into a funny state
470 */
471
472 if (amount == 1) {
473 /* need to read in a short and copy LSB */
474 volatile u_short *const s =
475 (volatile u_short *) sc->hpp_mem_start;
476 *dst = (*s) & 0xFF;
477 }
478
479 /* Restore Boot ROM access. */
480 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
481 } else {
482 /* Read in data using the I/O port */
483 if (use_32bit_access && (amount > 3)) {
484 ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
485 dst += (amount & ~3);
486 amount &= 3;
487 }
488 if (amount > 1) {
489 ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
490 dst += (amount & ~1);
491 amount &= 1;
492 }
493 if (amount == 1) { /* read in a short and keep the LSB */
494 *dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
495 }
496 }
497}
498
499/*
500 * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
501 * IO.
502 * Only used in the probe routine to test the memory. 'len' must
503 * be even.
504 */
505static void
506ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
507{
508 /* reset remote DMA complete flag */
509 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
510
511 /* program the write address in RAM */
512 ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
513
514 if (sc->hpp_mem_start) {
515 u_short *s = (u_short *) src;
516 volatile u_short *d = (u_short *) sc->hpp_mem_start;
517 u_short *const fence = s + (len >> 1);
518
519 /*
520 * Enable memory mapped access.
521 */
522 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
523 ~(ED_HPP_OPTION_MEM_DISABLE |
524 ED_HPP_OPTION_BOOT_ROM_ENB));
525
526 /*
527 * Copy to NIC memory.
528 */
529 while (s < fence)
530 *d = *s++;
531
532 /*
533 * Restore Boot ROM access.
534 */
535 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
536 } else {
537 /* write data using I/O writes */
538 ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
539 }
540}
541
542/*
543 * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using
544 * outsw() or via the memory mapped interface to the same register.
545 * Writes have to be in word units; byte accesses won't work and may cause
546 * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
547 * allows it.
548 */
549
550u_short
551ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst)
552{
553 int len, wantbyte;
554 unsigned short total_len;
555 unsigned char savebyte[2];
556 volatile u_short * const d =
557 (volatile u_short *) sc->hpp_mem_start;
558 int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
559
560 /* select page 0 registers */
561 ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
562
563 /* reset remote DMA complete flag */
564 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
565
566 /* program the write address in RAM */
567 ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
568
569 if (sc->hpp_mem_start) /* enable memory mapped I/O */
570 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
571 ~(ED_HPP_OPTION_MEM_DISABLE |
572 ED_HPP_OPTION_BOOT_ROM_ENB));
573
574 wantbyte = 0;
575 total_len = 0;
576
577 if (sc->hpp_mem_start) { /* Memory mapped I/O port */
578 while (m) {
579 total_len += (len = m->m_len);
580 if (len) {
581 caddr_t data = mtod(m, caddr_t);
582 /* finish the last word of the previous mbuf */
583 if (wantbyte) {
584 savebyte[1] = *data;
585 *d = *((u_short *) savebyte);
586 data++; len--; wantbyte = 0;
587 }
588 /* output contiguous words */
589 if ((len > 3) && (use_32bit_accesses)) {
590 volatile uint32_t *const dl =
591 (volatile uint32_t *) d;
592 uint32_t *sl = (uint32_t *) data;
593 uint32_t *fence = sl + (len >> 2);
594
595 while (sl < fence)
596 *dl = *sl++;
597
598 data += (len & ~3);
599 len &= 3;
600 }
601 /* finish off remain 16 bit writes */
602 if (len > 1) {
603 u_short *s = (u_short *) data;
604 u_short *fence = s + (len >> 1);
605
606 while (s < fence)
607 *d = *s++;
608
609 data += (len & ~1);
610 len &= 1;
611 }
612 /* save last byte if needed */
613 if ((wantbyte = (len == 1)) != 0)
614 savebyte[0] = *data;
615 }
616 m = m->m_next; /* to next mbuf */
617 }
618 if (wantbyte) /* write last byte */
619 *d = *((u_short *) savebyte);
620 } else {
621 /* use programmed I/O */
622 while (m) {
623 total_len += (len = m->m_len);
624 if (len) {
625 caddr_t data = mtod(m, caddr_t);
626 /* finish the last word of the previous mbuf */
627 if (wantbyte) {
628 savebyte[1] = *data;
629 ed_asic_outw(sc, ED_HPP_PAGE_4,
630 *((u_short *)savebyte));
631 data++;
632 len--;
633 wantbyte = 0;
634 }
635 /* output contiguous words */
636 if ((len > 3) && use_32bit_accesses) {
637 ed_asic_outsl(sc, ED_HPP_PAGE_4,
638 data, len >> 2);
639 data += (len & ~3);
640 len &= 3;
641 }
642 /* finish off remaining 16 bit accesses */
643 if (len > 1) {
644 ed_asic_outsw(sc, ED_HPP_PAGE_4,
645 data, len >> 1);
646 data += (len & ~1);
647 len &= 1;
648 }
649 if ((wantbyte = (len == 1)) != 0)
650 savebyte[0] = *data;
651
652 } /* if len != 0 */
653 m = m->m_next;
654 }
655 if (wantbyte) /* spit last byte */
656 ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
657
658 }
659
660 if (sc->hpp_mem_start) /* turn off memory mapped i/o */
661 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
662
663 return (total_len);
664}
665
666#endif /* ED_HPP */