summaryrefslogtreecommitdiffstats
path: root/uClinux-2.4.20-uc1/drivers/net/hplance.c
blob: 141516b101e072728a87510aa2d8d41092b4bb05 (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
/* hplance.c  : the  Linux/hp300/lance ethernet driver
 *
 * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
 * Based on the Sun Lance driver and the NetBSD HP Lance driver
 * Uses the generic 7990.c LANCE code.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>

/* Used for the temporal inet entries and routing */
#include <linux/socket.h>
#include <linux/route.h>

#include <linux/dio.h>

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>

#include "hplance.h"

/* We have 16834 bytes of RAM for the init block and buffers. This places
 * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
 * buffers and 2 Tx buffers.
 */
#define LANCE_LOG_TX_BUFFERS 1
#define LANCE_LOG_RX_BUFFERS 3

#include "7990.h"                                 /* use generic LANCE code */

/* Our private data structure */
struct hplance_private {
  struct lance_private lance;
  unsigned int scode;
  void *base;
};

/* function prototypes... This is easy because all the grot is in the
 * generic LANCE support. All we have to support is probing for boards,
 * plus board-specific init, open and close actions. 
 * Oh, and we need to tell the generic code how to read and write LANCE registers...
 */
int hplance_probe(struct net_device *dev);
static int hplance_init(struct net_device *dev, int scode);
static int hplance_open(struct net_device *dev);
static int hplance_close(struct net_device *dev);
static void hplance_writerap(void *priv, unsigned short value);
static void hplance_writerdp(void *priv, unsigned short value);
static unsigned short hplance_readrdp(void *priv);

#ifdef MODULE
static struct hplance_private *root_hplance_dev;
#endif

/* Find all the HP Lance boards and initialise them... */
int __init hplance_probe(struct net_device *dev)
{
        int cards = 0, called = 0;

        if (!MACH_IS_HP300 || called)
                return(ENODEV);
        called++;
        
        /* Isn't DIO nice? */
        for(;;)
        {
                int v, scode = dio_find(DIO_ID_LAN);
                                
                if (!scode)
                        break;
                
                if(cards)
                        dev = NULL;      /* don't trash previous device, make a new one */
                cards++;
                
                v = hplance_init(dev, scode);
                if (v)                            /* error, abort immediately */
                        return v;
        }
        /* OK, return success, or ENODEV if we didn't find any cards */
        if (!cards)
                return -ENODEV;
        return 0;
}

/* Initialise a single lance board at the given select code */
static int __init hplance_init(struct net_device *dev, int scode)
{
        const char *name = dio_scodetoname(scode);
        void *va = dio_scodetoviraddr(scode);
        struct hplance_private *lp;
        int i;
        
#ifdef MODULE
	dev = init_etherdev(0, sizeof(struct hplance_private));
	if (!dev)
		return -ENOMEM;
#else
	dev->priv = kmalloc(sizeof(struct hplance_private), GFP_KERNEL);
	if (dev->priv == NULL)
		return -ENOMEM;
	memset(dev->priv, 0, sizeof(struct hplance_private));
#endif
	SET_MODULE_OWNER(dev);

        printk("%s: %s; select code %d, addr", dev->name, name, scode);

        /* reset the board */
        out_8(va+DIO_IDOFF, 0xff);
        udelay(100);                              /* ariba! ariba! udelay! udelay! */

        /* Fill the dev fields */
        dev->base_addr = (unsigned long)va;
        dev->open = &hplance_open;
        dev->stop = &hplance_close;
        dev->hard_start_xmit = &lance_start_xmit;
        dev->get_stats = &lance_get_stats;
        dev->set_multicast_list = &lance_set_multicast;
        dev->dma = 0;
        
        for (i=0; i<6; i++)
        {
                /* The NVRAM holds our ethernet address, one nibble per byte,
                 * at bytes NVRAMOFF+1,3,5,7,9...
                 */
                dev->dev_addr[i] = ((in_8(va + HPLANCE_NVRAMOFF + i*4 + 1) & 0xF) << 4)
                        | (in_8(va + HPLANCE_NVRAMOFF + i*4 + 3) & 0xF);
                printk("%c%2.2x", i == 0 ? ' ' : ':', dev->dev_addr[i]);
        }
        
        lp = (struct hplance_private *)dev->priv;
        lp->lance.name = (char*)name;                   /* discards const, shut up gcc */
        lp->lance.ll = (struct lance_regs *)(va + HPLANCE_REGOFF);
        lp->lance.init_block = (struct lance_init_block *)(va + HPLANCE_MEMOFF); /* CPU addr */
        lp->lance.lance_init_block = 0;                 /* LANCE addr of same RAM */
        lp->lance.busmaster_regval = LE_C3_BSWP;        /* we're bigendian */
        lp->lance.irq = dio_scodetoipl(scode);
        lp->lance.writerap = hplance_writerap;
        lp->lance.writerdp = hplance_writerdp;
        lp->lance.readrdp = hplance_readrdp;
        lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
        lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
        lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
        lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
        lp->scode = scode;
	lp->base = va;
        ether_setup(dev);
	printk(", irq %d\n", lp->lance.irq);

#ifdef MODULE
        dev->ifindex = dev_new_index();
        lp->next_module = root_hplance_dev;
        root_hplance_dev = lp;
#endif /* MODULE */

        dio_config_board(scode);                  /* tell bus scanning code this one's taken */
        return 0;
}

/* This is disgusting. We have to check the DIO status register for ack every
 * time we read or write the LANCE registers.
 */
static void hplance_writerap(void *priv, unsigned short value)
{
	struct hplance_private *lp = (struct hplance_private *)priv;
        struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
        do {
                lp->lance.ll->rap = value;
        } while ((hpregs->status & LE_ACK) == 0);
}

static void hplance_writerdp(void *priv, unsigned short value)
{
	struct hplance_private *lp = (struct hplance_private *)priv;
        struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
        do {
                lp->lance.ll->rdp = value;
        } while ((hpregs->status & LE_ACK) == 0);
}

static unsigned short hplance_readrdp(void *priv)
{
        unsigned short val;
	struct hplance_private *lp = (struct hplance_private *)priv;
        struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
        do {
                val = lp->lance.ll->rdp;
        } while ((hpregs->status & LE_ACK) == 0);
        return val;
}

static int hplance_open(struct net_device *dev)
{
        int status;
        struct hplance_private *lp = (struct hplance_private *)dev->priv;
        struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
        
        status = lance_open(dev);                 /* call generic lance open code */
        if (status)
                return status;
        /* enable interrupts at board level. */
        out_8(&(hpregs->status), LE_IE);

        return 0;
}

static int hplance_close(struct net_device *dev)
{
        struct hplance_private *lp = (struct hplance_private *)dev->priv;
        struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
        out_8(&(hpregs->status), 8);              /* disable interrupts at boardlevel */
        lance_close(dev);
        return 0;
}

#ifdef MODULE
MODULE_LICENSE("GPL");
int init_module(void)
{
        root_lance_dev = NULL;
        return hplance_probe(NULL);
}

void cleanup_module(void)
{
        /* Walk the chain of devices, unregistering them */
        struct hplance_private *lp;
        while (root_hplance_dev) {
                lp = root_hplance_dev->next_module;
                dio_unconfig_board(lp->scode);
                unregister_netdev(root_lance_dev->dev);
                kfree(root_lance_dev->dev);
                root_lance_dev = lp;
        }
}

#endif /* MODULE */