2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2005, M. Warner Losh
6 * Copyright (c) 1995, David Greenman
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice unmodified, this list of conditions, and the following
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/sockio.h>
43 #include <sys/kernel.h>
44 #include <sys/socket.h>
45 #include <sys/syslog.h>
49 #include <machine/bus.h>
51 #include <machine/resource.h>
53 #include <net/ethernet.h>
55 #include <net/if_var.h> /* XXX: ed_hpp_set_physical_link() */
56 #include <net/if_arp.h>
57 #include <net/if_dl.h>
58 #include <net/if_mib.h>
59 #include <net/if_media.h>
63 #include <dev/ed/if_edreg.h>
64 #include <dev/ed/if_edvar.h>
66 static void ed_hpp_readmem(struct ed_softc *, bus_size_t, uint8_t *,
68 static void ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
70 static void ed_hpp_set_physical_link(struct ed_softc *sc);
71 static u_short ed_hpp_write_mbufs(struct ed_softc *, struct mbuf *,
75 * Interrupt conversion table for the HP PC LAN+
77 static uint16_t ed_hpp_intr_val[] = {
96 #define ED_HPP_TEST_SIZE 16
99 * Probe and vendor specific initialization for the HP PC Lan+ Cards.
100 * (HP Part nos: 27247B and 27252A).
102 * The card has an asic wrapper around a DS8390 core. The asic handles
103 * host accesses and offers both standard register IO and memory mapped
104 * IO. Memory mapped I/O allows better performance at the expense of greater
105 * chance of an incompatibility with existing ISA cards.
107 * The card has a few caveats: it isn't tolerant of byte wide accesses, only
108 * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions
109 * don't allow 32 bit accesses; these are indicated by a bit in the software
110 * ID register (see if_edreg.h).
112 * Other caveats are: we should read the MAC address only when the card
115 * For more information; please consult the CRYNWR packet driver.
117 * The AUI port is turned on using the "link2" option on the ifconfig
121 ed_probe_HP_pclanp(device_t dev, int port_rid, int flags)
123 struct ed_softc *sc = device_get_softc(dev);
125 int n; /* temp var */
126 int memsize; /* mem on board */
127 u_char checksum; /* checksum of board address */
128 u_char irq; /* board configured IRQ */
129 uint8_t test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */
130 uint8_t test_buffer[ED_HPP_TEST_SIZE]; /* probing card */
131 rman_res_t conf_maddr, conf_msize, conf_irq, junk;
133 error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
137 /* Fill in basic information */
138 sc->asic_offset = ED_HPP_ASIC_OFFSET;
139 sc->nic_offset = ED_HPP_NIC_OFFSET;
141 sc->chip_type = ED_CHIP_TYPE_DP8390;
142 sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */
145 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
148 if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
149 (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
150 ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
151 (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
155 * Read the MAC address and verify checksum on the address.
158 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
159 for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
160 checksum += (sc->enaddr[n] =
161 ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
163 checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
165 if (checksum != 0xFF)
169 * Verify that the software model number is 0.
172 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
173 if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
174 ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
178 * Read in and save the current options configured on card.
181 sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
183 sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
184 ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
187 * Reset the chip. This requires writing to the option register
188 * so take care to preserve the other bits.
191 ed_asic_outw(sc, ED_HPP_OPTION,
192 (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET |
193 ED_HPP_OPTION_CHIP_RESET)));
195 DELAY(5000); /* wait for chip reset to complete */
197 ed_asic_outw(sc, ED_HPP_OPTION,
198 (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
199 ED_HPP_OPTION_CHIP_RESET |
200 ED_HPP_OPTION_ENABLE_IRQ)));
204 if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
205 return (ENXIO); /* reset did not complete */
208 * Read out configuration information.
211 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
213 irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
216 * Check for impossible IRQ.
219 if (irq >= nitems(ed_hpp_intr_val))
223 * If the kernel IRQ was specified with a '?' use the cards idea
224 * of the IRQ. If the kernel IRQ was explicitly specified, it
225 * should match that of the hardware.
227 error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
229 bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
231 if (conf_irq != ed_hpp_intr_val[irq])
236 * Fill in softconfig info.
239 sc->vendor = ED_VENDOR_HP;
240 sc->type = ED_TYPE_HP_PCLANPLUS;
241 sc->type_str = "HP-PCLAN+";
243 sc->mem_shared = 0; /* we DON'T have dual ported RAM */
244 sc->mem_start = 0; /* we use offsets inside the card RAM */
246 sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
249 * The board has 32KB of memory. Is there a way to determine
250 * this programmatically?
256 * Check if memory mapping of the I/O registers possible.
258 if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
262 * determine the memory address from the board.
265 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
266 mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
269 * Check that the kernel specified start of memory and
270 * hardware's idea of it match.
272 error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
273 &conf_maddr, &conf_msize);
277 if (mem_addr != conf_maddr)
280 error = ed_alloc_memory(dev, 0, memsize);
284 sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
288 * Fill in the rest of the soft config structure.
292 * The transmit page index.
295 sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
297 if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
306 sc->mem_size = memsize;
307 sc->mem_ring = sc->mem_start +
308 (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
309 sc->mem_end = sc->mem_start + sc->mem_size;
312 * Receive area starts after the transmit area and
313 * continues till the end of memory.
316 sc->rec_page_start = sc->tx_page_start +
317 (sc->txb_cnt * ED_TXBUF_SIZE);
318 sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
321 sc->cr_proto = 0; /* value works */
324 * Set the wrap registers for string I/O reads.
327 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
328 ed_asic_outw(sc, ED_HPP_HW_WRAP,
329 ((sc->rec_page_start / ED_PAGE_SIZE) |
330 (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
333 * Reset the register page to normal operation.
336 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
339 * Verify that we can read/write from adapter memory.
340 * Create test pattern.
343 for (n = 0; n < ED_HPP_TEST_SIZE; n++)
344 test_pattern[n] = (n*n) ^ ~n;
346 #undef ED_HPP_TEST_SIZE
349 * Check that the memory is accessible thru the I/O ports.
350 * Write out the contents of "test_pattern", read back
351 * into "test_buffer" and compare the two for any
355 for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
356 ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
357 sizeof(test_pattern));
358 ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
359 test_buffer, sizeof(test_pattern));
361 if (bcmp(test_pattern, test_buffer,
362 sizeof(test_pattern)))
366 sc->sc_mediachg = ed_hpp_set_physical_link;
367 sc->sc_write_mbufs = ed_hpp_write_mbufs;
368 sc->readmem = ed_hpp_readmem;
373 * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
377 ed_hpp_set_physical_link(struct ed_softc *sc)
379 struct ifnet *ifp = sc->ifp;
382 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
383 lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
385 if (ifp->if_flags & IFF_LINK2) {
390 lan_page |= ED_HPP_LAN_AUI;
391 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
392 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
395 * Use the ThinLan interface
398 lan_page &= ~ED_HPP_LAN_AUI;
399 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
400 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
404 * Wait for the lan card to re-initialize itself
406 DELAY(150000); /* wait 150 ms */
409 * Restore normal pages.
411 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
415 * Support routines to handle the HP PC Lan+ card.
419 * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
424 ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
427 int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
429 /* Program the source address in RAM */
430 ed_asic_outw(sc, ED_HPP_PAGE_2, src);
433 * The HP PC Lan+ card supports word reads as well as
434 * a memory mapped i/o port that is aliased to every
435 * even address on the board.
437 if (sc->hpp_mem_start) {
438 /* Enable memory mapped access. */
439 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
440 ~(ED_HPP_OPTION_MEM_DISABLE |
441 ED_HPP_OPTION_BOOT_ROM_ENB));
443 if (use_32bit_access && (amount > 3)) {
444 uint32_t *dl = (uint32_t *) dst;
445 volatile uint32_t *const sl =
446 (uint32_t *) sc->hpp_mem_start;
447 uint32_t *const fence = dl + (amount >> 2);
450 * Copy out NIC data. We could probably write this
451 * as a `movsl'. The currently generated code is lousy.
456 dst += (amount & ~3);
461 /* Finish off any words left, as a series of short reads */
463 u_short *d = (u_short *) dst;
464 volatile u_short *const s =
465 (u_short *) sc->hpp_mem_start;
466 u_short *const fence = d + (amount >> 1);
468 /* Copy out NIC data. */
472 dst += (amount & ~1);
477 * read in a byte; however we need to always read 16 bits
478 * at a time or the hardware gets into a funny state
482 /* need to read in a short and copy LSB */
483 volatile u_short *const s =
484 (volatile u_short *) sc->hpp_mem_start;
488 /* Restore Boot ROM access. */
489 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
491 /* Read in data using the I/O port */
492 if (use_32bit_access && (amount > 3)) {
493 ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
494 dst += (amount & ~3);
498 ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
499 dst += (amount & ~1);
502 if (amount == 1) { /* read in a short and keep the LSB */
503 *dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
509 * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
511 * Only used in the probe routine to test the memory. 'len' must
515 ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
517 /* reset remote DMA complete flag */
518 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
520 /* program the write address in RAM */
521 ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
523 if (sc->hpp_mem_start) {
524 u_short *s = (u_short *) src;
525 volatile u_short *d = (u_short *) sc->hpp_mem_start;
526 u_short *const fence = s + (len >> 1);
529 * Enable memory mapped access.
531 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
532 ~(ED_HPP_OPTION_MEM_DISABLE |
533 ED_HPP_OPTION_BOOT_ROM_ENB));
536 * Copy to NIC memory.
542 * Restore Boot ROM access.
544 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
546 /* write data using I/O writes */
547 ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
552 * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using
553 * outsw() or via the memory mapped interface to the same register.
554 * Writes have to be in word units; byte accesses won't work and may cause
555 * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
560 ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, bus_size_t dst)
563 unsigned short total_len;
564 unsigned char savebyte[2];
565 volatile u_short * const d =
566 (volatile u_short *) sc->hpp_mem_start;
567 int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
569 /* select page 0 registers */
570 ed_nic_barrier(sc, ED_P0_CR, 1,
571 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
572 ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
573 ed_nic_barrier(sc, ED_P0_CR, 1,
574 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
576 /* reset remote DMA complete flag */
577 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
579 /* program the write address in RAM */
580 ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
582 if (sc->hpp_mem_start) /* enable memory mapped I/O */
583 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
584 ~(ED_HPP_OPTION_MEM_DISABLE |
585 ED_HPP_OPTION_BOOT_ROM_ENB));
590 if (sc->hpp_mem_start) { /* Memory mapped I/O port */
592 total_len += (len = m->m_len);
594 caddr_t data = mtod(m, caddr_t);
595 /* finish the last word of the previous mbuf */
598 *d = *((u_short *) savebyte);
599 data++; len--; wantbyte = 0;
601 /* output contiguous words */
602 if ((len > 3) && (use_32bit_accesses)) {
603 volatile uint32_t *const dl =
604 (volatile uint32_t *) d;
605 uint32_t *sl = (uint32_t *) data;
606 uint32_t *fence = sl + (len >> 2);
614 /* finish off remain 16 bit writes */
616 u_short *s = (u_short *) data;
617 u_short *fence = s + (len >> 1);
625 /* save last byte if needed */
626 if ((wantbyte = (len == 1)) != 0)
629 m = m->m_next; /* to next mbuf */
631 if (wantbyte) /* write last byte */
632 *d = *((u_short *) savebyte);
634 /* use programmed I/O */
636 total_len += (len = m->m_len);
638 caddr_t data = mtod(m, caddr_t);
639 /* finish the last word of the previous mbuf */
642 ed_asic_outw(sc, ED_HPP_PAGE_4,
643 *((u_short *)savebyte));
648 /* output contiguous words */
649 if ((len > 3) && use_32bit_accesses) {
650 ed_asic_outsl(sc, ED_HPP_PAGE_4,
655 /* finish off remaining 16 bit accesses */
657 ed_asic_outsw(sc, ED_HPP_PAGE_4,
662 if ((wantbyte = (len == 1)) != 0)
668 if (wantbyte) /* spit last byte */
669 ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
673 if (sc->hpp_mem_start) /* turn off memory mapped i/o */
674 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);