summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/8390/stnic.c
blob: d85f0a84bc7bc0c2a993d72a591114a2f43bb29f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 1999 kaz Kojima
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/delay.h>

#include <asm/system.h>
#include <asm/io.h>
#include <mach-se/mach/se.h>
#include <asm/machvec.h>
#ifdef CONFIG_SH_STANDARD_BIOS
#include <asm/sh_bios.h>
#endif

#include "8390.h"

#define DRV_NAME "stnic"

#define byte	unsigned char
#define half	unsigned short
#define word	unsigned int
#define vbyte	volatile unsigned char
#define vhalf	volatile unsigned short
#define vword	volatile unsigned int

#define STNIC_RUN	0x01	/* 1 == Run, 0 == reset. */

#define START_PG	0	/* First page of TX buffer */
#define STOP_PG		128	/* Last page +1 of RX ring */

/* Alias */
#define STNIC_CR	E8390_CMD
#define PG0_RSAR0	EN0_RSARLO
#define PG0_RSAR1	EN0_RSARHI
#define PG0_RBCR0	EN0_RCNTLO
#define PG0_RBCR1	EN0_RCNTHI

#define CR_RRD		E8390_RREAD
#define CR_RWR		E8390_RWRITE
#define CR_PG0		E8390_PAGE0
#define CR_STA		E8390_START
#define CR_RDMA		E8390_NODMA

/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS.  */
static byte stnic_eadr[6] =
{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07};

static struct net_device *stnic_dev;

static void stnic_reset (struct net_device *dev);
static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
			   int ring_page);
static void stnic_block_input (struct net_device *dev, int count,
			       struct sk_buff *skb , int ring_offset);
static void stnic_block_output (struct net_device *dev, int count,
				const unsigned char *buf, int start_page);

static void stnic_init (struct net_device *dev);

/* SH7750 specific read/write io. */
static inline void
STNIC_DELAY (void)
{
  vword trash;
  trash = *(vword *) 0xa0000000;
  trash = *(vword *) 0xa0000000;
  trash = *(vword *) 0xa0000000;
}

static inline byte
STNIC_READ (int reg)
{
  byte val;

  val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff;
  STNIC_DELAY ();
  return val;
}

static inline void
STNIC_WRITE (int reg, byte val)
{
  *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8);
  STNIC_DELAY ();
}

static int __init stnic_probe(void)
{
  struct net_device *dev;
  int i, err;

  /* If we are not running on a SolutionEngine, give up now */
  if (! MACH_SE)
    return -ENODEV;

  /* New style probing API */
  dev = alloc_ei_netdev();
  if (!dev)
  	return -ENOMEM;

#ifdef CONFIG_SH_STANDARD_BIOS
  sh_bios_get_node_addr (stnic_eadr);
#endif
  for (i = 0; i < ETHER_ADDR_LEN; i++)
    dev->dev_addr[i] = stnic_eadr[i];

  /* Set the base address to point to the NIC, not the "real" base! */
  dev->base_addr = 0x1000;
  dev->irq = IRQ_STNIC;
  dev->netdev_ops = &ei_netdev_ops;

  /* Snarf the interrupt now.  There's no point in waiting since we cannot
     share and the board will usually be enabled. */
  err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev);
  if (err)  {
      printk (KERN_EMERG " unable to get IRQ %d.\n", dev->irq);
      free_netdev(dev);
      return err;
    }

  ei_status.name = dev->name;
  ei_status.word16 = 1;
#ifdef __LITTLE_ENDIAN__
  ei_status.bigendian = 0;
#else
  ei_status.bigendian = 1;
#endif
  ei_status.tx_start_page = START_PG;
  ei_status.rx_start_page = START_PG + TX_PAGES;
  ei_status.stop_page = STOP_PG;

  ei_status.reset_8390 = &stnic_reset;
  ei_status.get_8390_hdr = &stnic_get_hdr;
  ei_status.block_input = &stnic_block_input;
  ei_status.block_output = &stnic_block_output;

  stnic_init (dev);

  err = register_netdev(dev);
  if (err) {
    free_irq(dev->irq, dev);
    free_netdev(dev);
    return err;
  }
  stnic_dev = dev;

  printk (KERN_INFO "NS ST-NIC 83902A\n");

  return 0;
}

static void
stnic_reset (struct net_device *dev)
{
  *(vhalf *) PA_83902_RST = 0;
  udelay (5);
  if (ei_debug > 1)
    printk (KERN_WARNING "8390 reset done (%ld).\n", jiffies);
  *(vhalf *) PA_83902_RST = ~0;
  udelay (5);
}

static void
stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
	       int ring_page)
{
  half buf[2];

  STNIC_WRITE (PG0_RSAR0, 0);
  STNIC_WRITE (PG0_RSAR1, ring_page);
  STNIC_WRITE (PG0_RBCR0, 4);
  STNIC_WRITE (PG0_RBCR1, 0);
  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);

  buf[0] = *(vhalf *) PA_83902_IF;
  STNIC_DELAY ();
  buf[1] = *(vhalf *) PA_83902_IF;
  STNIC_DELAY ();
  hdr->next = buf[0] >> 8;
  hdr->status = buf[0] & 0xff;
#ifdef __LITTLE_ENDIAN__
  hdr->count = buf[1];
#else
  hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8);
#endif

  if (ei_debug > 1)
    printk (KERN_DEBUG "ring %x status %02x next %02x count %04x.\n",
	    ring_page, hdr->status, hdr->next, hdr->count);

  STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
}

/* Block input and output, similar to the Crynwr packet driver. If you are
   porting to a new ethercard look at the packet driver source for hints.
   The HP LAN doesn't use shared memory -- we put the packet
   out through the "remote DMA" dataport. */

static void
stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb,
		   int offset)
{
  char *buf = skb->data;
  half val;

  STNIC_WRITE (PG0_RSAR0, offset & 0xff);
  STNIC_WRITE (PG0_RSAR1, offset >> 8);
  STNIC_WRITE (PG0_RBCR0, length & 0xff);
  STNIC_WRITE (PG0_RBCR1, length >> 8);
  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);

  if (length & 1)
    length++;

  while (length > 0)
    {
      val = *(vhalf *) PA_83902_IF;
#ifdef __LITTLE_ENDIAN__
      *buf++ = val & 0xff;
      *buf++ = val >> 8;
#else
      *buf++ = val >> 8;
      *buf++ = val & 0xff;
#endif
      STNIC_DELAY ();
      length -= sizeof (half);
    }

  STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
}

static void
stnic_block_output (struct net_device *dev, int length,
		    const unsigned char *buf, int output_page)
{
  STNIC_WRITE (PG0_RBCR0, 1);	/* Write non-zero value */
  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
  STNIC_DELAY ();

  STNIC_WRITE (PG0_RBCR0, length & 0xff);
  STNIC_WRITE (PG0_RBCR1, length >> 8);
  STNIC_WRITE (PG0_RSAR0, 0);
  STNIC_WRITE (PG0_RSAR1, output_page);
  STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA);

  if (length & 1)
    length++;

  while (length > 0)
    {
#ifdef __LITTLE_ENDIAN__
      *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0];
#else
      *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1];
#endif
      STNIC_DELAY ();
      buf += sizeof (half);
      length -= sizeof (half);
    }

  STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
}

/* This function resets the STNIC if something screws up.  */
static void
stnic_init (struct net_device *dev)
{
  stnic_reset (dev);
  NS8390_init (dev, 0);
}

static void __exit stnic_cleanup(void)
{
	unregister_netdev(stnic_dev);
	free_irq(stnic_dev->irq, stnic_dev);
	free_netdev(stnic_dev);
}

module_init(stnic_probe);
module_exit(stnic_cleanup);
MODULE_LICENSE("GPL");
OpenPOWER on IntegriCloud