2 * Copyright (c) 2005, M. Warner Losh
4 * Copyright (c) 1995, David Greenman
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/sockio.h>
41 #include <sys/kernel.h>
42 #include <sys/socket.h>
43 #include <sys/syslog.h>
47 #include <machine/bus.h>
49 #include <machine/resource.h>
51 #include <net/ethernet.h>
53 #include <net/if_var.h> /* XXX: ed_hpp_set_physical_link() */
54 #include <net/if_arp.h>
55 #include <net/if_dl.h>
56 #include <net/if_mib.h>
57 #include <net/if_media.h>
61 #include <dev/ed/if_edreg.h>
62 #include <dev/ed/if_edvar.h>
64 static void ed_hpp_readmem(struct ed_softc *, bus_size_t, uint8_t *,
66 static void ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
68 static void ed_hpp_set_physical_link(struct ed_softc *sc);
69 static u_short ed_hpp_write_mbufs(struct ed_softc *, struct mbuf *,
73 * Interrupt conversion table for the HP PC LAN+
75 static uint16_t ed_hpp_intr_val[] = {
94 #define ED_HPP_TEST_SIZE 16
97 * Probe and vendor specific initialization for the HP PC Lan+ Cards.
98 * (HP Part nos: 27247B and 27252A).
100 * The card has an asic wrapper around a DS8390 core. The asic handles
101 * host accesses and offers both standard register IO and memory mapped
102 * IO. Memory mapped I/O allows better performance at the expense of greater
103 * chance of an incompatibility with existing ISA cards.
105 * The card has a few caveats: it isn't tolerant of byte wide accesses, only
106 * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions
107 * don't allow 32 bit accesses; these are indicated by a bit in the software
108 * ID register (see if_edreg.h).
110 * Other caveats are: we should read the MAC address only when the card
113 * For more information; please consult the CRYNWR packet driver.
115 * The AUI port is turned on using the "link2" option on the ifconfig
119 ed_probe_HP_pclanp(device_t dev, int port_rid, int flags)
121 struct ed_softc *sc = device_get_softc(dev);
123 int n; /* temp var */
124 int memsize; /* mem on board */
125 u_char checksum; /* checksum of board address */
126 u_char irq; /* board configured IRQ */
127 uint8_t test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */
128 uint8_t test_buffer[ED_HPP_TEST_SIZE]; /* probing card */
129 rman_res_t conf_maddr, conf_msize, conf_irq, junk;
131 error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
135 /* Fill in basic information */
136 sc->asic_offset = ED_HPP_ASIC_OFFSET;
137 sc->nic_offset = ED_HPP_NIC_OFFSET;
139 sc->chip_type = ED_CHIP_TYPE_DP8390;
140 sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */
143 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
146 if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
147 (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
148 ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
149 (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
153 * Read the MAC address and verify checksum on the address.
156 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
157 for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
158 checksum += (sc->enaddr[n] =
159 ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
161 checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
163 if (checksum != 0xFF)
167 * Verify that the software model number is 0.
170 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
171 if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
172 ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
176 * Read in and save the current options configured on card.
179 sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
181 sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
182 ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
185 * Reset the chip. This requires writing to the option register
186 * so take care to preserve the other bits.
189 ed_asic_outw(sc, ED_HPP_OPTION,
190 (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET |
191 ED_HPP_OPTION_CHIP_RESET)));
193 DELAY(5000); /* wait for chip reset to complete */
195 ed_asic_outw(sc, ED_HPP_OPTION,
196 (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
197 ED_HPP_OPTION_CHIP_RESET |
198 ED_HPP_OPTION_ENABLE_IRQ)));
202 if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
203 return (ENXIO); /* reset did not complete */
206 * Read out configuration information.
209 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
211 irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
214 * Check for impossible IRQ.
217 if (irq >= nitems(ed_hpp_intr_val))
221 * If the kernel IRQ was specified with a '?' use the cards idea
222 * of the IRQ. If the kernel IRQ was explicitly specified, it
223 * should match that of the hardware.
225 error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
227 bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
229 if (conf_irq != ed_hpp_intr_val[irq])
234 * Fill in softconfig info.
237 sc->vendor = ED_VENDOR_HP;
238 sc->type = ED_TYPE_HP_PCLANPLUS;
239 sc->type_str = "HP-PCLAN+";
241 sc->mem_shared = 0; /* we DON'T have dual ported RAM */
242 sc->mem_start = 0; /* we use offsets inside the card RAM */
244 sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
247 * The board has 32KB of memory. Is there a way to determine
248 * this programmatically?
254 * Check if memory mapping of the I/O registers possible.
256 if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
260 * determine the memory address from the board.
263 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
264 mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
267 * Check that the kernel specified start of memory and
268 * hardware's idea of it match.
270 error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
271 &conf_maddr, &conf_msize);
275 if (mem_addr != conf_maddr)
278 error = ed_alloc_memory(dev, 0, memsize);
282 sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
286 * Fill in the rest of the soft config structure.
290 * The transmit page index.
293 sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
295 if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
304 sc->mem_size = memsize;
305 sc->mem_ring = sc->mem_start +
306 (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
307 sc->mem_end = sc->mem_start + sc->mem_size;
310 * Receive area starts after the transmit area and
311 * continues till the end of memory.
314 sc->rec_page_start = sc->tx_page_start +
315 (sc->txb_cnt * ED_TXBUF_SIZE);
316 sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
319 sc->cr_proto = 0; /* value works */
322 * Set the wrap registers for string I/O reads.
325 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
326 ed_asic_outw(sc, ED_HPP_HW_WRAP,
327 ((sc->rec_page_start / ED_PAGE_SIZE) |
328 (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
331 * Reset the register page to normal operation.
334 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
337 * Verify that we can read/write from adapter memory.
338 * Create test pattern.
341 for (n = 0; n < ED_HPP_TEST_SIZE; n++)
342 test_pattern[n] = (n*n) ^ ~n;
344 #undef ED_HPP_TEST_SIZE
347 * Check that the memory is accessible thru the I/O ports.
348 * Write out the contents of "test_pattern", read back
349 * into "test_buffer" and compare the two for any
353 for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
354 ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
355 sizeof(test_pattern));
356 ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
357 test_buffer, sizeof(test_pattern));
359 if (bcmp(test_pattern, test_buffer,
360 sizeof(test_pattern)))
364 sc->sc_mediachg = ed_hpp_set_physical_link;
365 sc->sc_write_mbufs = ed_hpp_write_mbufs;
366 sc->readmem = ed_hpp_readmem;
371 * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
375 ed_hpp_set_physical_link(struct ed_softc *sc)
377 struct ifnet *ifp = sc->ifp;
380 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
381 lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
383 if (ifp->if_flags & IFF_LINK2) {
388 lan_page |= ED_HPP_LAN_AUI;
389 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
390 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
393 * Use the ThinLan interface
396 lan_page &= ~ED_HPP_LAN_AUI;
397 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
398 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
402 * Wait for the lan card to re-initialize itself
404 DELAY(150000); /* wait 150 ms */
407 * Restore normal pages.
409 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
413 * Support routines to handle the HP PC Lan+ card.
417 * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
422 ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
425 int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
427 /* Program the source address in RAM */
428 ed_asic_outw(sc, ED_HPP_PAGE_2, src);
431 * The HP PC Lan+ card supports word reads as well as
432 * a memory mapped i/o port that is aliased to every
433 * even address on the board.
435 if (sc->hpp_mem_start) {
436 /* Enable memory mapped access. */
437 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
438 ~(ED_HPP_OPTION_MEM_DISABLE |
439 ED_HPP_OPTION_BOOT_ROM_ENB));
441 if (use_32bit_access && (amount > 3)) {
442 uint32_t *dl = (uint32_t *) dst;
443 volatile uint32_t *const sl =
444 (uint32_t *) sc->hpp_mem_start;
445 uint32_t *const fence = dl + (amount >> 2);
448 * Copy out NIC data. We could probably write this
449 * as a `movsl'. The currently generated code is lousy.
454 dst += (amount & ~3);
459 /* Finish off any words left, as a series of short reads */
461 u_short *d = (u_short *) dst;
462 volatile u_short *const s =
463 (u_short *) sc->hpp_mem_start;
464 u_short *const fence = d + (amount >> 1);
466 /* Copy out NIC data. */
470 dst += (amount & ~1);
475 * read in a byte; however we need to always read 16 bits
476 * at a time or the hardware gets into a funny state
480 /* need to read in a short and copy LSB */
481 volatile u_short *const s =
482 (volatile u_short *) sc->hpp_mem_start;
486 /* Restore Boot ROM access. */
487 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
489 /* Read in data using the I/O port */
490 if (use_32bit_access && (amount > 3)) {
491 ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
492 dst += (amount & ~3);
496 ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
497 dst += (amount & ~1);
500 if (amount == 1) { /* read in a short and keep the LSB */
501 *dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
507 * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
509 * Only used in the probe routine to test the memory. 'len' must
513 ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
515 /* reset remote DMA complete flag */
516 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
518 /* program the write address in RAM */
519 ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
521 if (sc->hpp_mem_start) {
522 u_short *s = (u_short *) src;
523 volatile u_short *d = (u_short *) sc->hpp_mem_start;
524 u_short *const fence = s + (len >> 1);
527 * Enable memory mapped access.
529 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
530 ~(ED_HPP_OPTION_MEM_DISABLE |
531 ED_HPP_OPTION_BOOT_ROM_ENB));
534 * Copy to NIC memory.
540 * Restore Boot ROM access.
542 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
544 /* write data using I/O writes */
545 ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
550 * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using
551 * outsw() or via the memory mapped interface to the same register.
552 * Writes have to be in word units; byte accesses won't work and may cause
553 * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
558 ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, bus_size_t dst)
561 unsigned short total_len;
562 unsigned char savebyte[2];
563 volatile u_short * const d =
564 (volatile u_short *) sc->hpp_mem_start;
565 int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
567 /* select page 0 registers */
568 ed_nic_barrier(sc, ED_P0_CR, 1,
569 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
570 ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
571 ed_nic_barrier(sc, ED_P0_CR, 1,
572 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
574 /* reset remote DMA complete flag */
575 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
577 /* program the write address in RAM */
578 ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
580 if (sc->hpp_mem_start) /* enable memory mapped I/O */
581 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
582 ~(ED_HPP_OPTION_MEM_DISABLE |
583 ED_HPP_OPTION_BOOT_ROM_ENB));
588 if (sc->hpp_mem_start) { /* Memory mapped I/O port */
590 total_len += (len = m->m_len);
592 caddr_t data = mtod(m, caddr_t);
593 /* finish the last word of the previous mbuf */
596 *d = *((u_short *) savebyte);
597 data++; len--; wantbyte = 0;
599 /* output contiguous words */
600 if ((len > 3) && (use_32bit_accesses)) {
601 volatile uint32_t *const dl =
602 (volatile uint32_t *) d;
603 uint32_t *sl = (uint32_t *) data;
604 uint32_t *fence = sl + (len >> 2);
612 /* finish off remain 16 bit writes */
614 u_short *s = (u_short *) data;
615 u_short *fence = s + (len >> 1);
623 /* save last byte if needed */
624 if ((wantbyte = (len == 1)) != 0)
627 m = m->m_next; /* to next mbuf */
629 if (wantbyte) /* write last byte */
630 *d = *((u_short *) savebyte);
632 /* use programmed I/O */
634 total_len += (len = m->m_len);
636 caddr_t data = mtod(m, caddr_t);
637 /* finish the last word of the previous mbuf */
640 ed_asic_outw(sc, ED_HPP_PAGE_4,
641 *((u_short *)savebyte));
646 /* output contiguous words */
647 if ((len > 3) && use_32bit_accesses) {
648 ed_asic_outsl(sc, ED_HPP_PAGE_4,
653 /* finish off remaining 16 bit accesses */
655 ed_asic_outsw(sc, ED_HPP_PAGE_4,
660 if ((wantbyte = (len == 1)) != 0)
666 if (wantbyte) /* spit last byte */
667 ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
671 if (sc->hpp_mem_start) /* turn off memory mapped i/o */
672 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);