0a15fd3fab
o Lock ed o Fix extra newline in probe messages o Eliminate gone. o Make detach less-racy. o Eliminate spl* o Switch from timeout/untimeout to callout interface. o Read/write card memory using bus_space calls. o generalize readmem so that we don't need ifs in the code. o Fix memory stuff to be consistant. o Remove OLDCARD compat stuff. o Mark interrupt as MPSAFE. # sic, hpp not tested at all # ISA and PCI attachments lightly tested
665 lines
16 KiB
C
665 lines
16 KiB
C
/*-
|
|
* Copyright (c) 2005, M. Warner Losh
|
|
* All rights reserved.
|
|
* Copyright (c) 1995, David Greenman
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice unmodified, this list of conditions, and the following
|
|
* disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_ed.h"
|
|
|
|
#ifdef ED_HPP
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <sys/rman.h>
|
|
#include <machine/resource.h>
|
|
|
|
#include <net/ethernet.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_mib.h>
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include <dev/ed/if_edreg.h>
|
|
#include <dev/ed/if_edvar.h>
|
|
|
|
static void ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
|
|
uint16_t);
|
|
|
|
/*
|
|
* Interrupt conversion table for the HP PC LAN+
|
|
*/
|
|
static uint16_t ed_hpp_intr_val[] = {
|
|
0, /* 0 */
|
|
0, /* 1 */
|
|
0, /* 2 */
|
|
3, /* 3 */
|
|
4, /* 4 */
|
|
5, /* 5 */
|
|
6, /* 6 */
|
|
7, /* 7 */
|
|
0, /* 8 */
|
|
9, /* 9 */
|
|
10, /* 10 */
|
|
11, /* 11 */
|
|
12, /* 12 */
|
|
0, /* 13 */
|
|
0, /* 14 */
|
|
15 /* 15 */
|
|
};
|
|
|
|
#define ED_HPP_TEST_SIZE 16
|
|
|
|
/*
|
|
* Probe and vendor specific initialization for the HP PC Lan+ Cards.
|
|
* (HP Part nos: 27247B and 27252A).
|
|
*
|
|
* The card has an asic wrapper around a DS8390 core. The asic handles
|
|
* host accesses and offers both standard register IO and memory mapped
|
|
* IO. Memory mapped I/O allows better performance at the expense of greater
|
|
* chance of an incompatibility with existing ISA cards.
|
|
*
|
|
* The card has a few caveats: it isn't tolerant of byte wide accesses, only
|
|
* short (16 bit) or word (32 bit) accesses are allowed. Some card revisions
|
|
* don't allow 32 bit accesses; these are indicated by a bit in the software
|
|
* ID register (see if_edreg.h).
|
|
*
|
|
* Other caveats are: we should read the MAC address only when the card
|
|
* is inactive.
|
|
*
|
|
* For more information; please consult the CRYNWR packet driver.
|
|
*
|
|
* The AUI port is turned on using the "link2" option on the ifconfig
|
|
* command line.
|
|
*/
|
|
int
|
|
ed_probe_HP_pclanp(device_t dev, int port_rid, int flags)
|
|
{
|
|
struct ed_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
int n; /* temp var */
|
|
int memsize; /* mem on board */
|
|
u_char checksum; /* checksum of board address */
|
|
u_char irq; /* board configured IRQ */
|
|
uint8_t test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */
|
|
uint8_t test_buffer[ED_HPP_TEST_SIZE]; /* probing card */
|
|
u_long conf_maddr, conf_msize, conf_irq, junk;
|
|
|
|
error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* Fill in basic information */
|
|
sc->asic_offset = ED_HPP_ASIC_OFFSET;
|
|
sc->nic_offset = ED_HPP_NIC_OFFSET;
|
|
|
|
sc->chip_type = ED_CHIP_TYPE_DP8390;
|
|
sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */
|
|
|
|
/*
|
|
* Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
|
|
*/
|
|
|
|
if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
|
|
(ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
|
|
((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
|
|
(ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
|
|
return ENXIO;
|
|
|
|
/*
|
|
* Read the MAC address and verify checksum on the address.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
|
|
for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
|
|
checksum += (sc->enaddr[n] =
|
|
ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
|
|
|
|
checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
|
|
|
|
if (checksum != 0xFF)
|
|
return ENXIO;
|
|
|
|
/*
|
|
* Verify that the software model number is 0.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
|
|
if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
|
|
ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
|
|
return ENXIO;
|
|
|
|
/*
|
|
* Read in and save the current options configured on card.
|
|
*/
|
|
|
|
sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
|
|
|
|
sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
|
|
ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
|
|
|
|
/*
|
|
* Reset the chip. This requires writing to the option register
|
|
* so take care to preserve the other bits.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_OPTION,
|
|
(sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET |
|
|
ED_HPP_OPTION_CHIP_RESET)));
|
|
|
|
DELAY(5000); /* wait for chip reset to complete */
|
|
|
|
ed_asic_outw(sc, ED_HPP_OPTION,
|
|
(sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
|
|
ED_HPP_OPTION_CHIP_RESET |
|
|
ED_HPP_OPTION_ENABLE_IRQ)));
|
|
|
|
DELAY(5000);
|
|
|
|
if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
|
|
return ENXIO; /* reset did not complete */
|
|
|
|
/*
|
|
* Read out configuration information.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
|
|
|
|
irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
|
|
|
|
/*
|
|
* Check for impossible IRQ.
|
|
*/
|
|
|
|
if (irq >= (sizeof(ed_hpp_intr_val) / sizeof(ed_hpp_intr_val[0])))
|
|
return ENXIO;
|
|
|
|
/*
|
|
* If the kernel IRQ was specified with a '?' use the cards idea
|
|
* of the IRQ. If the kernel IRQ was explicitly specified, it
|
|
* should match that of the hardware.
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
|
|
if (error)
|
|
bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
|
|
else {
|
|
if (conf_irq != ed_hpp_intr_val[irq])
|
|
return (ENXIO);
|
|
}
|
|
|
|
/*
|
|
* Fill in softconfig info.
|
|
*/
|
|
|
|
sc->vendor = ED_VENDOR_HP;
|
|
sc->type = ED_TYPE_HP_PCLANPLUS;
|
|
sc->type_str = "HP-PCLAN+";
|
|
|
|
sc->mem_shared = 0; /* we DON'T have dual ported RAM */
|
|
sc->mem_start = 0; /* we use offsets inside the card RAM */
|
|
|
|
sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
|
|
|
|
/*
|
|
* The board has 32KB of memory. Is there a way to determine
|
|
* this programmatically?
|
|
*/
|
|
|
|
memsize = 32768;
|
|
|
|
/*
|
|
* Check if memory mapping of the I/O registers possible.
|
|
*/
|
|
if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
|
|
u_long mem_addr;
|
|
|
|
/*
|
|
* determine the memory address from the board.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
|
|
mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
|
|
|
|
/*
|
|
* Check that the kernel specified start of memory and
|
|
* hardware's idea of it match.
|
|
*/
|
|
error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
|
|
&conf_maddr, &conf_msize);
|
|
if (error)
|
|
return (error);
|
|
|
|
if (mem_addr != conf_maddr)
|
|
return ENXIO;
|
|
|
|
error = ed_alloc_memory(dev, 0, memsize);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
|
|
}
|
|
|
|
/*
|
|
* Fill in the rest of the soft config structure.
|
|
*/
|
|
|
|
/*
|
|
* The transmit page index.
|
|
*/
|
|
|
|
sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
|
|
|
|
if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
|
|
sc->txb_cnt = 1;
|
|
else
|
|
sc->txb_cnt = 2;
|
|
|
|
/*
|
|
* Memory description
|
|
*/
|
|
|
|
sc->mem_size = memsize;
|
|
sc->mem_ring = sc->mem_start +
|
|
(sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
|
|
sc->mem_end = sc->mem_start + sc->mem_size;
|
|
|
|
/*
|
|
* Receive area starts after the transmit area and
|
|
* continues till the end of memory.
|
|
*/
|
|
|
|
sc->rec_page_start = sc->tx_page_start +
|
|
(sc->txb_cnt * ED_TXBUF_SIZE);
|
|
sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
|
|
|
|
|
|
sc->cr_proto = 0; /* value works */
|
|
|
|
/*
|
|
* Set the wrap registers for string I/O reads.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
|
|
ed_asic_outw(sc, ED_HPP_HW_WRAP,
|
|
((sc->rec_page_start / ED_PAGE_SIZE) |
|
|
(((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
|
|
|
|
/*
|
|
* Reset the register page to normal operation.
|
|
*/
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
|
|
|
|
/*
|
|
* Verify that we can read/write from adapter memory.
|
|
* Create test pattern.
|
|
*/
|
|
|
|
for (n = 0; n < ED_HPP_TEST_SIZE; n++)
|
|
test_pattern[n] = (n*n) ^ ~n;
|
|
|
|
#undef ED_HPP_TEST_SIZE
|
|
|
|
/*
|
|
* Check that the memory is accessible thru the I/O ports.
|
|
* Write out the contents of "test_pattern", read back
|
|
* into "test_buffer" and compare the two for any
|
|
* mismatch.
|
|
*/
|
|
|
|
for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
|
|
ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
|
|
sizeof(test_pattern));
|
|
ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
|
|
test_buffer, sizeof(test_pattern));
|
|
|
|
if (bcmp(test_pattern, test_buffer,
|
|
sizeof(test_pattern)))
|
|
return ENXIO;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* HP PC Lan+ : Set the physical link to use AUI or TP/TL.
|
|
*/
|
|
|
|
void
|
|
ed_hpp_set_physical_link(struct ed_softc *sc)
|
|
{
|
|
struct ifnet *ifp = sc->ifp;
|
|
int lan_page;
|
|
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
|
|
lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
|
|
|
|
if (ifp->if_flags & IFF_ALTPHYS) {
|
|
/*
|
|
* Use the AUI port.
|
|
*/
|
|
|
|
lan_page |= ED_HPP_LAN_AUI;
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
|
|
ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
|
|
} else {
|
|
/*
|
|
* Use the ThinLan interface
|
|
*/
|
|
|
|
lan_page &= ~ED_HPP_LAN_AUI;
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
|
|
ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
|
|
}
|
|
|
|
/*
|
|
* Wait for the lan card to re-initialize itself
|
|
*/
|
|
DELAY(150000); /* wait 150 ms */
|
|
|
|
/*
|
|
* Restore normal pages.
|
|
*/
|
|
ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
|
|
}
|
|
|
|
/*
|
|
* Support routines to handle the HP PC Lan+ card.
|
|
*/
|
|
|
|
/*
|
|
* HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
|
|
* IO.
|
|
*/
|
|
|
|
void
|
|
ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
|
|
uint16_t amount)
|
|
{
|
|
int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
|
|
|
|
/* Program the source address in RAM */
|
|
ed_asic_outw(sc, ED_HPP_PAGE_2, src);
|
|
|
|
/*
|
|
* The HP PC Lan+ card supports word reads as well as
|
|
* a memory mapped i/o port that is aliased to every
|
|
* even address on the board.
|
|
*/
|
|
if (sc->hpp_mem_start) {
|
|
/* Enable memory mapped access. */
|
|
ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
|
|
~(ED_HPP_OPTION_MEM_DISABLE |
|
|
ED_HPP_OPTION_BOOT_ROM_ENB));
|
|
|
|
if (use_32bit_access && (amount > 3)) {
|
|
uint32_t *dl = (uint32_t *) dst;
|
|
volatile uint32_t *const sl =
|
|
(uint32_t *) sc->hpp_mem_start;
|
|
uint32_t *const fence = dl + (amount >> 2);
|
|
|
|
/*
|
|
* Copy out NIC data. We could probably write this
|
|
* as a `movsl'. The currently generated code is lousy.
|
|
*/
|
|
while (dl < fence)
|
|
*dl++ = *sl;
|
|
|
|
dst += (amount & ~3);
|
|
amount &= 3;
|
|
|
|
}
|
|
|
|
/* Finish off any words left, as a series of short reads */
|
|
if (amount > 1) {
|
|
u_short *d = (u_short *) dst;
|
|
volatile u_short *const s =
|
|
(u_short *) sc->hpp_mem_start;
|
|
u_short *const fence = d + (amount >> 1);
|
|
|
|
/* Copy out NIC data. */
|
|
while (d < fence)
|
|
*d++ = *s;
|
|
|
|
dst += (amount & ~1);
|
|
amount &= 1;
|
|
}
|
|
|
|
/*
|
|
* read in a byte; however we need to always read 16 bits
|
|
* at a time or the hardware gets into a funny state
|
|
*/
|
|
|
|
if (amount == 1) {
|
|
/* need to read in a short and copy LSB */
|
|
volatile u_short *const s =
|
|
(volatile u_short *) sc->hpp_mem_start;
|
|
*dst = (*s) & 0xFF;
|
|
}
|
|
|
|
/* Restore Boot ROM access. */
|
|
ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
|
|
} else {
|
|
/* Read in data using the I/O port */
|
|
if (use_32bit_access && (amount > 3)) {
|
|
ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
|
|
dst += (amount & ~3);
|
|
amount &= 3;
|
|
}
|
|
if (amount > 1) {
|
|
ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
|
|
dst += (amount & ~1);
|
|
amount &= 1;
|
|
}
|
|
if (amount == 1) { /* read in a short and keep the LSB */
|
|
*dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
|
|
* IO.
|
|
* Only used in the probe routine to test the memory. 'len' must
|
|
* be even.
|
|
*/
|
|
static void
|
|
ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
|
|
{
|
|
/* reset remote DMA complete flag */
|
|
ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
|
|
|
|
/* program the write address in RAM */
|
|
ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
|
|
|
|
if (sc->hpp_mem_start) {
|
|
u_short *s = (u_short *) src;
|
|
volatile u_short *d = (u_short *) sc->hpp_mem_start;
|
|
u_short *const fence = s + (len >> 1);
|
|
|
|
/*
|
|
* Enable memory mapped access.
|
|
*/
|
|
ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
|
|
~(ED_HPP_OPTION_MEM_DISABLE |
|
|
ED_HPP_OPTION_BOOT_ROM_ENB));
|
|
|
|
/*
|
|
* Copy to NIC memory.
|
|
*/
|
|
while (s < fence)
|
|
*d = *s++;
|
|
|
|
/*
|
|
* Restore Boot ROM access.
|
|
*/
|
|
ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
|
|
} else {
|
|
/* write data using I/O writes */
|
|
ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write to HP PC Lan+ NIC memory. Access to the NIC can be by using
|
|
* outsw() or via the memory mapped interface to the same register.
|
|
* Writes have to be in word units; byte accesses won't work and may cause
|
|
* the NIC to behave weirdly. Long word accesses are permitted if the ASIC
|
|
* allows it.
|
|
*/
|
|
|
|
u_short
|
|
ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst)
|
|
{
|
|
int len, wantbyte;
|
|
unsigned short total_len;
|
|
unsigned char savebyte[2];
|
|
volatile u_short * const d =
|
|
(volatile u_short *) sc->hpp_mem_start;
|
|
int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
|
|
|
|
/* select page 0 registers */
|
|
ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
|
|
|
|
/* reset remote DMA complete flag */
|
|
ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
|
|
|
|
/* program the write address in RAM */
|
|
ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
|
|
|
|
if (sc->hpp_mem_start) /* enable memory mapped I/O */
|
|
ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
|
|
~(ED_HPP_OPTION_MEM_DISABLE |
|
|
ED_HPP_OPTION_BOOT_ROM_ENB));
|
|
|
|
wantbyte = 0;
|
|
total_len = 0;
|
|
|
|
if (sc->hpp_mem_start) { /* Memory mapped I/O port */
|
|
while (m) {
|
|
total_len += (len = m->m_len);
|
|
if (len) {
|
|
caddr_t data = mtod(m, caddr_t);
|
|
/* finish the last word of the previous mbuf */
|
|
if (wantbyte) {
|
|
savebyte[1] = *data;
|
|
*d = *((u_short *) savebyte);
|
|
data++; len--; wantbyte = 0;
|
|
}
|
|
/* output contiguous words */
|
|
if ((len > 3) && (use_32bit_accesses)) {
|
|
volatile uint32_t *const dl =
|
|
(volatile uint32_t *) d;
|
|
uint32_t *sl = (uint32_t *) data;
|
|
uint32_t *fence = sl + (len >> 2);
|
|
|
|
while (sl < fence)
|
|
*dl = *sl++;
|
|
|
|
data += (len & ~3);
|
|
len &= 3;
|
|
}
|
|
/* finish off remain 16 bit writes */
|
|
if (len > 1) {
|
|
u_short *s = (u_short *) data;
|
|
u_short *fence = s + (len >> 1);
|
|
|
|
while (s < fence)
|
|
*d = *s++;
|
|
|
|
data += (len & ~1);
|
|
len &= 1;
|
|
}
|
|
/* save last byte if needed */
|
|
if ((wantbyte = (len == 1)) != 0)
|
|
savebyte[0] = *data;
|
|
}
|
|
m = m->m_next; /* to next mbuf */
|
|
}
|
|
if (wantbyte) /* write last byte */
|
|
*d = *((u_short *) savebyte);
|
|
} else {
|
|
/* use programmed I/O */
|
|
while (m) {
|
|
total_len += (len = m->m_len);
|
|
if (len) {
|
|
caddr_t data = mtod(m, caddr_t);
|
|
/* finish the last word of the previous mbuf */
|
|
if (wantbyte) {
|
|
savebyte[1] = *data;
|
|
ed_asic_outw(sc, ED_HPP_PAGE_4,
|
|
*((u_short *)savebyte));
|
|
data++;
|
|
len--;
|
|
wantbyte = 0;
|
|
}
|
|
/* output contiguous words */
|
|
if ((len > 3) && use_32bit_accesses) {
|
|
ed_asic_outsl(sc, ED_HPP_PAGE_4,
|
|
data, len >> 2);
|
|
data += (len & ~3);
|
|
len &= 3;
|
|
}
|
|
/* finish off remaining 16 bit accesses */
|
|
if (len > 1) {
|
|
ed_asic_outsw(sc, ED_HPP_PAGE_4,
|
|
data, len >> 1);
|
|
data += (len & ~1);
|
|
len &= 1;
|
|
}
|
|
if ((wantbyte = (len == 1)) != 0)
|
|
savebyte[0] = *data;
|
|
|
|
} /* if len != 0 */
|
|
m = m->m_next;
|
|
}
|
|
if (wantbyte) /* spit last byte */
|
|
ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
|
|
|
|
}
|
|
|
|
if (sc->hpp_mem_start) /* turn off memory mapped i/o */
|
|
ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
|
|
|
|
return (total_len);
|
|
}
|
|
|
|
#endif /* ED_HPP */
|