OpenCloudOS-Kernel/drivers/net/myri_sbus.c

1188 lines
30 KiB
C
Raw Normal View History

/* myri_sbus.c: MyriCOM MyriNET SBUS card driver.
*
* Copyright (C) 1996, 1999, 2006, 2008 David S. Miller (davem@davemloft.net)
*/
static char version[] =
"myri_sbus.c:v2.0 June 23, 2006 David S. Miller (davem@davemloft.net)\n";
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/firmware.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
#include <linux/gfp.h>
#include <net/dst.h>
#include <net/arp.h>
#include <net/sock.h>
#include <net/ipv6.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#include <asm/idprom.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/auxio.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include "myri_sbus.h"
/* #define DEBUG_DETECT */
/* #define DEBUG_IRQ */
/* #define DEBUG_TRANSMIT */
/* #define DEBUG_RECEIVE */
/* #define DEBUG_HEADER */
#ifdef DEBUG_DETECT
#define DET(x) printk x
#else
#define DET(x)
#endif
#ifdef DEBUG_IRQ
#define DIRQ(x) printk x
#else
#define DIRQ(x)
#endif
#ifdef DEBUG_TRANSMIT
#define DTX(x) printk x
#else
#define DTX(x)
#endif
#ifdef DEBUG_RECEIVE
#define DRX(x) printk x
#else
#define DRX(x)
#endif
#ifdef DEBUG_HEADER
#define DHDR(x) printk x
#else
#define DHDR(x)
#endif
/* Firmware name */
#define FWNAME "myricom/lanai.bin"
static void myri_reset_off(void __iomem *lp, void __iomem *cregs)
{
/* Clear IRQ mask. */
sbus_writel(0, lp + LANAI_EIMASK);
/* Turn RESET function off. */
sbus_writel(CONTROL_ROFF, cregs + MYRICTRL_CTRL);
}
static void myri_reset_on(void __iomem *cregs)
{
/* Enable RESET function. */
sbus_writel(CONTROL_RON, cregs + MYRICTRL_CTRL);
/* Disable IRQ's. */
sbus_writel(CONTROL_DIRQ, cregs + MYRICTRL_CTRL);
}
static void myri_disable_irq(void __iomem *lp, void __iomem *cregs)
{
sbus_writel(CONTROL_DIRQ, cregs + MYRICTRL_CTRL);
sbus_writel(0, lp + LANAI_EIMASK);
sbus_writel(ISTAT_HOST, lp + LANAI_ISTAT);
}
static void myri_enable_irq(void __iomem *lp, void __iomem *cregs)
{
sbus_writel(CONTROL_EIRQ, cregs + MYRICTRL_CTRL);
sbus_writel(ISTAT_HOST, lp + LANAI_EIMASK);
}
static inline void bang_the_chip(struct myri_eth *mp)
{
struct myri_shmem __iomem *shmem = mp->shmem;
void __iomem *cregs = mp->cregs;
sbus_writel(1, &shmem->send);
sbus_writel(CONTROL_WON, cregs + MYRICTRL_CTRL);
}
static int myri_do_handshake(struct myri_eth *mp)
{
struct myri_shmem __iomem *shmem = mp->shmem;
void __iomem *cregs = mp->cregs;
struct myri_channel __iomem *chan = &shmem->channel;
int tick = 0;
DET(("myri_do_handshake: "));
if (sbus_readl(&chan->state) == STATE_READY) {
DET(("Already STATE_READY, failed.\n"));
return -1; /* We're hosed... */
}
myri_disable_irq(mp->lregs, cregs);
while (tick++ < 25) {
u32 softstate;
/* Wake it up. */
DET(("shakedown, CONTROL_WON, "));
sbus_writel(1, &shmem->shakedown);
sbus_writel(CONTROL_WON, cregs + MYRICTRL_CTRL);
softstate = sbus_readl(&chan->state);
DET(("chanstate[%08x] ", softstate));
if (softstate == STATE_READY) {
DET(("wakeup successful, "));
break;
}
if (softstate != STATE_WFN) {
DET(("not WFN setting that, "));
sbus_writel(STATE_WFN, &chan->state);
}
udelay(20);
}
myri_enable_irq(mp->lregs, cregs);
if (tick > 25) {
DET(("25 ticks we lose, failure.\n"));
return -1;
}
DET(("success\n"));
return 0;
}
static int __devinit myri_load_lanai(struct myri_eth *mp)
{
const struct firmware *fw;
struct net_device *dev = mp->dev;
struct myri_shmem __iomem *shmem = mp->shmem;
void __iomem *rptr;
int i, lanai4_data_size;
myri_disable_irq(mp->lregs, mp->cregs);
myri_reset_on(mp->cregs);
rptr = mp->lanai;
for (i = 0; i < mp->eeprom.ramsz; i++)
sbus_writeb(0, rptr + i);
if (mp->eeprom.cpuvers >= CPUVERS_3_0)
sbus_writel(mp->eeprom.cval, mp->lregs + LANAI_CVAL);
i = request_firmware(&fw, FWNAME, &mp->myri_op->dev);
if (i) {
printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
FWNAME, i);
return i;
}
if (fw->size < 2) {
printk(KERN_ERR "Bogus length %zu in image \"%s\"\n",
fw->size, FWNAME);
release_firmware(fw);
return -EINVAL;
}
lanai4_data_size = fw->data[0] << 8 | fw->data[1];
/* Load executable code. */
for (i = 2; i < fw->size; i++)
sbus_writeb(fw->data[i], rptr++);
/* Load data segment. */
for (i = 0; i < lanai4_data_size; i++)
sbus_writeb(0, rptr++);
/* Set device address. */
sbus_writeb(0, &shmem->addr[0]);
sbus_writeb(0, &shmem->addr[1]);
for (i = 0; i < 6; i++)
sbus_writeb(dev->dev_addr[i],
&shmem->addr[i + 2]);
/* Set SBUS bursts and interrupt mask. */
sbus_writel(((mp->myri_bursts & 0xf8) >> 3), &shmem->burst);
sbus_writel(SHMEM_IMASK_RX, &shmem->imask);
/* Release the LANAI. */
myri_disable_irq(mp->lregs, mp->cregs);
myri_reset_off(mp->lregs, mp->cregs);
myri_disable_irq(mp->lregs, mp->cregs);
/* Wait for the reset to complete. */
for (i = 0; i < 5000; i++) {
if (sbus_readl(&shmem->channel.state) != STATE_READY)
break;
else
udelay(10);
}
if (i == 5000)
printk(KERN_ERR "myricom: Chip would not reset after firmware load.\n");
i = myri_do_handshake(mp);
if (i)
printk(KERN_ERR "myricom: Handshake with LANAI failed.\n");
if (mp->eeprom.cpuvers == CPUVERS_4_0)
sbus_writel(0, mp->lregs + LANAI_VERS);
release_firmware(fw);
return i;
}
static void myri_clean_rings(struct myri_eth *mp)
{
struct sendq __iomem *sq = mp->sq;
struct recvq __iomem *rq = mp->rq;
int i;
sbus_writel(0, &rq->tail);
sbus_writel(0, &rq->head);
for (i = 0; i < (RX_RING_SIZE+1); i++) {
if (mp->rx_skbs[i] != NULL) {
struct myri_rxd __iomem *rxd = &rq->myri_rxd[i];
u32 dma_addr;
dma_addr = sbus_readl(&rxd->myri_scatters[0].addr);
dma_unmap_single(&mp->myri_op->dev, dma_addr,
RX_ALLOC_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb(mp->rx_skbs[i]);
mp->rx_skbs[i] = NULL;
}
}
mp->tx_old = 0;
sbus_writel(0, &sq->tail);
sbus_writel(0, &sq->head);
for (i = 0; i < TX_RING_SIZE; i++) {
if (mp->tx_skbs[i] != NULL) {
struct sk_buff *skb = mp->tx_skbs[i];
struct myri_txd __iomem *txd = &sq->myri_txd[i];
u32 dma_addr;
dma_addr = sbus_readl(&txd->myri_gathers[0].addr);
dma_unmap_single(&mp->myri_op->dev, dma_addr,
(skb->len + 3) & ~3,
DMA_TO_DEVICE);
dev_kfree_skb(mp->tx_skbs[i]);
mp->tx_skbs[i] = NULL;
}
}
}
static void myri_init_rings(struct myri_eth *mp, int from_irq)
{
struct recvq __iomem *rq = mp->rq;
struct myri_rxd __iomem *rxd = &rq->myri_rxd[0];
struct net_device *dev = mp->dev;
gfp_t gfp_flags = GFP_KERNEL;
int i;
if (from_irq || in_interrupt())
gfp_flags = GFP_ATOMIC;
myri_clean_rings(mp);
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = myri_alloc_skb(RX_ALLOC_SIZE, gfp_flags);
u32 dma_addr;
if (!skb)
continue;
mp->rx_skbs[i] = skb;
skb->dev = dev;
skb_put(skb, RX_ALLOC_SIZE);
dma_addr = dma_map_single(&mp->myri_op->dev,
skb->data, RX_ALLOC_SIZE,
DMA_FROM_DEVICE);
sbus_writel(dma_addr, &rxd[i].myri_scatters[0].addr);
sbus_writel(RX_ALLOC_SIZE, &rxd[i].myri_scatters[0].len);
sbus_writel(i, &rxd[i].ctx);
sbus_writel(1, &rxd[i].num_sg);
}
sbus_writel(0, &rq->head);
sbus_writel(RX_RING_SIZE, &rq->tail);
}
static int myri_init(struct myri_eth *mp, int from_irq)
{
myri_init_rings(mp, from_irq);
return 0;
}
static void myri_is_not_so_happy(struct myri_eth *mp)
{
}
#ifdef DEBUG_HEADER
static void dump_ehdr(struct ethhdr *ehdr)
{
printk("ehdr[h_dst(%pM)"
"h_source(%pM)"
"h_proto(%04x)]\n",
ehdr->h_dest, ehdr->h_source, ehdr->h_proto);
}
static void dump_ehdr_and_myripad(unsigned char *stuff)
{
struct ethhdr *ehdr = (struct ethhdr *) (stuff + 2);
printk("pad[%02x:%02x]", stuff[0], stuff[1]);
dump_ehdr(ehdr);
}
#endif
static void myri_tx(struct myri_eth *mp, struct net_device *dev)
{
struct sendq __iomem *sq= mp->sq;
int entry = mp->tx_old;
int limit = sbus_readl(&sq->head);
DTX(("entry[%d] limit[%d] ", entry, limit));
if (entry == limit)
return;
while (entry != limit) {
struct sk_buff *skb = mp->tx_skbs[entry];
u32 dma_addr;
DTX(("SKB[%d] ", entry));
dma_addr = sbus_readl(&sq->myri_txd[entry].myri_gathers[0].addr);
dma_unmap_single(&mp->myri_op->dev, dma_addr,
skb->len, DMA_TO_DEVICE);
dev_kfree_skb(skb);
mp->tx_skbs[entry] = NULL;
dev->stats.tx_packets++;
entry = NEXT_TX(entry);
}
mp->tx_old = entry;
}
/* Determine the packet's protocol ID. The rule here is that we
* assume 802.3 if the type field is short enough to be a length.
* This is normal practice and works for any 'now in use' protocol.
*/
static __be16 myri_type_trans(struct sk_buff *skb, struct net_device *dev)
{
struct ethhdr *eth;
unsigned char *rawp;
skb_set_mac_header(skb, MYRI_PAD_LEN);
skb_pull(skb, dev->hard_header_len);
eth = eth_hdr(skb);
#ifdef DEBUG_HEADER
DHDR(("myri_type_trans: "));
dump_ehdr(eth);
#endif
if (*eth->h_dest & 1) {
if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN)==0)
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
} else if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
skb->pkt_type = PACKET_OTHERHOST;
}
if (ntohs(eth->h_proto) >= 1536)
return eth->h_proto;
rawp = skb->data;
/* This is a magic hack to spot IPX packets. Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
* won't work for fault tolerant netware but does for the rest.
*/
if (*(unsigned short *)rawp == 0xFFFF)
return htons(ETH_P_802_3);
/* Real 802.2 LLC */
return htons(ETH_P_802_2);
}
static void myri_rx(struct myri_eth *mp, struct net_device *dev)
{
struct recvq __iomem *rq = mp->rq;
struct recvq __iomem *rqa = mp->rqack;
int entry = sbus_readl(&rqa->head);
int limit = sbus_readl(&rqa->tail);
int drops;
DRX(("entry[%d] limit[%d] ", entry, limit));
if (entry == limit)
return;
drops = 0;
DRX(("\n"));
while (entry != limit) {
struct myri_rxd __iomem *rxdack = &rqa->myri_rxd[entry];
u32 csum = sbus_readl(&rxdack->csum);
int len = sbus_readl(&rxdack->myri_scatters[0].len);
int index = sbus_readl(&rxdack->ctx);
struct myri_rxd __iomem *rxd = &rq->myri_rxd[sbus_readl(&rq->tail)];
struct sk_buff *skb = mp->rx_skbs[index];
/* Ack it. */
sbus_writel(NEXT_RX(entry), &rqa->head);
/* Check for errors. */
DRX(("rxd[%d]: %p len[%d] csum[%08x] ", entry, rxd, len, csum));
dma_sync_single_for_cpu(&mp->myri_op->dev,
sbus_readl(&rxd->myri_scatters[0].addr),
RX_ALLOC_SIZE, DMA_FROM_DEVICE);
if (len < (ETH_HLEN + MYRI_PAD_LEN) || (skb->data[0] != MYRI_PAD_LEN)) {
DRX(("ERROR["));
dev->stats.rx_errors++;
if (len < (ETH_HLEN + MYRI_PAD_LEN)) {
DRX(("BAD_LENGTH] "));
dev->stats.rx_length_errors++;
} else {
DRX(("NO_PADDING] "));
dev->stats.rx_frame_errors++;
}
/* Return it to the LANAI. */
drop_it:
drops++;
DRX(("DROP "));
dev->stats.rx_dropped++;
dma_sync_single_for_device(&mp->myri_op->dev,
sbus_readl(&rxd->myri_scatters[0].addr),
RX_ALLOC_SIZE,
DMA_FROM_DEVICE);
sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
sbus_writel(index, &rxd->ctx);
sbus_writel(1, &rxd->num_sg);
sbus_writel(NEXT_RX(sbus_readl(&rq->tail)), &rq->tail);
goto next;
}
DRX(("len[%d] ", len));
if (len > RX_COPY_THRESHOLD) {
struct sk_buff *new_skb;
u32 dma_addr;
DRX(("BIGBUFF "));
new_skb = myri_alloc_skb(RX_ALLOC_SIZE, GFP_ATOMIC);
if (new_skb == NULL) {
DRX(("skb_alloc(FAILED) "));
goto drop_it;
}
dma_unmap_single(&mp->myri_op->dev,
sbus_readl(&rxd->myri_scatters[0].addr),
RX_ALLOC_SIZE,
DMA_FROM_DEVICE);
mp->rx_skbs[index] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, RX_ALLOC_SIZE);
dma_addr = dma_map_single(&mp->myri_op->dev,
new_skb->data,
RX_ALLOC_SIZE,
DMA_FROM_DEVICE);
sbus_writel(dma_addr, &rxd->myri_scatters[0].addr);
sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
sbus_writel(index, &rxd->ctx);
sbus_writel(1, &rxd->num_sg);
sbus_writel(NEXT_RX(sbus_readl(&rq->tail)), &rq->tail);
/* Trim the original skb for the netif. */
DRX(("trim(%d) ", len));
skb_trim(skb, len);
} else {
struct sk_buff *copy_skb = dev_alloc_skb(len);
DRX(("SMALLBUFF "));
if (copy_skb == NULL) {
DRX(("dev_alloc_skb(FAILED) "));
goto drop_it;
}
/* DMA sync already done above. */
copy_skb->dev = dev;
DRX(("resv_and_put "));
skb_put(copy_skb, len);
skb_copy_from_linear_data(skb, copy_skb->data, len);
/* Reuse original ring buffer. */
DRX(("reuse "));
dma_sync_single_for_device(&mp->myri_op->dev,
sbus_readl(&rxd->myri_scatters[0].addr),
RX_ALLOC_SIZE,
DMA_FROM_DEVICE);
sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
sbus_writel(index, &rxd->ctx);
sbus_writel(1, &rxd->num_sg);
sbus_writel(NEXT_RX(sbus_readl(&rq->tail)), &rq->tail);
skb = copy_skb;
}
/* Just like the happy meal we get checksums from this card. */
skb->csum = csum;
skb->ip_summed = CHECKSUM_UNNECESSARY; /* XXX */
skb->protocol = myri_type_trans(skb, dev);
DRX(("prot[%04x] netif_rx ", skb->protocol));
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
next:
DRX(("NEXT\n"));
entry = NEXT_RX(entry);
}
}
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
static irqreturn_t myri_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
struct myri_eth *mp = netdev_priv(dev);
void __iomem *lregs = mp->lregs;
struct myri_channel __iomem *chan = &mp->shmem->channel;
unsigned long flags;
u32 status;
int handled = 0;
spin_lock_irqsave(&mp->irq_lock, flags);
status = sbus_readl(lregs + LANAI_ISTAT);
DIRQ(("myri_interrupt: status[%08x] ", status));
if (status & ISTAT_HOST) {
u32 softstate;
handled = 1;
DIRQ(("IRQ_DISAB "));
myri_disable_irq(lregs, mp->cregs);
softstate = sbus_readl(&chan->state);
DIRQ(("state[%08x] ", softstate));
if (softstate != STATE_READY) {
DIRQ(("myri_not_so_happy "));
myri_is_not_so_happy(mp);
}
DIRQ(("\nmyri_rx: "));
myri_rx(mp, dev);
DIRQ(("\nistat=ISTAT_HOST "));
sbus_writel(ISTAT_HOST, lregs + LANAI_ISTAT);
DIRQ(("IRQ_ENAB "));
myri_enable_irq(lregs, mp->cregs);
}
DIRQ(("\n"));
spin_unlock_irqrestore(&mp->irq_lock, flags);
return IRQ_RETVAL(handled);
}
static int myri_open(struct net_device *dev)
{
struct myri_eth *mp = netdev_priv(dev);
return myri_init(mp, in_interrupt());
}
static int myri_close(struct net_device *dev)
{
struct myri_eth *mp = netdev_priv(dev);
myri_clean_rings(mp);
return 0;
}
static void myri_tx_timeout(struct net_device *dev)
{
struct myri_eth *mp = netdev_priv(dev);
printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
dev->stats.tx_errors++;
myri_init(mp, 0);
netif_wake_queue(dev);
}
static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct myri_eth *mp = netdev_priv(dev);
struct sendq __iomem *sq = mp->sq;
struct myri_txd __iomem *txd;
unsigned long flags;
unsigned int head, tail;
int len, entry;
u32 dma_addr;
DTX(("myri_start_xmit: "));
myri_tx(mp, dev);
netif_stop_queue(dev);
/* This is just to prevent multiple PIO reads for TX_BUFFS_AVAIL. */
head = sbus_readl(&sq->head);
tail = sbus_readl(&sq->tail);
if (!TX_BUFFS_AVAIL(head, tail)) {
DTX(("no buffs available, returning 1\n"));
return NETDEV_TX_BUSY;
}
spin_lock_irqsave(&mp->irq_lock, flags);
DHDR(("xmit[skbdata(%p)]\n", skb->data));
#ifdef DEBUG_HEADER
dump_ehdr_and_myripad(((unsigned char *) skb->data));
#endif
/* XXX Maybe this can go as well. */
len = skb->len;
if (len & 3) {
DTX(("len&3 "));
len = (len + 4) & (~3);
}
entry = sbus_readl(&sq->tail);
txd = &sq->myri_txd[entry];
mp->tx_skbs[entry] = skb;
/* Must do this before we sbus map it. */
if (skb->data[MYRI_PAD_LEN] & 0x1) {
sbus_writew(0xffff, &txd->addr[0]);
sbus_writew(0xffff, &txd->addr[1]);
sbus_writew(0xffff, &txd->addr[2]);
sbus_writew(0xffff, &txd->addr[3]);
} else {
sbus_writew(0xffff, &txd->addr[0]);
sbus_writew((skb->data[0] << 8) | skb->data[1], &txd->addr[1]);
sbus_writew((skb->data[2] << 8) | skb->data[3], &txd->addr[2]);
sbus_writew((skb->data[4] << 8) | skb->data[5], &txd->addr[3]);
}
dma_addr = dma_map_single(&mp->myri_op->dev, skb->data,
len, DMA_TO_DEVICE);
sbus_writel(dma_addr, &txd->myri_gathers[0].addr);
sbus_writel(len, &txd->myri_gathers[0].len);
sbus_writel(1, &txd->num_sg);
sbus_writel(KERNEL_CHANNEL, &txd->chan);
sbus_writel(len, &txd->len);
sbus_writel((u32)-1, &txd->csum_off);
sbus_writel(0, &txd->csum_field);
sbus_writel(NEXT_TX(entry), &sq->tail);
DTX(("BangTheChip "));
bang_the_chip(mp);
DTX(("tbusy=0, returning 0\n"));
netif_start_queue(dev);
spin_unlock_irqrestore(&mp->irq_lock, flags);
return NETDEV_TX_OK;
}
/* Create the MyriNet MAC header for an arbitrary protocol layer
*
* saddr=NULL means use device source address
* daddr=NULL means leave destination address (eg unresolved arp)
*/
static int myri_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned len)
{
struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
unsigned char *pad = (unsigned char *) skb_push(skb, MYRI_PAD_LEN);
#ifdef DEBUG_HEADER
DHDR(("myri_header: pad[%02x,%02x] ", pad[0], pad[1]));
dump_ehdr(eth);
#endif
/* Set the MyriNET padding identifier. */
pad[0] = MYRI_PAD_LEN;
pad[1] = 0xab;
/* Set the protocol type. For a packet of type ETH_P_802_3/2 we put the
* length in here instead.
*/
if (type != ETH_P_802_3 && type != ETH_P_802_2)
eth->h_proto = htons(type);
else
eth->h_proto = htons(len);
/* Set the source hardware address. */
if (saddr)
memcpy(eth->h_source, saddr, dev->addr_len);
else
memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
/* Anyway, the loopback-device should never use this function... */
if (dev->flags & IFF_LOOPBACK) {
int i;
for (i = 0; i < dev->addr_len; i++)
eth->h_dest[i] = 0;
return dev->hard_header_len;
}
if (daddr) {
memcpy(eth->h_dest, daddr, dev->addr_len);
return dev->hard_header_len;
}
return -dev->hard_header_len;
}
/* Rebuild the MyriNet MAC header. This is called after an ARP
* (or in future other address resolution) has completed on this
* sk_buff. We now let ARP fill in the other fields.
*/
static int myri_rebuild_header(struct sk_buff *skb)
{
unsigned char *pad = (unsigned char *) skb->data;
struct ethhdr *eth = (struct ethhdr *) (pad + MYRI_PAD_LEN);
struct net_device *dev = skb->dev;
#ifdef DEBUG_HEADER
DHDR(("myri_rebuild_header: pad[%02x,%02x] ", pad[0], pad[1]));
dump_ehdr(eth);
#endif
/* Refill MyriNet padding identifiers, this is just being anal. */
pad[0] = MYRI_PAD_LEN;
pad[1] = 0xab;
switch (eth->h_proto)
{
#ifdef CONFIG_INET
case cpu_to_be16(ETH_P_IP):
return arp_find(eth->h_dest, skb);
#endif
default:
printk(KERN_DEBUG
"%s: unable to resolve type %X addresses.\n",
dev->name, (int)eth->h_proto);
memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
return 0;
break;
}
return 0;
}
static int myri_header_cache(const struct neighbour *neigh, struct hh_cache *hh)
{
unsigned short type = hh->hh_type;
unsigned char *pad;
struct ethhdr *eth;
const struct net_device *dev = neigh->dev;
pad = ((unsigned char *) hh->hh_data) +
HH_DATA_OFF(sizeof(*eth) + MYRI_PAD_LEN);
eth = (struct ethhdr *) (pad + MYRI_PAD_LEN);
if (type == htons(ETH_P_802_3))
return -1;
/* Refill MyriNet padding identifiers, this is just being anal. */
pad[0] = MYRI_PAD_LEN;
pad[1] = 0xab;
eth->h_proto = type;
memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, neigh->ha, dev->addr_len);
hh->hh_len = 16;
return 0;
}
/* Called by Address Resolution module to notify changes in address. */
void myri_header_cache_update(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char * haddr)
{
memcpy(((u8*)hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
haddr, dev->addr_len);
}
static int myri_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < (ETH_HLEN + MYRI_PAD_LEN)) || (new_mtu > MYRINET_MTU))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
static void myri_set_multicast(struct net_device *dev)
{
/* Do nothing, all MyriCOM nodes transmit multicast frames
* as broadcast packets...
*/
}
static inline void set_boardid_from_idprom(struct myri_eth *mp, int num)
{
mp->eeprom.id[0] = 0;
mp->eeprom.id[1] = idprom->id_machtype;
mp->eeprom.id[2] = (idprom->id_sernum >> 16) & 0xff;
mp->eeprom.id[3] = (idprom->id_sernum >> 8) & 0xff;
mp->eeprom.id[4] = (idprom->id_sernum >> 0) & 0xff;
mp->eeprom.id[5] = num;
}
static inline void determine_reg_space_size(struct myri_eth *mp)
{
switch(mp->eeprom.cpuvers) {
case CPUVERS_2_3:
case CPUVERS_3_0:
case CPUVERS_3_1:
case CPUVERS_3_2:
mp->reg_size = (3 * 128 * 1024) + 4096;
break;
case CPUVERS_4_0:
case CPUVERS_4_1:
mp->reg_size = ((4096<<1) + mp->eeprom.ramsz);
break;
case CPUVERS_4_2:
case CPUVERS_5_0:
default:
printk("myricom: AIEEE weird cpu version %04x assuming pre4.0\n",
mp->eeprom.cpuvers);
mp->reg_size = (3 * 128 * 1024) + 4096;
}
}
#ifdef DEBUG_DETECT
static void dump_eeprom(struct myri_eth *mp)
{
printk("EEPROM: clockval[%08x] cpuvers[%04x] "
"id[%02x,%02x,%02x,%02x,%02x,%02x]\n",
mp->eeprom.cval, mp->eeprom.cpuvers,
mp->eeprom.id[0], mp->eeprom.id[1], mp->eeprom.id[2],
mp->eeprom.id[3], mp->eeprom.id[4], mp->eeprom.id[5]);
printk("EEPROM: ramsz[%08x]\n", mp->eeprom.ramsz);
printk("EEPROM: fvers[%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
mp->eeprom.fvers[0], mp->eeprom.fvers[1], mp->eeprom.fvers[2],
mp->eeprom.fvers[3], mp->eeprom.fvers[4], mp->eeprom.fvers[5],
mp->eeprom.fvers[6], mp->eeprom.fvers[7]);
printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
mp->eeprom.fvers[8], mp->eeprom.fvers[9], mp->eeprom.fvers[10],
mp->eeprom.fvers[11], mp->eeprom.fvers[12], mp->eeprom.fvers[13],
mp->eeprom.fvers[14], mp->eeprom.fvers[15]);
printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
mp->eeprom.fvers[16], mp->eeprom.fvers[17], mp->eeprom.fvers[18],
mp->eeprom.fvers[19], mp->eeprom.fvers[20], mp->eeprom.fvers[21],
mp->eeprom.fvers[22], mp->eeprom.fvers[23]);
printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x]\n",
mp->eeprom.fvers[24], mp->eeprom.fvers[25], mp->eeprom.fvers[26],
mp->eeprom.fvers[27], mp->eeprom.fvers[28], mp->eeprom.fvers[29],
mp->eeprom.fvers[30], mp->eeprom.fvers[31]);
printk("EEPROM: mvers[%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
mp->eeprom.mvers[0], mp->eeprom.mvers[1], mp->eeprom.mvers[2],
mp->eeprom.mvers[3], mp->eeprom.mvers[4], mp->eeprom.mvers[5],
mp->eeprom.mvers[6], mp->eeprom.mvers[7]);
printk("EEPROM: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x]\n",
mp->eeprom.mvers[8], mp->eeprom.mvers[9], mp->eeprom.mvers[10],
mp->eeprom.mvers[11], mp->eeprom.mvers[12], mp->eeprom.mvers[13],
mp->eeprom.mvers[14], mp->eeprom.mvers[15]);
printk("EEPROM: dlval[%04x] brd_type[%04x] bus_type[%04x] prod_code[%04x]\n",
mp->eeprom.dlval, mp->eeprom.brd_type, mp->eeprom.bus_type,
mp->eeprom.prod_code);
printk("EEPROM: serial_num[%08x]\n", mp->eeprom.serial_num);
}
#endif
static const struct header_ops myri_header_ops = {
.create = myri_header,
.rebuild = myri_rebuild_header,
.cache = myri_header_cache,
.cache_update = myri_header_cache_update,
};
static const struct net_device_ops myri_ops = {
.ndo_open = myri_open,
.ndo_stop = myri_close,
.ndo_start_xmit = myri_start_xmit,
.ndo_set_multicast_list = myri_set_multicast,
.ndo_tx_timeout = myri_tx_timeout,
.ndo_change_mtu = myri_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static int __devinit myri_sbus_probe(struct platform_device *op)
{
struct device_node *dp = op->dev.of_node;
static unsigned version_printed;
struct net_device *dev;
struct myri_eth *mp;
const void *prop;
static int num;
int i, len;
DET(("myri_ether_init(%p,%d):\n", op, num));
dev = alloc_etherdev(sizeof(struct myri_eth));
if (!dev)
return -ENOMEM;
if (version_printed++ == 0)
printk(version);
SET_NETDEV_DEV(dev, &op->dev);
mp = netdev_priv(dev);
spin_lock_init(&mp->irq_lock);
mp->myri_op = op;
/* Clean out skb arrays. */
for (i = 0; i < (RX_RING_SIZE + 1); i++)
mp->rx_skbs[i] = NULL;
for (i = 0; i < TX_RING_SIZE; i++)
mp->tx_skbs[i] = NULL;
/* First check for EEPROM information. */
prop = of_get_property(dp, "myrinet-eeprom-info", &len);
if (prop)
memcpy(&mp->eeprom, prop, sizeof(struct myri_eeprom));
if (!prop) {
/* No eeprom property, must cook up the values ourselves. */
DET(("No EEPROM: "));
mp->eeprom.bus_type = BUS_TYPE_SBUS;
mp->eeprom.cpuvers =
of_getintprop_default(dp, "cpu_version", 0);
mp->eeprom.cval =
of_getintprop_default(dp, "clock_value", 0);
mp->eeprom.ramsz = of_getintprop_default(dp, "sram_size", 0);
if (!mp->eeprom.cpuvers)
mp->eeprom.cpuvers = CPUVERS_2_3;
if (mp->eeprom.cpuvers < CPUVERS_3_0)
mp->eeprom.cval = 0;
if (!mp->eeprom.ramsz)
mp->eeprom.ramsz = (128 * 1024);
prop = of_get_property(dp, "myrinet-board-id", &len);
if (prop)
memcpy(&mp->eeprom.id[0], prop, 6);
else
set_boardid_from_idprom(mp, num);
prop = of_get_property(dp, "fpga_version", &len);
if (prop)
memcpy(&mp->eeprom.fvers[0], prop, 32);
else
memset(&mp->eeprom.fvers[0], 0, 32);
if (mp->eeprom.cpuvers == CPUVERS_4_1) {
if (mp->eeprom.ramsz == (128 * 1024))
mp->eeprom.ramsz = (256 * 1024);
if ((mp->eeprom.cval == 0x40414041) ||
(mp->eeprom.cval == 0x90449044))
mp->eeprom.cval = 0x50e450e4;
}
}
#ifdef DEBUG_DETECT
dump_eeprom(mp);
#endif
for (i = 0; i < 6; i++)
dev->dev_addr[i] = mp->eeprom.id[i];
determine_reg_space_size(mp);
/* Map in the MyriCOM register/localram set. */
if (mp->eeprom.cpuvers < CPUVERS_4_0) {
/* XXX Makes no sense, if control reg is non-existent this
* XXX driver cannot function at all... maybe pre-4.0 is
* XXX only a valid version for PCI cards? Ask feldy...
*/
DET(("Mapping regs for cpuvers < CPUVERS_4_0\n"));
mp->regs = of_ioremap(&op->resource[0], 0,
mp->reg_size, "MyriCOM Regs");
if (!mp->regs) {
printk("MyriCOM: Cannot map MyriCOM registers.\n");
goto err;
}
mp->lanai = mp->regs + (256 * 1024);
mp->lregs = mp->lanai + (0x10000 * 2);
} else {
DET(("Mapping regs for cpuvers >= CPUVERS_4_0\n"));
mp->cregs = of_ioremap(&op->resource[0], 0,
PAGE_SIZE, "MyriCOM Control Regs");
mp->lregs = of_ioremap(&op->resource[0], (256 * 1024),
PAGE_SIZE, "MyriCOM LANAI Regs");
mp->lanai = of_ioremap(&op->resource[0], (512 * 1024),
mp->eeprom.ramsz, "MyriCOM SRAM");
}
DET(("Registers mapped: cregs[%p] lregs[%p] lanai[%p]\n",
mp->cregs, mp->lregs, mp->lanai));
if (mp->eeprom.cpuvers >= CPUVERS_4_0)
mp->shmem_base = 0xf000;
else
mp->shmem_base = 0x8000;
DET(("Shared memory base is %04x, ", mp->shmem_base));
mp->shmem = (struct myri_shmem __iomem *)
(mp->lanai + (mp->shmem_base * 2));
DET(("shmem mapped at %p\n", mp->shmem));
mp->rqack = &mp->shmem->channel.recvqa;
mp->rq = &mp->shmem->channel.recvq;
mp->sq = &mp->shmem->channel.sendq;
/* Reset the board. */
DET(("Resetting LANAI\n"));
myri_reset_off(mp->lregs, mp->cregs);
myri_reset_on(mp->cregs);
/* Turn IRQ's off. */
myri_disable_irq(mp->lregs, mp->cregs);
/* Reset once more. */
myri_reset_on(mp->cregs);
/* Get the supported DVMA burst sizes from our SBUS. */
mp->myri_bursts = of_getintprop_default(dp->parent,
"burst-sizes", 0x00);
if (!sbus_can_burst64())
mp->myri_bursts &= ~(DMA_BURST64);
DET(("MYRI bursts %02x\n", mp->myri_bursts));
/* Encode SBUS interrupt level in second control register. */
i = of_getintprop_default(dp, "interrupts", 0);
if (i == 0)
i = 4;
DET(("prom_getint(interrupts)==%d, irqlvl set to %04x\n",
i, (1 << i)));
sbus_writel((1 << i), mp->cregs + MYRICTRL_IRQLVL);
mp->dev = dev;
dev->watchdog_timeo = 5*HZ;
dev->irq = op->archdata.irqs[0];
dev->netdev_ops = &myri_ops;
/* Register interrupt handler now. */
DET(("Requesting MYRIcom IRQ line.\n"));
if (request_irq(dev->irq, myri_interrupt,
IRQF_SHARED, "MyriCOM Ethernet", (void *) dev)) {
printk("MyriCOM: Cannot register interrupt handler.\n");
goto err;
}
dev->mtu = MYRINET_MTU;
dev->header_ops = &myri_header_ops;
dev->hard_header_len = (ETH_HLEN + MYRI_PAD_LEN);
/* Load code onto the LANai. */
DET(("Loading LANAI firmware\n"));
if (myri_load_lanai(mp)) {
printk(KERN_ERR "MyriCOM: Cannot Load LANAI firmware.\n");
goto err_free_irq;
}
if (register_netdev(dev)) {
printk("MyriCOM: Cannot register device.\n");
goto err_free_irq;
}
dev_set_drvdata(&op->dev, mp);
num++;
printk("%s: MyriCOM MyriNET Ethernet %pM\n",
dev->name, dev->dev_addr);
return 0;
err_free_irq:
free_irq(dev->irq, dev);
err:
/* This will also free the co-allocated private data*/
free_netdev(dev);
return -ENODEV;
}
static int __devexit myri_sbus_remove(struct platform_device *op)
{
struct myri_eth *mp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = mp->dev;
unregister_netdev(net_dev);
free_irq(net_dev->irq, net_dev);
if (mp->eeprom.cpuvers < CPUVERS_4_0) {
of_iounmap(&op->resource[0], mp->regs, mp->reg_size);
} else {
of_iounmap(&op->resource[0], mp->cregs, PAGE_SIZE);
of_iounmap(&op->resource[0], mp->lregs, (256 * 1024));
of_iounmap(&op->resource[0], mp->lanai, (512 * 1024));
}
free_netdev(net_dev);
dev_set_drvdata(&op->dev, NULL);
return 0;
}
static const struct of_device_id myri_sbus_match[] = {
{
.name = "MYRICOM,mlanai",
},
{
.name = "myri",
},
{},
};
MODULE_DEVICE_TABLE(of, myri_sbus_match);
static struct platform_driver myri_sbus_driver = {
.driver = {
.name = "myri",
.owner = THIS_MODULE,
.of_match_table = myri_sbus_match,
},
.probe = myri_sbus_probe,
.remove = __devexit_p(myri_sbus_remove),
};
static int __init myri_sbus_init(void)
{
return platform_driver_register(&myri_sbus_driver);
}
static void __exit myri_sbus_exit(void)
{
platform_driver_unregister(&myri_sbus_driver);
}
module_init(myri_sbus_init);
module_exit(myri_sbus_exit);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FWNAME);