macsonic.c 19 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
/*
 * macsonic.c
 *
4
5
6
7
8
9
 * (C) 2005 Finn Thain
 *
 * Converted to DMA API, converted to unified driver model, made it work as
 * a module again, and from the mac68k project, introduced more 32-bit cards
 * and dhd's support for 16-bit cards.
 *
Linus Torvalds's avatar
Linus Torvalds committed
10
11
12
13
14
15
 * (C) 1998 Alan Cox
 *
 * Debugging Andreas Ehliar, Michael Schmitz
 *
 * Based on code
 * (C) 1996 by Thomas Bogendoerfer (tsbogend@bigbug.franken.de)
16
 *
Linus Torvalds's avatar
Linus Torvalds committed
17
18
 * This driver is based on work from Andreas Busse, but most of
 * the code is rewritten.
19
 *
Linus Torvalds's avatar
Linus Torvalds committed
20
21
22
23
 * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
 *
 * A driver for the Mac onboard Sonic ethernet chip.
 *
24
 * 98/12/21 MSch: judged from tests on Q800, it's basically working,
Linus Torvalds's avatar
Linus Torvalds committed
25
26
27
28
 *		  but eating up both receive and transmit resources
 *		  and duplicating packets. Needs more testing.
 *
 * 99/01/03 MSch: upgraded to version 0.92 of the core driver, fixed.
29
 *
Linus Torvalds's avatar
Linus Torvalds committed
30
31
32
33
34
 * 00/10/31 sammy@oh.verio.com: Updated driver for 2.4 kernels, fixed problems
 *          on centris.
 */

#include <linux/kernel.h>
35
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/nubus.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
50
#include <linux/platform_device.h>
51
#include <linux/dma-mapping.h>
Al Viro's avatar
Al Viro committed
52
#include <linux/bitrev.h>
Linus Torvalds's avatar
Linus Torvalds committed
53
54
55
56
57
58
59
60
61
62
63

#include <asm/bootinfo.h>
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/hwtest.h>
#include <asm/dma.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>

64
65
static char mac_sonic_string[] = "macsonic";
static struct platform_device *mac_sonic_device;
Linus Torvalds's avatar
Linus Torvalds committed
66
67
68

#include "sonic.h"

69
70
71
72
73
74
75
76
77
78
79
/* These should basically be bus-size and endian independent (since
   the SONIC is at least smart enough that it uses the same endianness
   as the host, unlike certain less enlightened Macintosh NICs) */
#define SONIC_READ(reg) (nubus_readw(dev->base_addr + (reg * 4) \
	      + lp->reg_offset))
#define SONIC_WRITE(reg,val) (nubus_writew(val, dev->base_addr + (reg * 4) \
	      + lp->reg_offset))

/* use 0 for production, 1 for verification, >1 for debug */
#ifdef SONIC_DEBUG
static unsigned int sonic_debug = SONIC_DEBUG;
80
#else
81
82
static unsigned int sonic_debug = 1;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
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

static int sonic_version_printed;

/* For onboard SONIC */
#define ONBOARD_SONIC_REGISTERS	0x50F0A000
#define ONBOARD_SONIC_PROM_BASE	0x50f08000

enum macsonic_type {
	MACSONIC_DUODOCK,
	MACSONIC_APPLE,
	MACSONIC_APPLE16,
	MACSONIC_DAYNA,
	MACSONIC_DAYNALINK
};

/* For the built-in SONIC in the Duo Dock */
#define DUODOCK_SONIC_REGISTERS 0xe10000
#define DUODOCK_SONIC_PROM_BASE 0xe12000

/* For Apple-style NuBus SONIC */
#define APPLE_SONIC_REGISTERS	0
#define APPLE_SONIC_PROM_BASE	0x40000

/* Daynalink LC SONIC */
#define DAYNALINK_PROM_BASE 0x400000

/* For Dayna-style NuBus SONIC (haven't seen one yet) */
#define DAYNA_SONIC_REGISTERS   0x180000
/* This is what OpenBSD says.  However, this is definitely in NuBus
   ROM space so we should be able to get it by walking the NuBus
   resource directories */
#define DAYNA_SONIC_MAC_ADDR	0xffe004

#define SONIC_READ_PROM(addr) nubus_readb(prom_addr+addr)

/*
 * For reversing the PROM address
 */

static inline void bit_reverse_addr(unsigned char addr[6])
{
	int i;

	for(i = 0; i < 6; i++)
Akinobu Mita's avatar
Akinobu Mita committed
127
		addr[i] = bitrev8(addr[i]);
Linus Torvalds's avatar
Linus Torvalds committed
128
129
}

Finn Thain's avatar
Finn Thain committed
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
static irqreturn_t macsonic_interrupt(int irq, void *dev_id)
{
	irqreturn_t result;
	unsigned long flags;

	local_irq_save(flags);
	result = sonic_interrupt(irq, dev_id);
	local_irq_restore(flags);
	return result;
}

static int macsonic_open(struct net_device* dev)
{
	if (request_irq(dev->irq, &sonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) {
		printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
		return -EAGAIN;
	}
	/* Under the A/UX interrupt scheme, the onboard SONIC interrupt comes
	 * in at priority level 3. However, we sometimes get the level 2 inter-
	 * rupt as well, which must prevent re-entrance of the sonic handler.
	 */
	if (dev->irq == IRQ_AUTO_3)
		if (request_irq(IRQ_NUBUS_9, &macsonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) {
			printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, IRQ_NUBUS_9);
			free_irq(dev->irq, dev);
			return -EAGAIN;
		}
	return sonic_open(dev);
}

static int macsonic_close(struct net_device* dev)
{
	int err;
	err = sonic_close(dev);
	free_irq(dev->irq, dev);
	if (dev->irq == IRQ_AUTO_3)
		free_irq(IRQ_NUBUS_9, dev);
	return err;
}

170
171
172
173
174
175
176
177
178
179
180
181
static const struct net_device_ops macsonic_netdev_ops = {
	.ndo_open		= macsonic_open,
	.ndo_stop		= macsonic_close,
	.ndo_start_xmit		= sonic_send_packet,
	.ndo_set_multicast_list	= sonic_multicast_list,
	.ndo_tx_timeout		= sonic_tx_timeout,
	.ndo_get_stats		= sonic_get_stats,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_change_mtu		= eth_change_mtu,
	.ndo_set_mac_address	= eth_mac_addr,
};

182
static int __devinit macsonic_init(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
183
{
184
	struct sonic_local* lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
185
186
187

	/* Allocate the entire chunk of memory for the descriptors.
           Note that this cannot cross a 64K boundary. */
188
189
190
	if ((lp->descriptors = dma_alloc_coherent(lp->device,
	            SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
	            &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
191
192
		printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
		       dev_name(lp->device));
Linus Torvalds's avatar
Linus Torvalds committed
193
		return -ENOMEM;
194
	}
Linus Torvalds's avatar
Linus Torvalds committed
195
196

	/* Now set up the pointers to point to the appropriate places */
197
198
199
	lp->cda = lp->descriptors;
	lp->tda = lp->cda + (SIZEOF_SONIC_CDA
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
Linus Torvalds's avatar
Linus Torvalds committed
200
	lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
201
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
Linus Torvalds's avatar
Linus Torvalds committed
202
	lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
203
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
Linus Torvalds's avatar
Linus Torvalds committed
204

205
206
207
208
209
210
211
	lp->cda_laddr = lp->descriptors_laddr;
	lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
	lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
	lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
Linus Torvalds's avatar
Linus Torvalds committed
212

213
	dev->netdev_ops = &macsonic_netdev_ops;
214
	dev->watchdog_timeo = TX_TIMEOUT;
Linus Torvalds's avatar
Linus Torvalds committed
215
216
217
218

	/*
	 * clear tally counter
	 */
219
220
221
	SONIC_WRITE(SONIC_CRCT, 0xffff);
	SONIC_WRITE(SONIC_FAET, 0xffff);
	SONIC_WRITE(SONIC_MPT, 0xffff);
Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225

	return 0;
}

226
227
228
229
230
231
#define INVALID_MAC(mac) (memcmp(mac, "\x08\x00\x07", 3) && \
                          memcmp(mac, "\x00\xA0\x40", 3) && \
                          memcmp(mac, "\x00\x80\x19", 3) && \
                          memcmp(mac, "\x00\x05\x02", 3))

static void __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
232
{
233
	struct sonic_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
234
	const int prom_addr = ONBOARD_SONIC_PROM_BASE;
235
	unsigned short val;
Linus Torvalds's avatar
Linus Torvalds committed
236

237
238
239
240
241
242
243
244
245
246
247
248
	/*
	 * On NuBus boards we can sometimes look in the ROM resources.
	 * No such luck for comm-slot/onboard.
	 * On the PowerBook 520, the PROM base address is a mystery.
	 */
	if (hwreg_present((void *)prom_addr)) {
		int i;

		for (i = 0; i < 6; i++)
			dev->dev_addr[i] = SONIC_READ_PROM(i);
		if (!INVALID_MAC(dev->dev_addr))
			return;
Linus Torvalds's avatar
Linus Torvalds committed
249

250
251
252
253
254
		/*
		 * Most of the time, the address is bit-reversed. The NetBSD
		 * source has a rather long and detailed historical account of
		 * why this is so.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
255
		bit_reverse_addr(dev->dev_addr);
256
257
258
		if (!INVALID_MAC(dev->dev_addr))
			return;

Linus Torvalds's avatar
Linus Torvalds committed
259
		/*
260
261
		 * If we still have what seems to be a bogus address, we'll
		 * look in the CAM. The top entry should be ours.
Linus Torvalds's avatar
Linus Torvalds committed
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
		printk(KERN_WARNING "macsonic: MAC address in PROM seems "
		                    "to be invalid, trying CAM\n");
	} else {
		printk(KERN_WARNING "macsonic: cannot read MAC address from "
		                    "PROM, trying CAM\n");
	}

	/* This only works if MacOS has already initialized the card. */

	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
	SONIC_WRITE(SONIC_CEP, 15);

	val = SONIC_READ(SONIC_CAP2);
	dev->dev_addr[5] = val >> 8;
	dev->dev_addr[4] = val & 0xff;
	val = SONIC_READ(SONIC_CAP1);
	dev->dev_addr[3] = val >> 8;
	dev->dev_addr[2] = val & 0xff;
	val = SONIC_READ(SONIC_CAP0);
	dev->dev_addr[1] = val >> 8;
	dev->dev_addr[0] = val & 0xff;

	if (!INVALID_MAC(dev->dev_addr))
		return;

	/* Still nonsense ... messed up someplace! */

	printk(KERN_WARNING "macsonic: MAC address in CAM entry 15 "
	                    "seems invalid, will use a random MAC\n");
	random_ether_addr(dev->dev_addr);
Linus Torvalds's avatar
Linus Torvalds committed
293
294
}

295
static int __devinit mac_onboard_sonic_probe(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
296
297
298
{
	/* Bwahahaha */
	static int once_is_more_than_enough;
299
300
301
	struct sonic_local* lp = netdev_priv(dev);
	int sr;
	int commslot = 0;
302

Linus Torvalds's avatar
Linus Torvalds committed
303
304
305
306
307
308
309
310
311
	if (once_is_more_than_enough)
		return -ENODEV;
	once_is_more_than_enough = 1;

	if (!MACH_IS_MAC)
		return -ENODEV;

	if (macintosh_config->ether_type != MAC_ETHER_SONIC)
		return -ENODEV;
312

313
	printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
314

Linus Torvalds's avatar
Linus Torvalds committed
315
316
317
318
319
320
	/* Bogus probing, on the models which may or may not have
	   Ethernet (BTW, the Ethernet *is* always at the same
	   address, and nothing else lives there, at least if Apple's
	   documentation is to be believed) */
	if (macintosh_config->ident == MAC_MODEL_Q630 ||
	    macintosh_config->ident == MAC_MODEL_P588 ||
321
	    macintosh_config->ident == MAC_MODEL_P575 ||
Linus Torvalds's avatar
Linus Torvalds committed
322
323
324
325
326
327
328
329
330
331
332
333
	    macintosh_config->ident == MAC_MODEL_C610) {
		unsigned long flags;
		int card_present;

		local_irq_save(flags);
		card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS);
		local_irq_restore(flags);

		if (!card_present) {
			printk("none.\n");
			return -ENODEV;
		}
334
		commslot = 1;
Linus Torvalds's avatar
Linus Torvalds committed
335
336
	}

337
	printk("yes\n");
Linus Torvalds's avatar
Linus Torvalds committed
338

339
340
	/* Danger!  My arms are flailing wildly!  You *must* set lp->reg_offset
	 * and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
Linus Torvalds's avatar
Linus Torvalds committed
341
342
343
344
345
346
347
348
349
350
351
	dev->base_addr = ONBOARD_SONIC_REGISTERS;
	if (via_alt_mapping)
		dev->irq = IRQ_AUTO_3;
	else
		dev->irq = IRQ_NUBUS_9;

	if (!sonic_version_printed) {
		printk(KERN_INFO "%s", version);
		sonic_version_printed = 1;
	}
	printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n",
352
	       dev_name(lp->device), dev->base_addr);
Linus Torvalds's avatar
Linus Torvalds committed
353
354
355

	/* The PowerBook's SONIC is 16 bit always. */
	if (macintosh_config->ident == MAC_MODEL_PB520) {
356
357
358
359
		lp->reg_offset = 0;
		lp->dma_bitmode = SONIC_BITMODE16;
		sr = SONIC_READ(SONIC_SR);
	} else if (commslot) {
Linus Torvalds's avatar
Linus Torvalds committed
360
		/* Some of the comm-slot cards are 16 bit.  But some
361
362
363
364
365
366
367
368
		   of them are not.  The 32-bit cards use offset 2 and
		   have known revisions, we try reading the revision
		   register at offset 2, if we don't get a known revision
		   we assume 16 bit at offset 0.  */
		lp->reg_offset = 2;
		lp->dma_bitmode = SONIC_BITMODE16;

		sr = SONIC_READ(SONIC_SR);
369
		if (sr == 0x0004 || sr == 0x0006 || sr == 0x0100 || sr == 0x0101)
370
371
372
373
374
375
			/* 83932 is 0x0004 or 0x0006, 83934 is 0x0100 or 0x0101 */
			lp->dma_bitmode = SONIC_BITMODE32;
		else {
			lp->dma_bitmode = SONIC_BITMODE16;
			lp->reg_offset = 0;
			sr = SONIC_READ(SONIC_SR);
Linus Torvalds's avatar
Linus Torvalds committed
376
		}
377
378
379
380
381
	} else {
		/* All onboard cards are at offset 2 with 32 bit DMA. */
		lp->reg_offset = 2;
		lp->dma_bitmode = SONIC_BITMODE32;
		sr = SONIC_READ(SONIC_SR);
Linus Torvalds's avatar
Linus Torvalds committed
382
	}
383
384
	printk(KERN_INFO
	       "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
385
	       dev_name(lp->device), sr, lp->dma_bitmode?32:16, lp->reg_offset);
Linus Torvalds's avatar
Linus Torvalds committed
386

387
#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
388
	printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
389
390
	       SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
391
392

	/* Software reset, then initialize control registers. */
393
394
395
396
397
	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);

	SONIC_WRITE(SONIC_DCR, SONIC_DCR_EXBUS | SONIC_DCR_BMS |
	                       SONIC_DCR_RFT1  | SONIC_DCR_TFT0 |
	                       (lp->dma_bitmode ? SONIC_DCR_DW : 0));
Linus Torvalds's avatar
Linus Torvalds committed
398
399

	/* This *must* be written back to in order to restore the
400
401
402
	 * extended programmable output bits, as it may not have been
	 * initialised since the hardware reset. */
	SONIC_WRITE(SONIC_DCR2, 0);
Linus Torvalds's avatar
Linus Torvalds committed
403
404

	/* Clear *and* disable interrupts to be on the safe side */
405
406
	SONIC_WRITE(SONIC_IMR, 0);
	SONIC_WRITE(SONIC_ISR, 0x7fff);
Linus Torvalds's avatar
Linus Torvalds committed
407
408

	/* Now look for the MAC address. */
409
	mac_onboard_sonic_ethernet_addr(dev);
Linus Torvalds's avatar
Linus Torvalds committed
410
411
412
413
414

	/* Shared init code */
	return macsonic_init(dev);
}

415
static int __devinit mac_nubus_sonic_ethernet_addr(struct net_device *dev,
416
417
						unsigned long prom_addr,
						int id)
Linus Torvalds's avatar
Linus Torvalds committed
418
419
420
421
{
	int i;
	for(i = 0; i < 6; i++)
		dev->dev_addr[i] = SONIC_READ_PROM(i);
422
423
424
425

	/* Some of the addresses are bit-reversed */
	if (id != MACSONIC_DAYNA)
		bit_reverse_addr(dev->dev_addr);
Linus Torvalds's avatar
Linus Torvalds committed
426
427
428
429

	return 0;
}

430
static int __devinit macsonic_ident(struct nubus_dev *ndev)
Linus Torvalds's avatar
Linus Torvalds committed
431
{
432
	if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC &&
Linus Torvalds's avatar
Linus Torvalds committed
433
434
435
436
437
438
439
440
441
442
	    ndev->dr_sw == NUBUS_DRSW_SONIC_LC)
		return MACSONIC_DAYNALINK;
	if (ndev->dr_hw == NUBUS_DRHW_SONIC &&
	    ndev->dr_sw == NUBUS_DRSW_APPLE) {
		/* There has to be a better way to do this... */
		if (strstr(ndev->board->name, "DuoDock"))
			return MACSONIC_DUODOCK;
		else
			return MACSONIC_APPLE;
	}
443

444
445
446
	if (ndev->dr_hw == NUBUS_DRHW_SMC9194 &&
	    ndev->dr_sw == NUBUS_DRSW_DAYNA)
		return MACSONIC_DAYNA;
447

Finn Thain's avatar
Finn Thain committed
448
	if (ndev->dr_hw == NUBUS_DRHW_APPLE_SONIC_LC &&
449
450
451
	    ndev->dr_sw == 0) { /* huh? */
		return MACSONIC_APPLE16;
	}
Linus Torvalds's avatar
Linus Torvalds committed
452
453
454
	return -1;
}

455
static int __devinit mac_nubus_sonic_probe(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
456
457
458
{
	static int slots;
	struct nubus_dev* ndev = NULL;
459
	struct sonic_local* lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
460
461
	unsigned long base_addr, prom_addr;
	u16 sonic_dcr;
462
463
	int id = -1;
	int reg_offset, dma_bitmode;
464

Linus Torvalds's avatar
Linus Torvalds committed
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
	/* Find the first SONIC that hasn't been initialized already */
	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK,
				       NUBUS_TYPE_ETHERNET, ndev)) != NULL)
	{
		/* Have we seen it already? */
		if (slots & (1<<ndev->board->slot))
			continue;
		slots |= 1<<ndev->board->slot;

		/* Is it one of ours? */
		if ((id = macsonic_ident(ndev)) != -1)
			break;
	}

	if (ndev == NULL)
		return -ENODEV;

	switch (id) {
	case MACSONIC_DUODOCK:
		base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS;
		prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE;
486
487
		sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 |
		            SONIC_DCR_TFT0;
Linus Torvalds's avatar
Linus Torvalds committed
488
		reg_offset = 2;
489
		dma_bitmode = SONIC_BITMODE32;
Linus Torvalds's avatar
Linus Torvalds committed
490
491
492
493
494
495
		break;
	case MACSONIC_APPLE:
		base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
		prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
		sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0;
		reg_offset = 0;
496
		dma_bitmode = SONIC_BITMODE32;
Linus Torvalds's avatar
Linus Torvalds committed
497
498
499
500
		break;
	case MACSONIC_APPLE16:
		base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
		prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
501
		sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
502
		            SONIC_DCR_PO1 | SONIC_DCR_BMS;
Linus Torvalds's avatar
Linus Torvalds committed
503
		reg_offset = 0;
504
		dma_bitmode = SONIC_BITMODE16;
Linus Torvalds's avatar
Linus Torvalds committed
505
506
507
508
		break;
	case MACSONIC_DAYNALINK:
		base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
		prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE;
509
		sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
510
		            SONIC_DCR_PO1 | SONIC_DCR_BMS;
Linus Torvalds's avatar
Linus Torvalds committed
511
		reg_offset = 0;
512
		dma_bitmode = SONIC_BITMODE16;
Linus Torvalds's avatar
Linus Torvalds committed
513
514
515
516
		break;
	case MACSONIC_DAYNA:
		base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS;
		prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR;
517
518
		sonic_dcr = SONIC_DCR_BMS |
		            SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1;
Linus Torvalds's avatar
Linus Torvalds committed
519
		reg_offset = 0;
520
		dma_bitmode = SONIC_BITMODE16;
Linus Torvalds's avatar
Linus Torvalds committed
521
522
523
524
525
526
		break;
	default:
		printk(KERN_ERR "macsonic: WTF, id is %d\n", id);
		return -ENODEV;
	}

527
528
	/* Danger!  My arms are flailing wildly!  You *must* set lp->reg_offset
	 * and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
Linus Torvalds's avatar
Linus Torvalds committed
529
	dev->base_addr = base_addr;
530
531
	lp->reg_offset = reg_offset;
	lp->dma_bitmode = dma_bitmode;
Linus Torvalds's avatar
Linus Torvalds committed
532
533
534
535
536
537
538
	dev->irq = SLOT2IRQ(ndev->board->slot);

	if (!sonic_version_printed) {
		printk(KERN_INFO "%s", version);
		sonic_version_printed = 1;
	}
	printk(KERN_INFO "%s: %s in slot %X\n",
539
	       dev_name(lp->device), ndev->board->name, ndev->board->slot);
Linus Torvalds's avatar
Linus Torvalds committed
540
	printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
541
	       dev_name(lp->device), SONIC_READ(SONIC_SR), dma_bitmode?32:16, reg_offset);
Linus Torvalds's avatar
Linus Torvalds committed
542

543
#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
544
	printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
545
546
	       SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
547
548

	/* Software reset, then initialize control registers. */
549
550
551
552
553
554
	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
	SONIC_WRITE(SONIC_DCR, sonic_dcr | (dma_bitmode ? SONIC_DCR_DW : 0));
	/* This *must* be written back to in order to restore the
	 * extended programmable output bits, since it may not have been
	 * initialised since the hardware reset. */
	SONIC_WRITE(SONIC_DCR2, 0);
Linus Torvalds's avatar
Linus Torvalds committed
555
556

	/* Clear *and* disable interrupts to be on the safe side */
557
558
	SONIC_WRITE(SONIC_IMR, 0);
	SONIC_WRITE(SONIC_ISR, 0x7fff);
Linus Torvalds's avatar
Linus Torvalds committed
559
560
561
562
563

	/* Now look for the MAC address. */
	if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0)
		return -ENODEV;

564
565
566
567
	/* Shared init code */
	return macsonic_init(dev);
}

568
static int __devinit mac_sonic_probe(struct platform_device *pdev)
569
570
571
572
573
574
575
576
577
578
{
	struct net_device *dev;
	struct sonic_local *lp;
	int err;

	dev = alloc_etherdev(sizeof(struct sonic_local));
	if (!dev)
		return -ENOMEM;

	lp = netdev_priv(dev);
Finn Thain's avatar
Finn Thain committed
579
580
	lp->device = &pdev->dev;
	SET_NETDEV_DEV(dev, &pdev->dev);
581
	platform_set_drvdata(pdev, dev);
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596

	/* This will catch fatal stuff like -ENOMEM as well as success */
	err = mac_onboard_sonic_probe(dev);
	if (err == 0)
		goto found;
	if (err != -ENODEV)
		goto out;
	err = mac_nubus_sonic_probe(dev);
	if (err)
		goto out;
found:
	err = register_netdev(dev);
	if (err)
		goto out;

Johannes Berg's avatar
Johannes Berg committed
597
	printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
Linus Torvalds's avatar
Linus Torvalds committed
598

599
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
600

601
602
out:
	free_netdev(dev);
Linus Torvalds's avatar
Linus Torvalds committed
603

604
605
606
607
608
	return err;
}

MODULE_DESCRIPTION("Macintosh SONIC ethernet driver");
module_param(sonic_debug, int, 0);
Linus Torvalds's avatar
Linus Torvalds committed
609
610
MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)");

611
612
#include "sonic.c"

Finn Thain's avatar
Finn Thain committed
613
static int __devexit mac_sonic_device_remove (struct platform_device *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
614
{
Finn Thain's avatar
Finn Thain committed
615
	struct net_device *dev = platform_get_drvdata(pdev);
616
617
	struct sonic_local* lp = netdev_priv(dev);

Finn Thain's avatar
Finn Thain committed
618
	unregister_netdev(dev);
619
620
	dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
	                  lp->descriptors, lp->descriptors_laddr);
Finn Thain's avatar
Finn Thain committed
621
	free_netdev(dev);
622

Linus Torvalds's avatar
Linus Torvalds committed
623
624
625
	return 0;
}

626
static struct platform_driver mac_sonic_driver = {
627
628
	.probe  = mac_sonic_probe,
	.remove = __devexit_p(mac_sonic_device_remove),
629
630
631
	.driver	= {
		.name = mac_sonic_string,
	},
632
633
634
635
636
};

static int __init mac_sonic_init_module(void)
{
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
637

638
	if ((err = platform_driver_register(&mac_sonic_driver))) {
639
640
641
		printk(KERN_ERR "Driver registration failed\n");
		return err;
	}
Linus Torvalds's avatar
Linus Torvalds committed
642

Russell King's avatar
Russell King committed
643
	mac_sonic_device = platform_device_alloc(mac_sonic_string, 0);
Finn Thain's avatar
Finn Thain committed
644
	if (!mac_sonic_device)
645
		goto out_unregister;
Linus Torvalds's avatar
Linus Torvalds committed
646

Russell King's avatar
Russell King committed
647
648
	if (platform_device_add(mac_sonic_device)) {
		platform_device_put(mac_sonic_device);
649
650
651
652
653
654
		mac_sonic_device = NULL;
	}

	return 0;

out_unregister:
655
	platform_driver_unregister(&mac_sonic_driver);
656
657
658
659
660
661

	return -ENOMEM;
}

static void __exit mac_sonic_cleanup_module(void)
{
662
	platform_driver_unregister(&mac_sonic_driver);
663
664
665
666
667
668
669
670
671

	if (mac_sonic_device) {
		platform_device_unregister(mac_sonic_device);
		mac_sonic_device = NULL;
	}
}

module_init(mac_sonic_init_module);
module_exit(mac_sonic_cleanup_module);