162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* mvme147.c : the Linux/mvme147/lance ethernet driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk> 562306a36Sopenharmony_ci * Based on the Sun Lance driver and the NetBSD HP Lance driver 662306a36Sopenharmony_ci * Uses the generic 7990.c LANCE code. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/ioport.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/gfp.h> 1962306a36Sopenharmony_ci#include <linux/pgtable.h> 2062306a36Sopenharmony_ci/* Used for the temporal inet entries and routing */ 2162306a36Sopenharmony_ci#include <linux/socket.h> 2262306a36Sopenharmony_ci#include <linux/route.h> 2362306a36Sopenharmony_ci#include <linux/netdevice.h> 2462306a36Sopenharmony_ci#include <linux/etherdevice.h> 2562306a36Sopenharmony_ci#include <linux/skbuff.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/io.h> 2862306a36Sopenharmony_ci#include <asm/mvme147hw.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* We have 32K of RAM for the init block and buffers. This places 3162306a36Sopenharmony_ci * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx 3262306a36Sopenharmony_ci * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci#define LANCE_LOG_TX_BUFFERS 1 3562306a36Sopenharmony_ci#define LANCE_LOG_RX_BUFFERS 3 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "7990.h" /* use generic LANCE code */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Our private data structure */ 4062306a36Sopenharmony_cistruct m147lance_private { 4162306a36Sopenharmony_ci struct lance_private lance; 4262306a36Sopenharmony_ci unsigned long ram; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* function prototypes... This is easy because all the grot is in the 4662306a36Sopenharmony_ci * generic LANCE support. All we have to support is probing for boards, 4762306a36Sopenharmony_ci * plus board-specific init, open and close actions. 4862306a36Sopenharmony_ci * Oh, and we need to tell the generic code how to read and write LANCE registers... 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic int m147lance_open(struct net_device *dev); 5162306a36Sopenharmony_cistatic int m147lance_close(struct net_device *dev); 5262306a36Sopenharmony_cistatic void m147lance_writerap(struct lance_private *lp, unsigned short value); 5362306a36Sopenharmony_cistatic void m147lance_writerdp(struct lance_private *lp, unsigned short value); 5462306a36Sopenharmony_cistatic unsigned short m147lance_readrdp(struct lance_private *lp); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_citypedef void (*writerap_t)(void *, unsigned short); 5762306a36Sopenharmony_citypedef void (*writerdp_t)(void *, unsigned short); 5862306a36Sopenharmony_citypedef unsigned short (*readrdp_t)(void *); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct net_device_ops lance_netdev_ops = { 6162306a36Sopenharmony_ci .ndo_open = m147lance_open, 6262306a36Sopenharmony_ci .ndo_stop = m147lance_close, 6362306a36Sopenharmony_ci .ndo_start_xmit = lance_start_xmit, 6462306a36Sopenharmony_ci .ndo_set_rx_mode = lance_set_multicast, 6562306a36Sopenharmony_ci .ndo_tx_timeout = lance_tx_timeout, 6662306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 6762306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Initialise the one and only on-board 7990 */ 7162306a36Sopenharmony_cistatic struct net_device * __init mvme147lance_probe(void) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct net_device *dev; 7462306a36Sopenharmony_ci static int called; 7562306a36Sopenharmony_ci static const char name[] = "MVME147 LANCE"; 7662306a36Sopenharmony_ci struct m147lance_private *lp; 7762306a36Sopenharmony_ci u8 macaddr[ETH_ALEN]; 7862306a36Sopenharmony_ci u_long *addr; 7962306a36Sopenharmony_ci u_long address; 8062306a36Sopenharmony_ci int err; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!MACH_IS_MVME147 || called) 8362306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 8462306a36Sopenharmony_ci called++; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct m147lance_private)); 8762306a36Sopenharmony_ci if (!dev) 8862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Fill the dev fields */ 9162306a36Sopenharmony_ci dev->base_addr = (unsigned long)MVME147_LANCE_BASE; 9262306a36Sopenharmony_ci dev->netdev_ops = &lance_netdev_ops; 9362306a36Sopenharmony_ci dev->dma = 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci addr = (u_long *)ETHERNET_ADDRESS; 9662306a36Sopenharmony_ci address = *addr; 9762306a36Sopenharmony_ci macaddr[0] = 0x08; 9862306a36Sopenharmony_ci macaddr[1] = 0x00; 9962306a36Sopenharmony_ci macaddr[2] = 0x3e; 10062306a36Sopenharmony_ci address = address >> 8; 10162306a36Sopenharmony_ci macaddr[5] = address&0xff; 10262306a36Sopenharmony_ci address = address >> 8; 10362306a36Sopenharmony_ci macaddr[4] = address&0xff; 10462306a36Sopenharmony_ci address = address >> 8; 10562306a36Sopenharmony_ci macaddr[3] = address&0xff; 10662306a36Sopenharmony_ci eth_hw_addr_set(dev, macaddr); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci printk("%s: MVME147 at 0x%08lx, irq %d, Hardware Address %pM\n", 10962306a36Sopenharmony_ci dev->name, dev->base_addr, MVME147_LANCE_IRQ, 11062306a36Sopenharmony_ci dev->dev_addr); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci lp = netdev_priv(dev); 11362306a36Sopenharmony_ci lp->ram = __get_dma_pages(GFP_ATOMIC, 3); /* 32K */ 11462306a36Sopenharmony_ci if (!lp->ram) { 11562306a36Sopenharmony_ci printk("%s: No memory for LANCE buffers\n", dev->name); 11662306a36Sopenharmony_ci free_netdev(dev); 11762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci lp->lance.name = name; 12162306a36Sopenharmony_ci lp->lance.base = dev->base_addr; 12262306a36Sopenharmony_ci lp->lance.init_block = (struct lance_init_block *)(lp->ram); /* CPU addr */ 12362306a36Sopenharmony_ci lp->lance.lance_init_block = (struct lance_init_block *)(lp->ram); /* LANCE addr of same RAM */ 12462306a36Sopenharmony_ci lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */ 12562306a36Sopenharmony_ci lp->lance.irq = MVME147_LANCE_IRQ; 12662306a36Sopenharmony_ci lp->lance.writerap = (writerap_t)m147lance_writerap; 12762306a36Sopenharmony_ci lp->lance.writerdp = (writerdp_t)m147lance_writerdp; 12862306a36Sopenharmony_ci lp->lance.readrdp = (readrdp_t)m147lance_readrdp; 12962306a36Sopenharmony_ci lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS; 13062306a36Sopenharmony_ci lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS; 13162306a36Sopenharmony_ci lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK; 13262306a36Sopenharmony_ci lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci err = register_netdev(dev); 13562306a36Sopenharmony_ci if (err) { 13662306a36Sopenharmony_ci free_pages(lp->ram, 3); 13762306a36Sopenharmony_ci free_netdev(dev); 13862306a36Sopenharmony_ci return ERR_PTR(err); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return dev; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void m147lance_writerap(struct lance_private *lp, unsigned short value) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci out_be16(lp->base + LANCE_RAP, value); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void m147lance_writerdp(struct lance_private *lp, unsigned short value) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci out_be16(lp->base + LANCE_RDP, value); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic unsigned short m147lance_readrdp(struct lance_private *lp) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return in_be16(lp->base + LANCE_RDP); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int m147lance_open(struct net_device *dev) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int status; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci status = lance_open(dev); /* call generic lance open code */ 16462306a36Sopenharmony_ci if (status) 16562306a36Sopenharmony_ci return status; 16662306a36Sopenharmony_ci /* enable interrupts at board level. */ 16762306a36Sopenharmony_ci m147_pcc->lan_cntrl = 0; /* clear the interrupts (if any) */ 16862306a36Sopenharmony_ci m147_pcc->lan_cntrl = 0x08 | 0x04; /* Enable irq 4 */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int m147lance_close(struct net_device *dev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci /* disable interrupts at boardlevel */ 17662306a36Sopenharmony_ci m147_pcc->lan_cntrl = 0x0; /* disable interrupts */ 17762306a36Sopenharmony_ci lance_close(dev); 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct net_device *dev_mvme147_lance; 18462306a36Sopenharmony_cistatic int __init m147lance_init(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci dev_mvme147_lance = mvme147lance_probe(); 18762306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(dev_mvme147_lance); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_cimodule_init(m147lance_init); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void __exit m147lance_exit(void) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct m147lance_private *lp = netdev_priv(dev_mvme147_lance); 19462306a36Sopenharmony_ci unregister_netdev(dev_mvme147_lance); 19562306a36Sopenharmony_ci free_pages(lp->ram, 3); 19662306a36Sopenharmony_ci free_netdev(dev_mvme147_lance); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_cimodule_exit(m147lance_exit); 199