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