162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for ColdFire CPU based boards using a NS8390 Ethernet device. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from the many other 8390 drivers. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) Copyright 2012, Greg Ungerer <gerg@uclinux.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <linux/jiffies.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <asm/mcf8390.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const char version[] = 2262306a36Sopenharmony_ci "mcf8390.c: (15-06-2012) Greg Ungerer <gerg@uclinux.org>"; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define NE_CMD 0x00 2562306a36Sopenharmony_ci#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset */ 2662306a36Sopenharmony_ci#define NE_RESET 0x1f /* Issue a read to reset ,a write to clear */ 2762306a36Sopenharmony_ci#define NE_EN0_ISR 0x07 2862306a36Sopenharmony_ci#define NE_EN0_DCFG 0x0e 2962306a36Sopenharmony_ci#define NE_EN0_RSARLO 0x08 3062306a36Sopenharmony_ci#define NE_EN0_RSARHI 0x09 3162306a36Sopenharmony_ci#define NE_EN0_RCNTLO 0x0a 3262306a36Sopenharmony_ci#define NE_EN0_RXCR 0x0c 3362306a36Sopenharmony_ci#define NE_EN0_TXCR 0x0d 3462306a36Sopenharmony_ci#define NE_EN0_RCNTHI 0x0b 3562306a36Sopenharmony_ci#define NE_EN0_IMR 0x0f 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define NESM_START_PG 0x40 /* First page of TX buffer */ 3862306a36Sopenharmony_ci#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef NE2000_ODDOFFSET 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * A lot of the ColdFire boards use a separate address region for odd offset 4362306a36Sopenharmony_ci * register addresses. The following functions convert and map as required. 4462306a36Sopenharmony_ci * Note that the data port accesses are treated a little differently, and 4562306a36Sopenharmony_ci * always accessed via the insX/outsX functions. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic inline u32 NE_PTR(u32 addr) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci if (addr & 1) 5062306a36Sopenharmony_ci return addr - 1 + NE2000_ODDOFFSET; 5162306a36Sopenharmony_ci return addr; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline u32 NE_DATA_PTR(u32 addr) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci return addr; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_civoid ei_outb(u32 val, u32 addr) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci NE2000_BYTE *rp; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci rp = (NE2000_BYTE *) NE_PTR(addr); 6462306a36Sopenharmony_ci *rp = RSWAP(val); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define ei_inb ei_inb 6862306a36Sopenharmony_ciu8 ei_inb(u32 addr) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci NE2000_BYTE *rp, val; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci rp = (NE2000_BYTE *) NE_PTR(addr); 7362306a36Sopenharmony_ci val = *rp; 7462306a36Sopenharmony_ci return (u8) (RSWAP(val) & 0xff); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_civoid ei_insb(u32 addr, void *vbuf, int len) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci NE2000_BYTE *rp, val; 8062306a36Sopenharmony_ci u8 *buf; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci buf = (u8 *) vbuf; 8362306a36Sopenharmony_ci rp = (NE2000_BYTE *) NE_DATA_PTR(addr); 8462306a36Sopenharmony_ci for (; (len > 0); len--) { 8562306a36Sopenharmony_ci val = *rp; 8662306a36Sopenharmony_ci *buf++ = RSWAP(val); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_civoid ei_insw(u32 addr, void *vbuf, int len) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci volatile u16 *rp; 9362306a36Sopenharmony_ci u16 w, *buf; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci buf = (u16 *) vbuf; 9662306a36Sopenharmony_ci rp = (volatile u16 *) NE_DATA_PTR(addr); 9762306a36Sopenharmony_ci for (; (len > 0); len--) { 9862306a36Sopenharmony_ci w = *rp; 9962306a36Sopenharmony_ci *buf++ = BSWAP(w); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid ei_outsb(u32 addr, const void *vbuf, int len) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci NE2000_BYTE *rp, val; 10662306a36Sopenharmony_ci u8 *buf; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci buf = (u8 *) vbuf; 10962306a36Sopenharmony_ci rp = (NE2000_BYTE *) NE_DATA_PTR(addr); 11062306a36Sopenharmony_ci for (; (len > 0); len--) { 11162306a36Sopenharmony_ci val = *buf++; 11262306a36Sopenharmony_ci *rp = RSWAP(val); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid ei_outsw(u32 addr, const void *vbuf, int len) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci volatile u16 *rp; 11962306a36Sopenharmony_ci u16 w, *buf; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci buf = (u16 *) vbuf; 12262306a36Sopenharmony_ci rp = (volatile u16 *) NE_DATA_PTR(addr); 12362306a36Sopenharmony_ci for (; (len > 0); len--) { 12462306a36Sopenharmony_ci w = *buf++; 12562306a36Sopenharmony_ci *rp = BSWAP(w); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#else /* !NE2000_ODDOFFSET */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define ei_inb inb 13262306a36Sopenharmony_ci#define ei_outb outb 13362306a36Sopenharmony_ci#define ei_insb insb 13462306a36Sopenharmony_ci#define ei_insw insw 13562306a36Sopenharmony_ci#define ei_outsb outsb 13662306a36Sopenharmony_ci#define ei_outsw outsw 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#endif /* !NE2000_ODDOFFSET */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define ei_inb_p ei_inb 14162306a36Sopenharmony_ci#define ei_outb_p ei_outb 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#include "lib8390.c" 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * Hard reset the card. This used to pause for the same period that a 14762306a36Sopenharmony_ci * 8390 reset command required, but that shouldn't be necessary. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic void mcf8390_reset_8390(struct net_device *dev) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned long reset_start_time = jiffies; 15262306a36Sopenharmony_ci u32 addr = dev->base_addr; 15362306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ei_status.txing = 0; 16062306a36Sopenharmony_ci ei_status.dmaing = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* This check _should_not_ be necessary, omit eventually. */ 16362306a36Sopenharmony_ci while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RESET) == 0) { 16462306a36Sopenharmony_ci if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) { 16562306a36Sopenharmony_ci netdev_warn(dev, "%s: did not complete\n", __func__); 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ei_outb(ENISR_RESET, addr + NE_EN0_ISR); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * This *shouldn't* happen. 17562306a36Sopenharmony_ci * If it does, it's the last thing you'll see 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_cistatic void mcf8390_dmaing_err(const char *func, struct net_device *dev, 17862306a36Sopenharmony_ci struct ei_device *ei_local) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n", 18162306a36Sopenharmony_ci func, ei_local->dmaing, ei_local->irqlock); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* 18562306a36Sopenharmony_ci * Grab the 8390 specific header. Similar to the block_input routine, but 18662306a36Sopenharmony_ci * we don't need to be concerned with ring wrap as the header will be at 18762306a36Sopenharmony_ci * the start of a page, so we optimize accordingly. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic void mcf8390_get_8390_hdr(struct net_device *dev, 19062306a36Sopenharmony_ci struct e8390_pkt_hdr *hdr, int ring_page) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 19362306a36Sopenharmony_ci u32 addr = dev->base_addr; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (ei_local->dmaing) { 19662306a36Sopenharmony_ci mcf8390_dmaing_err(__func__, dev, ei_local); 19762306a36Sopenharmony_ci return; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ei_local->dmaing |= 0x01; 20162306a36Sopenharmony_ci ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD); 20262306a36Sopenharmony_ci ei_outb(ENISR_RDC, addr + NE_EN0_ISR); 20362306a36Sopenharmony_ci ei_outb(sizeof(struct e8390_pkt_hdr), addr + NE_EN0_RCNTLO); 20462306a36Sopenharmony_ci ei_outb(0, addr + NE_EN0_RCNTHI); 20562306a36Sopenharmony_ci ei_outb(0, addr + NE_EN0_RSARLO); /* On page boundary */ 20662306a36Sopenharmony_ci ei_outb(ring_page, addr + NE_EN0_RSARHI); 20762306a36Sopenharmony_ci ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ei_insw(addr + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr) >> 1); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */ 21262306a36Sopenharmony_ci ei_local->dmaing &= ~0x01; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci hdr->count = cpu_to_le16(hdr->count); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* 21862306a36Sopenharmony_ci * Block input and output, similar to the Crynwr packet driver. 21962306a36Sopenharmony_ci * If you are porting to a new ethercard, look at the packet driver source 22062306a36Sopenharmony_ci * for hints. The NEx000 doesn't share the on-board packet memory -- 22162306a36Sopenharmony_ci * you have to put the packet out through the "remote DMA" dataport 22262306a36Sopenharmony_ci * using z_writeb. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic void mcf8390_block_input(struct net_device *dev, int count, 22562306a36Sopenharmony_ci struct sk_buff *skb, int ring_offset) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 22862306a36Sopenharmony_ci u32 addr = dev->base_addr; 22962306a36Sopenharmony_ci char *buf = skb->data; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (ei_local->dmaing) { 23262306a36Sopenharmony_ci mcf8390_dmaing_err(__func__, dev, ei_local); 23362306a36Sopenharmony_ci return; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ei_local->dmaing |= 0x01; 23762306a36Sopenharmony_ci ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD); 23862306a36Sopenharmony_ci ei_outb(ENISR_RDC, addr + NE_EN0_ISR); 23962306a36Sopenharmony_ci ei_outb(count & 0xff, addr + NE_EN0_RCNTLO); 24062306a36Sopenharmony_ci ei_outb(count >> 8, addr + NE_EN0_RCNTHI); 24162306a36Sopenharmony_ci ei_outb(ring_offset & 0xff, addr + NE_EN0_RSARLO); 24262306a36Sopenharmony_ci ei_outb(ring_offset >> 8, addr + NE_EN0_RSARHI); 24362306a36Sopenharmony_ci ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ei_insw(addr + NE_DATAPORT, buf, count >> 1); 24662306a36Sopenharmony_ci if (count & 1) 24762306a36Sopenharmony_ci buf[count - 1] = ei_inb(addr + NE_DATAPORT); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */ 25062306a36Sopenharmony_ci ei_local->dmaing &= ~0x01; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void mcf8390_block_output(struct net_device *dev, int count, 25462306a36Sopenharmony_ci const unsigned char *buf, 25562306a36Sopenharmony_ci const int start_page) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 25862306a36Sopenharmony_ci u32 addr = dev->base_addr; 25962306a36Sopenharmony_ci unsigned long dma_start; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Make sure we transfer all bytes if 16bit IO writes */ 26262306a36Sopenharmony_ci if (count & 0x1) 26362306a36Sopenharmony_ci count++; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (ei_local->dmaing) { 26662306a36Sopenharmony_ci mcf8390_dmaing_err(__func__, dev, ei_local); 26762306a36Sopenharmony_ci return; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ei_local->dmaing |= 0x01; 27162306a36Sopenharmony_ci /* We should already be in page 0, but to be safe... */ 27262306a36Sopenharmony_ci ei_outb(E8390_PAGE0 + E8390_START + E8390_NODMA, addr + NE_CMD); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ei_outb(ENISR_RDC, addr + NE_EN0_ISR); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Now the normal output. */ 27762306a36Sopenharmony_ci ei_outb(count & 0xff, addr + NE_EN0_RCNTLO); 27862306a36Sopenharmony_ci ei_outb(count >> 8, addr + NE_EN0_RCNTHI); 27962306a36Sopenharmony_ci ei_outb(0x00, addr + NE_EN0_RSARLO); 28062306a36Sopenharmony_ci ei_outb(start_page, addr + NE_EN0_RSARHI); 28162306a36Sopenharmony_ci ei_outb(E8390_RWRITE + E8390_START, addr + NE_CMD); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ei_outsw(addr + NE_DATAPORT, buf, count >> 1); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci dma_start = jiffies; 28662306a36Sopenharmony_ci while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RDC) == 0) { 28762306a36Sopenharmony_ci if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */ 28862306a36Sopenharmony_ci netdev_warn(dev, "timeout waiting for Tx RDC\n"); 28962306a36Sopenharmony_ci mcf8390_reset_8390(dev); 29062306a36Sopenharmony_ci __NS8390_init(dev, 1); 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */ 29662306a36Sopenharmony_ci ei_local->dmaing &= ~0x01; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic const struct net_device_ops mcf8390_netdev_ops = { 30062306a36Sopenharmony_ci .ndo_open = __ei_open, 30162306a36Sopenharmony_ci .ndo_stop = __ei_close, 30262306a36Sopenharmony_ci .ndo_start_xmit = __ei_start_xmit, 30362306a36Sopenharmony_ci .ndo_tx_timeout = __ei_tx_timeout, 30462306a36Sopenharmony_ci .ndo_get_stats = __ei_get_stats, 30562306a36Sopenharmony_ci .ndo_set_rx_mode = __ei_set_multicast_list, 30662306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 30762306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 30862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 30962306a36Sopenharmony_ci .ndo_poll_controller = __ei_poll, 31062306a36Sopenharmony_ci#endif 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int mcf8390_init(struct net_device *dev) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci static u32 offsets[] = { 31662306a36Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 31762306a36Sopenharmony_ci 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 31862306a36Sopenharmony_ci }; 31962306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 32062306a36Sopenharmony_ci unsigned char SA_prom[32]; 32162306a36Sopenharmony_ci u32 addr = dev->base_addr; 32262306a36Sopenharmony_ci int start_page, stop_page; 32362306a36Sopenharmony_ci int i, ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci mcf8390_reset_8390(dev); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * Read the 16 bytes of station address PROM. 32962306a36Sopenharmony_ci * We must first initialize registers, 33062306a36Sopenharmony_ci * similar to NS8390_init(eifdev, 0). 33162306a36Sopenharmony_ci * We can't reliably read the SAPROM address without this. 33262306a36Sopenharmony_ci * (I learned the hard way!). 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci { 33562306a36Sopenharmony_ci static const struct { 33662306a36Sopenharmony_ci u32 value; 33762306a36Sopenharmony_ci u32 offset; 33862306a36Sopenharmony_ci } program_seq[] = { 33962306a36Sopenharmony_ci {E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD}, 34062306a36Sopenharmony_ci /* Select page 0 */ 34162306a36Sopenharmony_ci {0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */ 34262306a36Sopenharmony_ci {0x00, NE_EN0_RCNTLO}, /* Clear the count regs */ 34362306a36Sopenharmony_ci {0x00, NE_EN0_RCNTHI}, 34462306a36Sopenharmony_ci {0x00, NE_EN0_IMR}, /* Mask completion irq */ 34562306a36Sopenharmony_ci {0xFF, NE_EN0_ISR}, 34662306a36Sopenharmony_ci {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */ 34762306a36Sopenharmony_ci {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */ 34862306a36Sopenharmony_ci {32, NE_EN0_RCNTLO}, 34962306a36Sopenharmony_ci {0x00, NE_EN0_RCNTHI}, 35062306a36Sopenharmony_ci {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */ 35162306a36Sopenharmony_ci {0x00, NE_EN0_RSARHI}, 35262306a36Sopenharmony_ci {E8390_RREAD + E8390_START, NE_CMD}, 35362306a36Sopenharmony_ci }; 35462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(program_seq); i++) { 35562306a36Sopenharmony_ci ei_outb(program_seq[i].value, 35662306a36Sopenharmony_ci addr + program_seq[i].offset); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 36162306a36Sopenharmony_ci SA_prom[i] = ei_inb(addr + NE_DATAPORT); 36262306a36Sopenharmony_ci ei_inb(addr + NE_DATAPORT); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* We must set the 8390 for word mode. */ 36662306a36Sopenharmony_ci ei_outb(0x49, addr + NE_EN0_DCFG); 36762306a36Sopenharmony_ci start_page = NESM_START_PG; 36862306a36Sopenharmony_ci stop_page = NESM_STOP_PG; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Install the Interrupt handler */ 37162306a36Sopenharmony_ci ret = request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev); 37262306a36Sopenharmony_ci if (ret) 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci eth_hw_addr_set(dev, SA_prom); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci netdev_dbg(dev, "Found ethernet address: %pM\n", dev->dev_addr); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ei_local->name = "mcf8390"; 38062306a36Sopenharmony_ci ei_local->tx_start_page = start_page; 38162306a36Sopenharmony_ci ei_local->stop_page = stop_page; 38262306a36Sopenharmony_ci ei_local->word16 = 1; 38362306a36Sopenharmony_ci ei_local->rx_start_page = start_page + TX_PAGES; 38462306a36Sopenharmony_ci ei_local->reset_8390 = mcf8390_reset_8390; 38562306a36Sopenharmony_ci ei_local->block_input = mcf8390_block_input; 38662306a36Sopenharmony_ci ei_local->block_output = mcf8390_block_output; 38762306a36Sopenharmony_ci ei_local->get_8390_hdr = mcf8390_get_8390_hdr; 38862306a36Sopenharmony_ci ei_local->reg_offset = offsets; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci dev->netdev_ops = &mcf8390_netdev_ops; 39162306a36Sopenharmony_ci __NS8390_init(dev, 0); 39262306a36Sopenharmony_ci ret = register_netdev(dev); 39362306a36Sopenharmony_ci if (ret) { 39462306a36Sopenharmony_ci free_irq(dev->irq, dev); 39562306a36Sopenharmony_ci return ret; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci netdev_info(dev, "addr=0x%08x irq=%d, Ethernet Address %pM\n", 39962306a36Sopenharmony_ci addr, dev->irq, dev->dev_addr); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int mcf8390_probe(struct platform_device *pdev) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct net_device *dev; 40662306a36Sopenharmony_ci struct resource *mem; 40762306a36Sopenharmony_ci resource_size_t msize; 40862306a36Sopenharmony_ci int ret, irq; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 41162306a36Sopenharmony_ci if (irq < 0) 41262306a36Sopenharmony_ci return -ENXIO; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 41562306a36Sopenharmony_ci if (mem == NULL) { 41662306a36Sopenharmony_ci dev_err(&pdev->dev, "no memory address specified?\n"); 41762306a36Sopenharmony_ci return -ENXIO; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci msize = resource_size(mem); 42062306a36Sopenharmony_ci if (!request_mem_region(mem->start, msize, pdev->name)) 42162306a36Sopenharmony_ci return -EBUSY; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci dev = ____alloc_ei_netdev(0); 42462306a36Sopenharmony_ci if (dev == NULL) { 42562306a36Sopenharmony_ci release_mem_region(mem->start, msize); 42662306a36Sopenharmony_ci return -ENOMEM; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 43062306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci dev->irq = irq; 43362306a36Sopenharmony_ci dev->base_addr = mem->start; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ret = mcf8390_init(dev); 43662306a36Sopenharmony_ci if (ret) { 43762306a36Sopenharmony_ci release_mem_region(mem->start, msize); 43862306a36Sopenharmony_ci free_netdev(dev); 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int mcf8390_remove(struct platform_device *pdev) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 44762306a36Sopenharmony_ci struct resource *mem; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci unregister_netdev(dev); 45062306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 45162306a36Sopenharmony_ci release_mem_region(mem->start, resource_size(mem)); 45262306a36Sopenharmony_ci free_netdev(dev); 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic struct platform_driver mcf8390_drv = { 45762306a36Sopenharmony_ci .driver = { 45862306a36Sopenharmony_ci .name = "mcf8390", 45962306a36Sopenharmony_ci }, 46062306a36Sopenharmony_ci .probe = mcf8390_probe, 46162306a36Sopenharmony_ci .remove = mcf8390_remove, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cimodule_platform_driver(mcf8390_drv); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciMODULE_DESCRIPTION("MCF8390 ColdFire NS8390 driver"); 46762306a36Sopenharmony_ciMODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>"); 46862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 46962306a36Sopenharmony_ciMODULE_ALIAS("platform:mcf8390"); 470