162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1999 kaz Kojima 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/ioport.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/io.h> 1862306a36Sopenharmony_ci#include <mach-se/mach/se.h> 1962306a36Sopenharmony_ci#include <asm/machvec.h> 2062306a36Sopenharmony_ci#ifdef CONFIG_SH_STANDARD_BIOS 2162306a36Sopenharmony_ci#include <asm/sh_bios.h> 2262306a36Sopenharmony_ci#endif 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "8390.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRV_NAME "stnic" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define byte unsigned char 2962306a36Sopenharmony_ci#define half unsigned short 3062306a36Sopenharmony_ci#define word unsigned int 3162306a36Sopenharmony_ci#define vbyte volatile unsigned char 3262306a36Sopenharmony_ci#define vhalf volatile unsigned short 3362306a36Sopenharmony_ci#define vword volatile unsigned int 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define STNIC_RUN 0x01 /* 1 == Run, 0 == reset. */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define START_PG 0 /* First page of TX buffer */ 3862306a36Sopenharmony_ci#define STOP_PG 128 /* Last page +1 of RX ring */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Alias */ 4162306a36Sopenharmony_ci#define STNIC_CR E8390_CMD 4262306a36Sopenharmony_ci#define PG0_RSAR0 EN0_RSARLO 4362306a36Sopenharmony_ci#define PG0_RSAR1 EN0_RSARHI 4462306a36Sopenharmony_ci#define PG0_RBCR0 EN0_RCNTLO 4562306a36Sopenharmony_ci#define PG0_RBCR1 EN0_RCNTHI 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define CR_RRD E8390_RREAD 4862306a36Sopenharmony_ci#define CR_RWR E8390_RWRITE 4962306a36Sopenharmony_ci#define CR_PG0 E8390_PAGE0 5062306a36Sopenharmony_ci#define CR_STA E8390_START 5162306a36Sopenharmony_ci#define CR_RDMA E8390_NODMA 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS. */ 5462306a36Sopenharmony_cistatic byte stnic_eadr[6] = 5562306a36Sopenharmony_ci{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct net_device *stnic_dev; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void stnic_reset (struct net_device *dev); 6062306a36Sopenharmony_cistatic void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr, 6162306a36Sopenharmony_ci int ring_page); 6262306a36Sopenharmony_cistatic void stnic_block_input (struct net_device *dev, int count, 6362306a36Sopenharmony_ci struct sk_buff *skb , int ring_offset); 6462306a36Sopenharmony_cistatic void stnic_block_output (struct net_device *dev, int count, 6562306a36Sopenharmony_ci const unsigned char *buf, int start_page); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void stnic_init (struct net_device *dev); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic u32 stnic_msg_enable; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cimodule_param_named(msg_enable, stnic_msg_enable, uint, 0444); 7262306a36Sopenharmony_ciMODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* SH7750 specific read/write io. */ 7562306a36Sopenharmony_cistatic inline void 7662306a36Sopenharmony_ciSTNIC_DELAY (void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci vword trash; 7962306a36Sopenharmony_ci trash = *(vword *) 0xa0000000; 8062306a36Sopenharmony_ci trash = *(vword *) 0xa0000000; 8162306a36Sopenharmony_ci trash = *(vword *) 0xa0000000; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic inline byte 8562306a36Sopenharmony_ciSTNIC_READ (int reg) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci byte val; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff; 9062306a36Sopenharmony_ci STNIC_DELAY (); 9162306a36Sopenharmony_ci return val; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic inline void 9562306a36Sopenharmony_ciSTNIC_WRITE (int reg, byte val) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8); 9862306a36Sopenharmony_ci STNIC_DELAY (); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int __init stnic_probe(void) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct net_device *dev; 10462306a36Sopenharmony_ci struct ei_device *ei_local; 10562306a36Sopenharmony_ci int err; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* If we are not running on a SolutionEngine, give up now */ 10862306a36Sopenharmony_ci if (! MACH_SE) 10962306a36Sopenharmony_ci return -ENODEV; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* New style probing API */ 11262306a36Sopenharmony_ci dev = alloc_ei_netdev(); 11362306a36Sopenharmony_ci if (!dev) 11462306a36Sopenharmony_ci return -ENOMEM; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#ifdef CONFIG_SH_STANDARD_BIOS 11762306a36Sopenharmony_ci sh_bios_get_node_addr (stnic_eadr); 11862306a36Sopenharmony_ci#endif 11962306a36Sopenharmony_ci eth_hw_addr_set(dev, stnic_eadr); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Set the base address to point to the NIC, not the "real" base! */ 12262306a36Sopenharmony_ci dev->base_addr = 0x1000; 12362306a36Sopenharmony_ci dev->irq = IRQ_STNIC; 12462306a36Sopenharmony_ci dev->netdev_ops = &ei_netdev_ops; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Snarf the interrupt now. There's no point in waiting since we cannot 12762306a36Sopenharmony_ci share and the board will usually be enabled. */ 12862306a36Sopenharmony_ci err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev); 12962306a36Sopenharmony_ci if (err) { 13062306a36Sopenharmony_ci netdev_emerg(dev, " unable to get IRQ %d.\n", dev->irq); 13162306a36Sopenharmony_ci free_netdev(dev); 13262306a36Sopenharmony_ci return err; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ei_status.name = dev->name; 13662306a36Sopenharmony_ci ei_status.word16 = 1; 13762306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 13862306a36Sopenharmony_ci ei_status.bigendian = 0; 13962306a36Sopenharmony_ci#else 14062306a36Sopenharmony_ci ei_status.bigendian = 1; 14162306a36Sopenharmony_ci#endif 14262306a36Sopenharmony_ci ei_status.tx_start_page = START_PG; 14362306a36Sopenharmony_ci ei_status.rx_start_page = START_PG + TX_PAGES; 14462306a36Sopenharmony_ci ei_status.stop_page = STOP_PG; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ei_status.reset_8390 = &stnic_reset; 14762306a36Sopenharmony_ci ei_status.get_8390_hdr = &stnic_get_hdr; 14862306a36Sopenharmony_ci ei_status.block_input = &stnic_block_input; 14962306a36Sopenharmony_ci ei_status.block_output = &stnic_block_output; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci stnic_init (dev); 15262306a36Sopenharmony_ci ei_local = netdev_priv(dev); 15362306a36Sopenharmony_ci ei_local->msg_enable = stnic_msg_enable; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci err = register_netdev(dev); 15662306a36Sopenharmony_ci if (err) { 15762306a36Sopenharmony_ci free_irq(dev->irq, dev); 15862306a36Sopenharmony_ci free_netdev(dev); 15962306a36Sopenharmony_ci return err; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci stnic_dev = dev; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci netdev_info(dev, "NS ST-NIC 83902A\n"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void 16962306a36Sopenharmony_cistnic_reset (struct net_device *dev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci *(vhalf *) PA_83902_RST = 0; 17462306a36Sopenharmony_ci udelay (5); 17562306a36Sopenharmony_ci netif_warn(ei_local, hw, dev, "8390 reset done (%ld).\n", jiffies); 17662306a36Sopenharmony_ci *(vhalf *) PA_83902_RST = ~0; 17762306a36Sopenharmony_ci udelay (5); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void 18162306a36Sopenharmony_cistnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr, 18262306a36Sopenharmony_ci int ring_page) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci half buf[2]; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci STNIC_WRITE (PG0_RSAR0, 0); 18962306a36Sopenharmony_ci STNIC_WRITE (PG0_RSAR1, ring_page); 19062306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR0, 4); 19162306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR1, 0); 19262306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci buf[0] = *(vhalf *) PA_83902_IF; 19562306a36Sopenharmony_ci STNIC_DELAY (); 19662306a36Sopenharmony_ci buf[1] = *(vhalf *) PA_83902_IF; 19762306a36Sopenharmony_ci STNIC_DELAY (); 19862306a36Sopenharmony_ci hdr->next = buf[0] >> 8; 19962306a36Sopenharmony_ci hdr->status = buf[0] & 0xff; 20062306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 20162306a36Sopenharmony_ci hdr->count = buf[1]; 20262306a36Sopenharmony_ci#else 20362306a36Sopenharmony_ci hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8); 20462306a36Sopenharmony_ci#endif 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci netif_dbg(ei_local, probe, dev, "ring %x status %02x next %02x count %04x.\n", 20762306a36Sopenharmony_ci ring_page, hdr->status, hdr->next, hdr->count); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* Block input and output, similar to the Crynwr packet driver. If you are 21362306a36Sopenharmony_ci porting to a new ethercard look at the packet driver source for hints. 21462306a36Sopenharmony_ci The HP LAN doesn't use shared memory -- we put the packet 21562306a36Sopenharmony_ci out through the "remote DMA" dataport. */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void 21862306a36Sopenharmony_cistnic_block_input (struct net_device *dev, int length, struct sk_buff *skb, 21962306a36Sopenharmony_ci int offset) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci char *buf = skb->data; 22262306a36Sopenharmony_ci half val; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci STNIC_WRITE (PG0_RSAR0, offset & 0xff); 22562306a36Sopenharmony_ci STNIC_WRITE (PG0_RSAR1, offset >> 8); 22662306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR0, length & 0xff); 22762306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR1, length >> 8); 22862306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (length & 1) 23162306a36Sopenharmony_ci length++; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci while (length > 0) 23462306a36Sopenharmony_ci { 23562306a36Sopenharmony_ci val = *(vhalf *) PA_83902_IF; 23662306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 23762306a36Sopenharmony_ci *buf++ = val & 0xff; 23862306a36Sopenharmony_ci *buf++ = val >> 8; 23962306a36Sopenharmony_ci#else 24062306a36Sopenharmony_ci *buf++ = val >> 8; 24162306a36Sopenharmony_ci *buf++ = val & 0xff; 24262306a36Sopenharmony_ci#endif 24362306a36Sopenharmony_ci STNIC_DELAY (); 24462306a36Sopenharmony_ci length -= sizeof (half); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void 25162306a36Sopenharmony_cistnic_block_output (struct net_device *dev, int length, 25262306a36Sopenharmony_ci const unsigned char *buf, int output_page) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR0, 1); /* Write non-zero value */ 25562306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); 25662306a36Sopenharmony_ci STNIC_DELAY (); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR0, length & 0xff); 25962306a36Sopenharmony_ci STNIC_WRITE (PG0_RBCR1, length >> 8); 26062306a36Sopenharmony_ci STNIC_WRITE (PG0_RSAR0, 0); 26162306a36Sopenharmony_ci STNIC_WRITE (PG0_RSAR1, output_page); 26262306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (length & 1) 26562306a36Sopenharmony_ci length++; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci while (length > 0) 26862306a36Sopenharmony_ci { 26962306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 27062306a36Sopenharmony_ci *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0]; 27162306a36Sopenharmony_ci#else 27262306a36Sopenharmony_ci *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1]; 27362306a36Sopenharmony_ci#endif 27462306a36Sopenharmony_ci STNIC_DELAY (); 27562306a36Sopenharmony_ci buf += sizeof (half); 27662306a36Sopenharmony_ci length -= sizeof (half); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* This function resets the STNIC if something screws up. */ 28362306a36Sopenharmony_cistatic void 28462306a36Sopenharmony_cistnic_init (struct net_device *dev) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci stnic_reset (dev); 28762306a36Sopenharmony_ci NS8390_init (dev, 0); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void __exit stnic_cleanup(void) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci unregister_netdev(stnic_dev); 29362306a36Sopenharmony_ci free_irq(stnic_dev->irq, stnic_dev); 29462306a36Sopenharmony_ci free_netdev(stnic_dev); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cimodule_init(stnic_probe); 29862306a36Sopenharmony_cimodule_exit(stnic_cleanup); 29962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 300